diff options
author | Jakob Unterwurzacher | 2019-01-02 20:48:46 +0100 |
---|---|---|
committer | Jakob Unterwurzacher | 2019-01-02 20:48:46 +0100 |
commit | b214be5e3f76dd17efc9832131f4a7e0414b4cea (patch) | |
tree | 511e71e93a919f7319df7ee774eaa5c2e1fd974c /internal/fusefrontend | |
parent | d269c28d169cdf071acf57d283b756cde2b6437f (diff) |
fusefrontend: xattr: fix operations on files without read permissions
* listxattr is fixed via the /proc/self/fd trick
* setxattr,removexattr are fixed by opening the file O_WRONLY
Fixes https://github.com/rfjakob/gocryptfs/issues/308
Diffstat (limited to 'internal/fusefrontend')
-rw-r--r-- | internal/fusefrontend/fs.go | 11 | ||||
-rw-r--r-- | internal/fusefrontend/xattr.go | 88 |
2 files changed, 53 insertions, 46 deletions
diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go index e7c3903..5c52a19 100644 --- a/internal/fusefrontend/fs.go +++ b/internal/fusefrontend/fs.go @@ -160,6 +160,17 @@ func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile n return NewFile(f, fs) } +// openBackingFile opens the ciphertext file that backs relative plaintext +// path "relPath". Always adds O_NOFOLLOW to the flags. +func (fs *FS) openBackingFile(relPath string, flags int) (fd int, err error) { + dirfd, cName, err := fs.openBackingDir(relPath) + if err != nil { + return -1, err + } + defer syscall.Close(dirfd) + return syscallcompat.Openat(dirfd, cName, flags|syscall.O_NOFOLLOW, 0) +} + // 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, diff --git a/internal/fusefrontend/xattr.go b/internal/fusefrontend/xattr.go index 045f963..7ffec25 100644 --- a/internal/fusefrontend/xattr.go +++ b/internal/fusefrontend/xattr.go @@ -2,6 +2,8 @@ package fusefrontend import ( + "fmt" + "runtime" "strings" "syscall" @@ -34,11 +36,12 @@ func (fs *FS) GetXAttr(relPath string, attr string, context *fuse.Context) ([]by return nil, _EOPNOTSUPP } - file, fd, status := fs.getFileFd(relPath, context) - if !status.Ok() { - return nil, status + // O_NONBLOCK to not block on FIFOs. + fd, err := fs.openBackingFile(relPath, syscall.O_RDONLY|syscall.O_NONBLOCK) + if err != nil { + return nil, fuse.ToStatus(err) } - defer file.Release() + defer syscall.Close(fd) cAttr := fs.encryptXattrName(attr) @@ -66,17 +69,18 @@ func (fs *FS) SetXAttr(relPath string, attr string, data []byte, flags int, cont return _EOPNOTSUPP } - file, fd, status := fs.getFileFd(relPath, context) - if !status.Ok() { - return status + // O_NONBLOCK to not block on FIFOs. + fd, err := fs.openBackingFile(relPath, syscall.O_WRONLY|syscall.O_NONBLOCK) + if err != nil { + return fuse.ToStatus(err) } - defer file.Release() + defer syscall.Close(fd) flags = filterXattrSetFlags(flags) cAttr := fs.encryptXattrName(attr) cData := fs.encryptXattrValue(data) - err := unix.Fsetxattr(fd, cAttr, cData, flags) + err = unix.Fsetxattr(fd, cAttr, cData, flags) if err != nil { return fuse.ToStatus(err) } @@ -94,14 +98,15 @@ func (fs *FS) RemoveXAttr(relPath string, attr string, context *fuse.Context) fu return _EOPNOTSUPP } - file, fd, status := fs.getFileFd(relPath, context) - if !status.Ok() { - return status + // O_NONBLOCK to not block on FIFOs. + fd, err := fs.openBackingFile(relPath, syscall.O_WRONLY|syscall.O_NONBLOCK) + if err != nil { + return fuse.ToStatus(err) } - defer file.Release() + defer syscall.Close(fd) cAttr := fs.encryptXattrName(attr) - err := unix.Fremovexattr(fd, cAttr) + err = unix.Fremovexattr(fd, cAttr) if err != nil { return fuse.ToStatus(err) } @@ -115,19 +120,30 @@ func (fs *FS) ListXAttr(relPath string, context *fuse.Context) ([]string, fuse.S if fs.isFiltered(relPath) { return nil, fuse.EPERM } - - file, fd, status := fs.getFileFd(relPath, context) - // On a symlink, getFileFd fails with ELOOP. Let's pretend there - // can be no xattrs on symlinks, and always return an empty result. - if status == fuse.Status(syscall.ELOOP) { - return nil, fuse.OK - } - if !status.Ok() { - return nil, status + var cNames []string + var err error + if runtime.GOOS == "linux" { + dirfd, cName, err2 := fs.openBackingDir(relPath) + if err2 != nil { + return nil, fuse.ToStatus(err2) + } + defer syscall.Close(dirfd) + procPath := fmt.Sprintf("/proc/self/fd/%d/%s", dirfd, cName) + cNames, err = syscallcompat.Llistxattr(procPath) + } else { + // O_NONBLOCK to not block on FIFOs. + fd, err2 := fs.openBackingFile(relPath, syscall.O_WRONLY|syscall.O_NONBLOCK) + // On a symlink, openBackingFile fails with ELOOP. Let's pretend there + // can be no xattrs on symlinks, and always return an empty result. + if err2 == syscall.ELOOP { + return nil, fuse.OK + } + if err2 != nil { + return nil, fuse.ToStatus(err2) + } + defer syscall.Close(fd) + cNames, err = syscallcompat.Flistxattr(fd) } - defer file.Release() - - cNames, err := syscallcompat.Flistxattr(fd) if err != nil { return nil, fuse.ToStatus(err) } @@ -199,23 +215,3 @@ func (fs *FS) decryptXattrValue(cData []byte) (data []byte, err error) { } return fs.contentEnc.DecryptBlock([]byte(cData), 0, nil) } - -// getFileFd calls fs.Open() on relative plaintext path "relPath" and returns -// the resulting fusefrontend.*File along with the underlying fd. The caller -// MUST call file.Release() when done with the file. The O_NONBLOCK flag is -// used to not block on FIFOs. -// -// Used by xattrGet() and friends. -func (fs *FS) getFileFd(relPath string, context *fuse.Context) (*File, int, fuse.Status) { - fuseFile, status := fs.Open(relPath, syscall.O_RDONLY|syscall.O_NONBLOCK, context) - if !status.Ok() { - return nil, -1, status - } - file, ok := fuseFile.(*File) - if !ok { - tlog.Warn.Printf("BUG: xattrGet: cast to *File failed") - fuseFile.Release() - return nil, -1, fuse.EIO - } - return file, file.intFd(), fuse.OK -} |