From 810d2a8b474e0102a8be3f6b00a3855e182dbd43 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sun, 11 Nov 2018 17:57:24 +0100 Subject: [PATCH] fusefrontend: make SetXAttr() symlink-safe on Linux Uses the /proc/self/fd trick. --- internal/fusefrontend/xattr.go | 25 ++++++++++--------------- internal/fusefrontend/xattr_darwin.go | 9 +++++++++ internal/fusefrontend/xattr_linux.go | 20 ++++++++++++++++++-- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/internal/fusefrontend/xattr.go b/internal/fusefrontend/xattr.go index caf1e15..b511bcf 100644 --- a/internal/fusefrontend/xattr.go +++ b/internal/fusefrontend/xattr.go @@ -26,8 +26,8 @@ var xattrStorePrefix = "user.gocryptfs." // GetXAttr - FUSE call. Reads the value of extended attribute "attr". // // This function is symlink-safe on Linux. -// Darwin does not have fgetxattr(2) nor /proc. How to implement this on Darwin -// in a symlink-safe way? +// Darwin does not have fgetxattr(2) nor /proc/self/fd. How to implement this +// on Darwin in a symlink-safe way? func (fs *FS) GetXAttr(relPath string, attr string, context *fuse.Context) ([]byte, fuse.Status) { if fs.isFiltered(relPath) { return nil, fuse.EPERM @@ -48,27 +48,22 @@ func (fs *FS) GetXAttr(relPath string, attr string, context *fuse.Context) ([]by return data, fuse.OK } -// SetXAttr - FUSE call. +// SetXAttr - FUSE call. Set extended attribute. // -// TODO: Make symlink-safe. Currently blocked because the xattr package does -// not provide fsetxattr. -func (fs *FS) SetXAttr(path string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status { - if fs.isFiltered(path) { +// This function is symlink-safe on Linux. +// Darwin does not have fsetxattr(2) nor /proc/self/fd. How to implement this +// on Darwin in a symlink-safe way? +func (fs *FS) SetXAttr(relPath string, attr string, data []byte, flags int, context *fuse.Context) fuse.Status { + if fs.isFiltered(relPath) { return fuse.EPERM } if disallowedXAttrName(attr) { return _EOPNOTSUPP } - flags = filterXattrSetFlags(flags) - - cPath, err := fs.getBackingPath(path) - if err != nil { - return fuse.ToStatus(err) - } cAttr := fs.encryptXattrName(attr) cData := fs.encryptXattrValue(data) - return unpackXattrErr(xattr.LSetWithFlags(cPath, cAttr, cData, flags)) + return fs.setXattr(relPath, cAttr, cData, flags, context) } // RemoveXAttr - FUSE call. @@ -175,7 +170,7 @@ func (fs *FS) decryptXattrValue(cData []byte) (data []byte, err error) { } // unpackXattrErr unpacks an error value that we got from xattr.LGet/LSet/etc -// and converts it to a fuse status. +// and converts it to a fuse status. If err == nil, it returns fuse.OK. func unpackXattrErr(err error) fuse.Status { if err == nil { return fuse.OK diff --git a/internal/fusefrontend/xattr_darwin.go b/internal/fusefrontend/xattr_darwin.go index cf48d13..e605f83 100644 --- a/internal/fusefrontend/xattr_darwin.go +++ b/internal/fusefrontend/xattr_darwin.go @@ -30,3 +30,12 @@ func (fs *FS) getXattr(relPath string, cAttr string, context *fuse.Context) ([]b } return cData, fuse.OK } + +func (fs *FS) setXattr(relPath string, cAttr string, cData []byte, flags int, context *fuse.Context) fuse.Status { + cPath, err := fs.getBackingPath(relPath) + if err != nil { + return fuse.ToStatus(err) + } + err = xattr.LSetWithFlags(cPath, cAttr, cData, flags) + return unpackXattrErr(err) +} diff --git a/internal/fusefrontend/xattr_linux.go b/internal/fusefrontend/xattr_linux.go index 5a189db..659657d 100644 --- a/internal/fusefrontend/xattr_linux.go +++ b/internal/fusefrontend/xattr_linux.go @@ -28,6 +28,7 @@ func filterXattrSetFlags(flags int) int { return flags } +// procFd returns the path to file descriptor "fd" in /proc/self/fd. func procFd(fd int) string { return fmt.Sprintf("/proc/self/fd/%d", fd) } @@ -51,10 +52,10 @@ func (fs *FS) getFileFd(relPath string, context *fuse.Context) (*File, int, fuse return file, file.intFd(), fuse.OK } -// getXattr - read encrypted xattr name "cAttr" from the file at relative +// getXattr - read encrypted xattr name "cAttr" from relative // plaintext path "relPath". Returns the encrypted xattr value. // -// This function is symlink-safe. +// This function is symlink-safe by using /proc/self/fd. func (fs *FS) getXattr(relPath string, cAttr string, context *fuse.Context) ([]byte, fuse.Status) { file, fd, status := fs.getFileFd(relPath, context) if !status.Ok() { @@ -68,3 +69,18 @@ func (fs *FS) getXattr(relPath string, cAttr string, context *fuse.Context) ([]b } return cData, fuse.OK } + +// setXattr - set encrypted xattr name "cAttr" to value "cData" on plaintext +// path "relPath". +// +// This function is symlink-safe by using /proc/self/fd. +func (fs *FS) setXattr(relPath string, cAttr string, cData []byte, flags int, context *fuse.Context) fuse.Status { + file, fd, status := fs.getFileFd(relPath, context) + if !status.Ok() { + return status + } + defer file.Release() + + err := xattr.SetWithFlags(procFd(fd), cAttr, cData, flags) + return unpackXattrErr(err) +}