From a38e5988bae3319a2c1c6745064f5785a0971d05 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Wed, 2 Jun 2021 19:10:36 +0200 Subject: fusefrontend: run acl Setxattr in user context The result of setting an acl depends on who runs the operation! Fixes fuse-xfstests generic/375 (see https://github.com/rfjakob/fuse-xfstests/wiki/results_2021-05-19) --- internal/fusefrontend/node_xattr.go | 11 +++++++++-- internal/fusefrontend/node_xattr_darwin.go | 3 ++- internal/fusefrontend/node_xattr_linux.go | 6 ++++-- internal/syscallcompat/sys_linux.go | 20 ++++++++++++++++++++ 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/internal/fusefrontend/node_xattr.go b/internal/fusefrontend/node_xattr.go index 925dcbf..ceb10f1 100644 --- a/internal/fusefrontend/node_xattr.go +++ b/internal/fusefrontend/node_xattr.go @@ -7,6 +7,8 @@ import ( "strings" "syscall" + "github.com/hanwen/go-fuse/v2/fuse" + "github.com/rfjakob/gocryptfs/internal/tlog" ) @@ -90,7 +92,12 @@ func (n *Node) Setxattr(ctx context.Context, attr string, data []byte, flags uin // ACLs are passed through without encryption if isAcl(attr) { - return n.setXAttr(attr, data, flags) + // result of setting an acl depends on the user doing it + var context *fuse.Context + if rn.args.PreserveOwner { + context = toFuseCtx(ctx) + } + return n.setXAttr(context, attr, data, flags) } cAttr, err := rn.encryptXattrName(attr) @@ -98,7 +105,7 @@ func (n *Node) Setxattr(ctx context.Context, attr string, data []byte, flags uin return syscall.EINVAL } cData := rn.encryptXattrValue(data) - return n.setXAttr(cAttr, cData, flags) + return n.setXAttr(nil, cAttr, cData, flags) } // RemoveXAttr - FUSE call. diff --git a/internal/fusefrontend/node_xattr_darwin.go b/internal/fusefrontend/node_xattr_darwin.go index 82c0dff..0f61a5d 100644 --- a/internal/fusefrontend/node_xattr_darwin.go +++ b/internal/fusefrontend/node_xattr_darwin.go @@ -6,6 +6,7 @@ import ( "golang.org/x/sys/unix" "github.com/hanwen/go-fuse/v2/fs" + "github.com/hanwen/go-fuse/v2/fuse" "github.com/rfjakob/gocryptfs/internal/syscallcompat" ) @@ -40,7 +41,7 @@ func (n *Node) getXAttr(cAttr string) (out []byte, errno syscall.Errno) { return cData, 0 } -func (n *Node) setXAttr(cAttr string, cData []byte, flags uint32) (errno syscall.Errno) { +func (n *Node) setXAttr(context *fuse.Context, cAttr string, cData []byte, flags uint32) (errno syscall.Errno) { dirfd, cName, errno := n.prepareAtSyscall("") if errno != 0 { return diff --git a/internal/fusefrontend/node_xattr_linux.go b/internal/fusefrontend/node_xattr_linux.go index 342bdf6..9698282 100644 --- a/internal/fusefrontend/node_xattr_linux.go +++ b/internal/fusefrontend/node_xattr_linux.go @@ -7,6 +7,7 @@ import ( "golang.org/x/sys/unix" "github.com/hanwen/go-fuse/v2/fs" + "github.com/hanwen/go-fuse/v2/fuse" "github.com/rfjakob/gocryptfs/internal/syscallcompat" ) @@ -30,7 +31,7 @@ func (n *Node) getXAttr(cAttr string) (out []byte, errno syscall.Errno) { return cData, 0 } -func (n *Node) setXAttr(cAttr string, cData []byte, flags uint32) (errno syscall.Errno) { +func (n *Node) setXAttr(context *fuse.Context, cAttr string, cData []byte, flags uint32) (errno syscall.Errno) { dirfd, cName, errno := n.prepareAtSyscall("") if errno != 0 { return @@ -38,7 +39,8 @@ func (n *Node) setXAttr(cAttr string, cData []byte, flags uint32) (errno syscall defer syscall.Close(dirfd) procPath := fmt.Sprintf("/proc/self/fd/%d/%s", dirfd, cName) - return fs.ToErrno(unix.Lsetxattr(procPath, cAttr, cData, int(flags))) + + return fs.ToErrno(syscallcompat.LsetxattrUser(procPath, cAttr, cData, int(flags), context)) } func (n *Node) removeXAttr(cAttr string) (errno syscall.Errno) { diff --git a/internal/syscallcompat/sys_linux.go b/internal/syscallcompat/sys_linux.go index 36ce7d5..45c18d2 100644 --- a/internal/syscallcompat/sys_linux.go +++ b/internal/syscallcompat/sys_linux.go @@ -130,6 +130,9 @@ func asUser(f func() (int, error), context *fuse.Context) (int, error) { // // It switches the current thread to the new user, performs the syscall, // and switches back. +// +// If `context` is nil, this function behaves like ordinary Openat (no +// user switching). func OpenatUser(dirfd int, path string, flags int, mode uint32, context *fuse.Context) (fd int, err error) { f := func() (int, error) { return Openat(dirfd, path, flags, mode) @@ -143,6 +146,7 @@ func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) { } // MknodatUser runs the Mknodat syscall in the context of a different user. +// If `context` is nil, this function behaves like ordinary Mknodat. // // See OpenatUser() for how this works. func MknodatUser(dirfd int, path string, mode uint32, dev int, context *fuse.Context) (err error) { @@ -195,6 +199,7 @@ func FchmodatNofollow(dirfd int, path string, mode uint32) (err error) { } // SymlinkatUser runs the Symlinkat syscall in the context of a different user. +// If `context` is nil, this function behaves like ordinary Symlinkat. // // See OpenatUser() for how this works. func SymlinkatUser(oldpath string, newdirfd int, newpath string, context *fuse.Context) (err error) { @@ -207,6 +212,7 @@ func SymlinkatUser(oldpath string, newdirfd int, newpath string, context *fuse.C } // MkdiratUser runs the Mkdirat syscall in the context of a different user. +// If `context` is nil, this function behaves like ordinary Mkdirat. // // See OpenatUser() for how this works. func MkdiratUser(dirfd int, path string, mode uint32, context *fuse.Context) (err error) { @@ -218,6 +224,20 @@ func MkdiratUser(dirfd int, path string, mode uint32, context *fuse.Context) (er return err } +// LsetxattrUser runs the Lsetxattr syscall in the context of a different user. +// This is useful when setting ACLs, as the result depends on the user running +// the operation (see fuse-xfstests generic/375). +// +// If `context` is nil, this function behaves like ordinary Lsetxattr. +func LsetxattrUser(path string, attr string, data []byte, flags int, context *fuse.Context) (err error) { + f := func() (int, error) { + err := unix.Lsetxattr(path, attr, data, flags) + return -1, err + } + _, err = asUser(f, context) + return err +} + func timesToTimespec(a *time.Time, m *time.Time) []unix.Timespec { ts := make([]unix.Timespec, 2) ts[0] = unix.Timespec(fuse.UtimeToTimespec(a)) -- cgit v1.2.3