v2api: implement Open()
This commit is contained in:
parent
d73e4b3f7c
commit
d2139e18ef
@ -147,13 +147,15 @@ func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint3
|
||||
}
|
||||
|
||||
// Get device number and inode number into `st`
|
||||
st, err := syscallcompat.Fstatat2(dirfd, cName, unix.AT_SYMLINK_NOFOLLOW)
|
||||
var st syscall.Stat_t
|
||||
err = syscall.Fstat(fd, &st)
|
||||
if err != nil {
|
||||
return nil, nil, 0, fs.ToErrno(err)
|
||||
errno = fs.ToErrno(err)
|
||||
return
|
||||
}
|
||||
// Get unique inode number
|
||||
rn.inoMap.TranslateStat(st)
|
||||
out.Attr.FromStat(st)
|
||||
rn.inoMap.TranslateStat(&st)
|
||||
out.Attr.FromStat(&st)
|
||||
// Create child node
|
||||
id := fs.StableAttr{
|
||||
Mode: uint32(st.Mode),
|
||||
@ -164,7 +166,7 @@ func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint3
|
||||
ch := n.NewInode(ctx, node, id)
|
||||
|
||||
f := os.NewFile(uintptr(fd), cName)
|
||||
return ch, NewFile2(f, rn, st), 0, 0
|
||||
return ch, NewFile2(f, rn, &st), 0, 0
|
||||
}
|
||||
|
||||
// Unlink - FUSE call. Delete a file.
|
||||
@ -218,3 +220,49 @@ func (n *Node) Readlink(ctx context.Context) (out []byte, errno syscall.Errno) {
|
||||
}
|
||||
return []byte(target), 0
|
||||
}
|
||||
|
||||
// Open - FUSE call. Open already-existing file.
|
||||
//
|
||||
// Symlink-safe through Openat().
|
||||
func (n *Node) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) {
|
||||
dirfd, cName, errno := n.prepareAtSyscall("")
|
||||
if errno != 0 {
|
||||
return
|
||||
}
|
||||
defer syscall.Close(dirfd)
|
||||
|
||||
rn := n.rootNode()
|
||||
newFlags := rn.mangleOpenFlags(flags)
|
||||
// Taking this lock makes sure we don't race openWriteOnlyFile()
|
||||
rn.openWriteOnlyLock.RLock()
|
||||
defer rn.openWriteOnlyLock.RUnlock()
|
||||
|
||||
// Open backing file
|
||||
fd, err := syscallcompat.Openat(dirfd, cName, newFlags, 0)
|
||||
// Handle a few specific errors
|
||||
if err != nil {
|
||||
if err == syscall.EMFILE {
|
||||
var lim syscall.Rlimit
|
||||
syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim)
|
||||
tlog.Warn.Printf("Open %q: too many open files. Current \"ulimit -n\": %d", cName, lim.Cur)
|
||||
}
|
||||
if err == syscall.EACCES && (int(flags)&syscall.O_ACCMODE) == syscall.O_WRONLY {
|
||||
fd, err = rn.openWriteOnlyFile(dirfd, cName, newFlags)
|
||||
}
|
||||
}
|
||||
// Could not handle the error? Bail out
|
||||
if err != nil {
|
||||
errno = fs.ToErrno(err)
|
||||
return
|
||||
}
|
||||
|
||||
var st syscall.Stat_t
|
||||
err = syscall.Fstat(fd, &st)
|
||||
if err != nil {
|
||||
errno = fs.ToErrno(err)
|
||||
return
|
||||
}
|
||||
f := os.NewFile(uintptr(fd), cName)
|
||||
fh = NewFile2(f, rn, &st)
|
||||
return
|
||||
}
|
||||
|
@ -13,3 +13,4 @@ var _ = (fs.NodeMkdirer)((*Node)(nil))
|
||||
var _ = (fs.NodeRmdirer)((*Node)(nil))
|
||||
var _ = (fs.NodeUnlinker)((*Node)(nil))
|
||||
var _ = (fs.NodeReadlinker)((*Node)(nil))
|
||||
var _ = (fs.NodeOpener)((*Node)(nil))
|
||||
|
@ -28,6 +28,9 @@ type RootNode struct {
|
||||
nameTransform nametransform.NameTransformer
|
||||
// Content encryption helper
|
||||
contentEnc *contentenc.ContentEnc
|
||||
// This lock is used by openWriteOnlyFile() to block concurrent opens while
|
||||
// it relaxes the permissions on a file.
|
||||
openWriteOnlyLock sync.RWMutex
|
||||
// MitigatedCorruptions is used to report data corruption that is internally
|
||||
// mitigated by ignoring the corrupt item. For example, when OpenDir() finds
|
||||
// a corrupt filename, we still return the other valid filenames.
|
||||
@ -137,3 +140,48 @@ func (rn *RootNode) decryptSymlinkTarget(cData64 string) (string, error) {
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
// Due to RMW, we always need read permissions on the backing file. This is a
|
||||
// problem if the file permissions do not allow reading (i.e. 0200 permissions).
|
||||
// This function works around that problem by chmod'ing the file, obtaining a fd,
|
||||
// and chmod'ing it back.
|
||||
func (rn *RootNode) openWriteOnlyFile(dirfd int, cName string, newFlags int) (rwFd int, err error) {
|
||||
woFd, err := syscallcompat.Openat(dirfd, cName, syscall.O_WRONLY|syscall.O_NOFOLLOW, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer syscall.Close(woFd)
|
||||
var st syscall.Stat_t
|
||||
err = syscall.Fstat(woFd, &st)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// The cast to uint32 fixes a build failure on Darwin, where st.Mode is uint16.
|
||||
perms := uint32(st.Mode)
|
||||
// Verify that we don't have read permissions
|
||||
if perms&0400 != 0 {
|
||||
tlog.Warn.Printf("openWriteOnlyFile: unexpected permissions %#o, returning EPERM", perms)
|
||||
err = syscall.EPERM
|
||||
return
|
||||
}
|
||||
// Upgrade the lock to block other Open()s and downgrade again on return
|
||||
rn.openWriteOnlyLock.RUnlock()
|
||||
rn.openWriteOnlyLock.Lock()
|
||||
defer func() {
|
||||
rn.openWriteOnlyLock.Unlock()
|
||||
rn.openWriteOnlyLock.RLock()
|
||||
}()
|
||||
// Relax permissions and revert on return
|
||||
err = syscall.Fchmod(woFd, perms|0400)
|
||||
if err != nil {
|
||||
tlog.Warn.Printf("openWriteOnlyFile: changing permissions failed: %v", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
err2 := syscall.Fchmod(woFd, perms)
|
||||
if err2 != nil {
|
||||
tlog.Warn.Printf("openWriteOnlyFile: reverting permissions failed: %v", err2)
|
||||
}
|
||||
}()
|
||||
return syscallcompat.Openat(dirfd, cName, newFlags, 0)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user