Skip to content

Commit

Permalink
fusefrontend: run acl Setxattr in user context
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
rfjakob committed Jun 2, 2021
1 parent b23e21f commit a38e598
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 5 deletions.
11 changes: 9 additions & 2 deletions internal/fusefrontend/node_xattr.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"strings"
"syscall"

"github.com/hanwen/go-fuse/v2/fuse"

"github.com/rfjakob/gocryptfs/internal/tlog"
)

Expand Down Expand Up @@ -90,15 +92,20 @@ 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)
if err != nil {
return syscall.EINVAL
}
cData := rn.encryptXattrValue(data)
return n.setXAttr(cAttr, cData, flags)
return n.setXAttr(nil, cAttr, cData, flags)
}

// RemoveXAttr - FUSE call.
Expand Down
3 changes: 2 additions & 1 deletion internal/fusefrontend/node_xattr_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions internal/fusefrontend/node_xattr_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -30,15 +31,16 @@ 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
}
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) {
Expand Down
20 changes: 20 additions & 0 deletions internal/syscallcompat/sys_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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))
Expand Down

0 comments on commit a38e598

Please sign in to comment.