From 0fa824933cfc8788450138a2d2e52c3dea2e592f Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sun, 12 Jul 2020 20:17:15 +0200 Subject: [PATCH] v2api: properly implement Node.Setattr We used to always open a *File2 and letting the *File2 handle Setattr. This does not work it we cannot open the file! Before: $ go test 2020/07/12 20:14:57 writer: Write/Writev failed, err: 2=no such file or directory. opcode: INTERRUPT 2020/07/12 20:14:57 writer: Write/Writev failed, err: 2=no such file or directory. opcode: INTERRUPT --- FAIL: TestLchown (0.00s) matrix_test.go:634: lchown /tmp/gocryptfs-test-parent-1026/014500839/default-plain/symlink: too many levels of symbolic links touch: setting times of '/tmp/gocryptfs-test-parent-1026/014500839/default-plain/utimesnano_symlink': Too many levels of symbolic links --- FAIL: TestUtimesNanoSymlink (0.00s) matrix_test.go:655: exit status 1 --- FAIL: TestMkfifo (0.00s) matrix_test.go:755: file exists --- FAIL: TestMagicNames (0.00s) matrix_test.go:773: Testing n="gocryptfs.longname.QhUr5d9FHerwEs--muUs6_80cy6JRp89c1otLwp92Cs" matrix_test.go:773: Testing n="gocryptfs.diriv" matrix_test.go:815: open /tmp/gocryptfs-test-parent-1026/014500839/default-plain/linktarget: permission denied --- FAIL: TestChmod (0.00s) matrix_test.go:840: chmod 444 -> 000 failed: permission denied matrix_test.go:840: chmod 444 -> 111 failed: permission denied matrix_test.go:840: chmod 444 -> 123 failed: permission denied matrix_test.go:840: chmod 444 -> 321 failed: permission denied FAIL exit status 1 FAIL github.com/rfjakob/gocryptfs/tests/matrix 0.790s After: $ go test --- FAIL: TestMkfifo (0.00s) matrix_test.go:755: file exists --- FAIL: TestMagicNames (0.00s) matrix_test.go:773: Testing n="gocryptfs.longname.QhUr5d9FHerwEs--muUs6_80cy6JRp89c1otLwp92Cs" matrix_test.go:773: Testing n="gocryptfs.diriv" matrix_test.go:815: open /tmp/gocryptfs-test-parent-1026/501766059/default-plain/linktarget: permission denied --- FAIL: TestChmod (0.00s) matrix_test.go:849: modeHave 0644 != modeWant 0 FAIL exit status 1 FAIL github.com/rfjakob/gocryptfs/tests/matrix 0.787s --- internal/fusefrontend/file2_setattr.go | 4 +- internal/fusefrontend/node.go | 71 +++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/internal/fusefrontend/file2_setattr.go b/internal/fusefrontend/file2_setattr.go index 697e0d9..1385f3f 100644 --- a/internal/fusefrontend/file2_setattr.go +++ b/internal/fusefrontend/file2_setattr.go @@ -29,7 +29,7 @@ func (f *File2) setAttr(ctx context.Context, in *fuse.SetAttrIn) (errno syscall. f.fileTableEntry.ContentLock.Lock() defer f.fileTableEntry.ContentLock.Unlock() - // chmod(2) & fchmod(2) + // fchmod(2) if mode, ok := in.GetMode(); ok { errno = fs.ToErrno(syscall.Fchmod(f.intFd(), mode)) if errno != 0 { @@ -37,7 +37,7 @@ func (f *File2) setAttr(ctx context.Context, in *fuse.SetAttrIn) (errno syscall. } } - // chown(2) & fchown(2) + // fchown(2) uid32, uOk := in.GetUID() gid32, gOk := in.GetGID() if uOk || gOk { diff --git a/internal/fusefrontend/node.go b/internal/fusefrontend/node.go index a6272b5..b03829e 100644 --- a/internal/fusefrontend/node.go +++ b/internal/fusefrontend/node.go @@ -296,18 +296,77 @@ func (n *Node) Open(ctx context.Context, flags uint32) (fh fs.FileHandle, fuseFl // 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 + // Use the fd if the kernel gave us one if f != nil { - f2 = f.(*File2) - } else { + f2 := f.(*File2) + return f2.Setattr(ctx, in, out) + } + + dirfd, cName, errno := n.prepareAtSyscall("") + if errno != 0 { + return + } + defer syscall.Close(dirfd) + + // chmod(2) + if mode, ok := in.GetMode(); ok { + errno = fs.ToErrno(syscallcompat.FchmodatNofollow(dirfd, cName, mode)) + if errno != 0 { + return errno + } + } + + // chown(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(syscallcompat.Fchownat(dirfd, cName, uid, gid, unix.AT_SYMLINK_NOFOLLOW)) + 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.UtimesNanoAtNofollow(dirfd, cName, ap, mp)) + if errno != 0 { + return errno + } + } + + // For truncate, the user has to have write permissions. That means we can + // depend on opening a RDWR fd and letting the File handle truncate. + if sz, ok := in.GetSize(); ok { f, _, errno := n.Open(ctx, syscall.O_RDWR) if errno != 0 { return errno } - f2 = f.(*File2) - defer f2.Release(ctx) + f2 := f.(*File2) + errno = syscall.Errno(f2.truncate(sz)) + if errno != 0 { + return errno + } } - return f2.Setattr(ctx, in, out) + return 0 } // StatFs - FUSE call. Returns information about the filesystem.