From 3b9a1b628b11a9deb03a8fe7e6022e75b16ded49 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sun, 21 Mar 2021 09:31:05 +0100 Subject: [PATCH] fusefronted: move Create() and Open() to new file And deduplicate the code a little. --- internal/fusefrontend/file.go | 21 +++- internal/fusefrontend/node.go | 114 ---------------------- internal/fusefrontend/node_open_create.go | 108 ++++++++++++++++++++ 3 files changed, 125 insertions(+), 118 deletions(-) create mode 100644 internal/fusefrontend/node_open_create.go diff --git a/internal/fusefrontend/file.go b/internal/fusefrontend/file.go index 98e5802..33a3cbe 100644 --- a/internal/fusefrontend/file.go +++ b/internal/fusefrontend/file.go @@ -53,18 +53,31 @@ type File struct { rootNode *RootNode } -// NewFile returns a new go-fuse File instance. -func NewFile(fd *os.File, rn *RootNode, st *syscall.Stat_t) *File { +// NewFile returns a new go-fuse File instance based on an already-open file +// descriptor. NewFile internally calls Fstat() on the fd. The resulting Stat_t +// is returned because node.Create() needs it. +// +// `cName` is only used for error logging and may be left blank. +func NewFile(fd int, cName string, rn *RootNode) (f *File, st *syscall.Stat_t, errno syscall.Errno) { + // Need device number and inode number for openfiletable locking + st = &syscall.Stat_t{} + if err := syscall.Fstat(fd, st); err != nil { + errno = fs.ToErrno(err) + return + } qi := inomap.QInoFromStat(st) e := openfiletable.Register(qi) - return &File{ - fd: fd, + osFile := os.NewFile(uintptr(fd), cName) + + f = &File{ + fd: osFile, contentEnc: rn.contentEnc, qIno: qi, fileTableEntry: e, rootNode: rn, } + return f, st, 0 } // intFd - return the backing file descriptor as an integer. diff --git a/internal/fusefrontend/node.go b/internal/fusefrontend/node.go index 2e5d370..5a7edc8 100644 --- a/internal/fusefrontend/node.go +++ b/internal/fusefrontend/node.go @@ -2,7 +2,6 @@ package fusefrontend import ( "context" - "os" "syscall" "golang.org/x/sys/unix" @@ -78,69 +77,6 @@ func (n *Node) Getattr(ctx context.Context, f fs.FileHandle, out *fuse.AttrOut) return 0 } -// Create - FUSE call. Creates a new file. -// -// Symlink-safe through the use of Openat(). -func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (inode *fs.Inode, fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) { - dirfd, cName, errno := n.prepareAtSyscall(name) - if errno != 0 { - return - } - defer syscall.Close(dirfd) - - var err error - fd := -1 - // Make sure context is nil if we don't want to preserve the owner - rn := n.rootNode() - if !rn.args.PreserveOwner { - ctx = nil - } - newFlags := rn.mangleOpenFlags(flags) - // Handle long file name - ctx2 := toFuseCtx(ctx) - if !rn.args.PlaintextNames && nametransform.IsLongContent(cName) { - // Create ".name" - err = rn.nameTransform.WriteLongNameAt(dirfd, cName, name) - if err != nil { - return nil, nil, 0, fs.ToErrno(err) - } - // Create content - fd, err = syscallcompat.OpenatUser(dirfd, cName, newFlags|syscall.O_CREAT|syscall.O_EXCL, mode, ctx2) - if err != nil { - nametransform.DeleteLongNameAt(dirfd, cName) - } - } else { - // Create content, normal (short) file name - fd, err = syscallcompat.OpenatUser(dirfd, cName, newFlags|syscall.O_CREAT|syscall.O_EXCL, mode, ctx2) - } - if err != nil { - // xfstests generic/488 triggers this - if err == syscall.EMFILE { - var lim syscall.Rlimit - syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim) - tlog.Warn.Printf("Create %q: too many open files. Current \"ulimit -n\": %d", cName, lim.Cur) - } - return nil, nil, 0, fs.ToErrno(err) - } - - // Get device number and inode number into `st` - var st syscall.Stat_t - err = syscall.Fstat(fd, &st) - if err != nil { - errno = fs.ToErrno(err) - return - } - // Save original stat values before newChild() translates the inode number. - // For an open fd, we assume the inode number cannot change behind our back, - // even in `-sharedstorage` mode. - origSt := st - - ch := n.newChild(ctx, &st, out) - - f := os.NewFile(uintptr(fd), cName) - return ch, NewFile(f, rn, &origSt), 0, 0 -} - // Unlink - FUSE call. Delete a file. // // Symlink-safe through use of Unlinkat(). @@ -179,56 +115,6 @@ func (n *Node) Readlink(ctx context.Context) (out []byte, errno syscall.Errno) { return n.readlink(dirfd, cName) } -// 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() - - if rn.args.KernelCache { - fuseFlags = fuse.FOPEN_KEEP_CACHE - } - - // 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 = NewFile(f, rn, &st) - return -} - // Setattr - FUSE call. Called for chmod, truncate, utimens, ... func (n *Node) Setattr(ctx context.Context, f fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) (errno syscall.Errno) { // Use the fd if the kernel gave us one diff --git a/internal/fusefrontend/node_open_create.go b/internal/fusefrontend/node_open_create.go new file mode 100644 index 0000000..8b31932 --- /dev/null +++ b/internal/fusefrontend/node_open_create.go @@ -0,0 +1,108 @@ +package fusefrontend + +import ( + "context" + "syscall" + + "github.com/hanwen/go-fuse/v2/fs" + "github.com/hanwen/go-fuse/v2/fuse" + + "github.com/rfjakob/gocryptfs/internal/nametransform" + "github.com/rfjakob/gocryptfs/internal/syscallcompat" + "github.com/rfjakob/gocryptfs/internal/tlog" +) + +// 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() + + if rn.args.KernelCache { + fuseFlags = fuse.FOPEN_KEEP_CACHE + } + + // 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 + } + fh, _, errno = NewFile(fd, cName, rn) + return fh, fuseFlags, errno +} + +// Create - FUSE call. Creates a new file. +// +// Symlink-safe through the use of Openat(). +func (n *Node) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (inode *fs.Inode, fh fs.FileHandle, fuseFlags uint32, errno syscall.Errno) { + dirfd, cName, errno := n.prepareAtSyscall(name) + if errno != 0 { + return + } + defer syscall.Close(dirfd) + + var err error + fd := -1 + // Make sure context is nil if we don't want to preserve the owner + rn := n.rootNode() + if !rn.args.PreserveOwner { + ctx = nil + } + newFlags := rn.mangleOpenFlags(flags) + // Handle long file name + ctx2 := toFuseCtx(ctx) + if !rn.args.PlaintextNames && nametransform.IsLongContent(cName) { + // Create ".name" + err = rn.nameTransform.WriteLongNameAt(dirfd, cName, name) + if err != nil { + return nil, nil, 0, fs.ToErrno(err) + } + // Create content + fd, err = syscallcompat.OpenatUser(dirfd, cName, newFlags|syscall.O_CREAT|syscall.O_EXCL, mode, ctx2) + if err != nil { + nametransform.DeleteLongNameAt(dirfd, cName) + } + } else { + // Create content, normal (short) file name + fd, err = syscallcompat.OpenatUser(dirfd, cName, newFlags|syscall.O_CREAT|syscall.O_EXCL, mode, ctx2) + } + if err != nil { + // xfstests generic/488 triggers this + if err == syscall.EMFILE { + var lim syscall.Rlimit + syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim) + tlog.Warn.Printf("Create %q: too many open files. Current \"ulimit -n\": %d", cName, lim.Cur) + } + return nil, nil, 0, fs.ToErrno(err) + } + + fh, st, errno := NewFile(fd, cName, rn) + if errno != 0 { + return + } + inode = n.newChild(ctx, st, out) + return inode, fh, fuseFlags, errno +}