diff --git a/internal/fusefrontend/file2.go b/internal/fusefrontend/file2.go index 2882732..7fe3d3a 100644 --- a/internal/fusefrontend/file2.go +++ b/internal/fusefrontend/file2.go @@ -4,6 +4,7 @@ package fusefrontend import ( "bytes" + "context" "encoding/hex" "fmt" "io" @@ -11,10 +12,9 @@ import ( "os" "sync" "syscall" - "time" + "github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fuse" - "github.com/hanwen/go-fuse/v2/fuse/nodefs" "github.com/rfjakob/gocryptfs/internal/contentenc" "github.com/rfjakob/gocryptfs/internal/inomap" @@ -25,9 +25,6 @@ import ( "github.com/rfjakob/gocryptfs/internal/tlog" ) -var _ nodefs.File = &File{} // Verify that interface is implemented. - -// File - based on loopbackFile in go-fuse/fuse/nodefs/files.go type File2 struct { fd *os.File // Has Release() already been called on this file? This also means that the @@ -53,10 +50,6 @@ type File2 struct { lastOpCount uint64 // Parent filesystem rootNode *RootNode - // We embed a nodefs.NewDefaultFile() that returns ENOSYS for every operation we - // have not implemented. This prevents build breakage when the go-fuse library - // adds new methods to the nodefs.File interface. - nodefs.File } // NewFile returns a new go-fuse File instance. @@ -70,7 +63,6 @@ func NewFile2(fd *os.File, rn *RootNode, st *syscall.Stat_t) *File2 { qIno: qi, fileTableEntry: e, rootNode: rn, - File: nodefs.NewDefaultFile(), } } @@ -427,27 +419,8 @@ func (f *File2) Fsync(flags int) (code fuse.Status) { return fuse.ToStatus(syscall.Fsync(f.intFd())) } -// Chmod FUSE call -func (f *File2) Chmod(mode uint32) fuse.Status { - f.fdLock.RLock() - defer f.fdLock.RUnlock() - - // os.File.Chmod goes through the "syscallMode" translation function that messes - // up the suid and sgid bits. So use syscall.Fchmod directly. - err := syscall.Fchmod(f.intFd(), mode) - return fuse.ToStatus(err) -} - -// Chown FUSE call -func (f *File2) Chown(uid uint32, gid uint32) fuse.Status { - f.fdLock.RLock() - defer f.fdLock.RUnlock() - - return fuse.ToStatus(f.fd.Chown(int(uid), int(gid))) -} - -// GetAttr FUSE call (like stat) -func (f *File2) GetAttr(a *fuse.Attr) fuse.Status { +// Getattr FUSE call (like stat) +func (f *File2) Getattr(ctx context.Context, a *fuse.AttrOut) syscall.Errno { f.fdLock.RLock() defer f.fdLock.RUnlock() @@ -455,7 +428,7 @@ func (f *File2) GetAttr(a *fuse.Attr) fuse.Status { st := syscall.Stat_t{} err := syscall.Fstat(f.intFd(), &st) if err != nil { - return fuse.ToStatus(err) + return fs.ToErrno(err) } f.rootNode.inoMap.TranslateStat(&st) a.FromStat(&st) @@ -464,13 +437,5 @@ func (f *File2) GetAttr(a *fuse.Attr) fuse.Status { a.Owner = *f.rootNode.args.ForceOwner } - return fuse.OK -} - -// Utimens FUSE call -func (f *File2) Utimens(a *time.Time, m *time.Time) fuse.Status { - f.fdLock.RLock() - defer f.fdLock.RUnlock() - err := syscallcompat.FutimesNano(f.intFd(), a, m) - return fuse.ToStatus(err) + return 0 } diff --git a/internal/fusefrontend/file2_allocate_truncate.go b/internal/fusefrontend/file2_allocate_truncate.go index f799a3e..9a3d7d1 100644 --- a/internal/fusefrontend/file2_allocate_truncate.go +++ b/internal/fusefrontend/file2_allocate_truncate.go @@ -81,8 +81,8 @@ func (f *File2) Allocate(off uint64, sz uint64, mode uint32) fuse.Status { return f.truncateGrowFile(oldPlainSz, newPlainSz) } -// Truncate - FUSE call -func (f *File2) Truncate(newSize uint64) fuse.Status { +// truncate - called from Setattr. +func (f *File2) truncate(newSize uint64) fuse.Status { f.fdLock.RLock() defer f.fdLock.RUnlock() if f.released { diff --git a/internal/fusefrontend/file2_setattr.go b/internal/fusefrontend/file2_setattr.go new file mode 100644 index 0000000..c341e53 --- /dev/null +++ b/internal/fusefrontend/file2_setattr.go @@ -0,0 +1,83 @@ +package fusefrontend + +import ( + "context" + "syscall" + + "github.com/hanwen/go-fuse/v2/fs" + "github.com/hanwen/go-fuse/v2/fuse" + + "github.com/rfjakob/gocryptfs/internal/syscallcompat" +) + +func (f *File2) Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) (errno syscall.Errno) { + errno = f.setAttr(ctx, in) + if errno != 0 { + return errno + } + return f.Getattr(ctx, out) +} + +func (f *File2) setAttr(ctx context.Context, in *fuse.SetAttrIn) (errno syscall.Errno) { + f.fdLock.RLock() + defer f.fdLock.RUnlock() + if f.released { + return syscall.EBADF + } + f.fileTableEntry.ContentLock.Lock() + defer f.fileTableEntry.ContentLock.Unlock() + + // chmod(2) & fchmod(2) + if mode, ok := in.GetMode(); ok { + errno = fs.ToErrno(syscall.Fchmod(f.intFd(), mode)) + if errno != 0 { + return errno + } + } + + // chown(2) & fchown(2) + uid32, uOk := in.GetUID() + gid32, gOk := in.GetGID() + if uOk || gOk { + uid := -1 + gid := -1 + + if uOk { + uid = int(uid32) + } + if gOk { + gid = int(gid32) + } + errno = fs.ToErrno(syscall.Fchown(f.intFd(), uid, gid)) + if errno != 0 { + return errno + } + } + + // utimens(2) + mtime, mok := in.GetMTime() + atime, aok := in.GetATime() + if mok || aok { + ap := &atime + mp := &mtime + if !aok { + ap = nil + } + if !mok { + mp = nil + } + errno = fs.ToErrno(syscallcompat.FutimesNano(f.intFd(), ap, mp)) + if errno != 0 { + return errno + } + } + + // truncate(2) + if sz, ok := in.GetSize(); ok { + errno = syscall.Errno(f.truncate(sz)) + if errno != 0 { + return errno + } + } + return 0 +} diff --git a/internal/fusefrontend/node.go b/internal/fusefrontend/node.go index 239ec54..dae38df 100644 --- a/internal/fusefrontend/node.go +++ b/internal/fusefrontend/node.go @@ -266,3 +266,19 @@ func (n *Node) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFl fh = NewFile2(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) { + var f2 *File2 + if f != nil { + f2 = f.(*File2) + } else { + f, _, errno := n.Open(ctx, syscall.O_RDWR) + if errno != 0 { + return errno + } + f2 = f.(*File2) + defer f2.Release() + } + return f2.Setattr(ctx, in, out) +} diff --git a/internal/fusefrontend/node_api_check.go b/internal/fusefrontend/node_api_check.go index d9facb7..2b3d02b 100644 --- a/internal/fusefrontend/node_api_check.go +++ b/internal/fusefrontend/node_api_check.go @@ -15,3 +15,4 @@ var _ = (fs.NodeUnlinker)((*Node)(nil)) var _ = (fs.NodeReadlinker)((*Node)(nil)) var _ = (fs.NodeOpener)((*Node)(nil)) var _ = (fs.NodeOpendirer)((*Node)(nil)) +var _ = (fs.NodeSetattrer)((*Node)(nil))