libgocryptfs/internal/fusefrontend/xattr_darwin.go
Sebastian Lackner 5055f39bd5 fusefrontend: Allow to set/remove xattr on directory without read permission.
Setting/removing extended attributes on directories was partially fixed with
commit eff35e60b6. However, on most file systems
it is also possible to do these operations without read access (see tests).

Since we cannot open a write-access fd to a directory, we have to use the
/proc/self/fd trick (already used for ListXAttr) for the other operations aswell.
For simplicity, let's separate the Linux and Darwin code again (basically revert
commit f320b76fd1), and always use the
/proc/self/fd trick on Linux. On Darwin we use the best-effort approach with
openBackingFile() as a fallback.

More discussion about the available options is available in
https://github.com/rfjakob/gocryptfs/issues/308.
2019-01-05 12:34:40 +01:00

95 lines
2.6 KiB
Go

// +build darwin
// Package fusefrontend interfaces directly with the go-fuse library.
package fusefrontend
import (
"syscall"
"golang.org/x/sys/unix"
"github.com/hanwen/go-fuse/fuse"
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
)
func disallowedXAttrName(attr string) bool {
return false
}
// On Darwin it is needed to unset XATTR_NOSECURITY 0x0008
func filterXattrSetFlags(flags int) int {
// See https://opensource.apple.com/source/xnu/xnu-1504.15.3/bsd/sys/xattr.h.auto.html
const XATTR_NOSECURITY = 0x0008
return flags &^ XATTR_NOSECURITY
}
func (fs *FS) getXAttr(relPath string, cAttr string, context *fuse.Context) ([]byte, fuse.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 syscall.Close(fd)
cData, err := syscallcompat.Fgetxattr(fd, cAttr)
if err != nil {
return nil, fuse.ToStatus(err)
}
return cData, fuse.OK
}
func (fs *FS) setXAttr(relPath string, cAttr string, cData []byte, flags int, context *fuse.Context) fuse.Status {
// O_NONBLOCK to not block on FIFOs.
fd, err := fs.openBackingFile(relPath, syscall.O_WRONLY|syscall.O_NONBLOCK)
// Directories cannot be opened read-write. Retry.
if err == syscall.EISDIR {
fd, err = fs.openBackingFile(relPath, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NONBLOCK)
}
if err != nil {
return fuse.ToStatus(err)
}
defer syscall.Close(fd)
err = unix.Fsetxattr(fd, cAttr, cData, flags)
return fuse.ToStatus(err)
}
func (fs *FS) removeXAttr(relPath string, cAttr string, context *fuse.Context) fuse.Status {
// O_NONBLOCK to not block on FIFOs.
fd, err := fs.openBackingFile(relPath, syscall.O_WRONLY|syscall.O_NONBLOCK)
// Directories cannot be opened read-write. Retry.
if err == syscall.EISDIR {
fd, err = fs.openBackingFile(relPath, syscall.O_RDONLY|syscall.O_DIRECTORY|syscall.O_NONBLOCK)
}
if err != nil {
return fuse.ToStatus(err)
}
defer syscall.Close(fd)
err = unix.Fremovexattr(fd, cAttr)
return fuse.ToStatus(err)
}
func (fs *FS) listXAttr(relPath string, context *fuse.Context) ([]string, fuse.Status) {
// O_NONBLOCK to not block on FIFOs.
fd, err := fs.openBackingFile(relPath, syscall.O_RDONLY|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 err == syscall.ELOOP {
return nil, fuse.OK
}
if err != nil {
return nil, fuse.ToStatus(err)
}
defer syscall.Close(fd)
cNames, err := syscallcompat.Flistxattr(fd)
if err != nil {
return nil, fuse.ToStatus(err)
}
return cNames, fuse.OK
}