diff options
| author | Jakob Unterwurzacher | 2020-07-12 20:17:15 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2020-07-12 20:17:15 +0200 | 
| commit | 0fa824933cfc8788450138a2d2e52c3dea2e592f (patch) | |
| tree | c07ef1a0065db62bd52b51907c66455b84e23c10 | |
| parent | f11432d02ab8fce306ba85e735b7c3db085f9da6 (diff) | |
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
| -rw-r--r-- | internal/fusefrontend/file2_setattr.go | 4 | ||||
| -rw-r--r-- | 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. | 
