diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go index b4819fd..8f77538 100644 --- a/internal/fusefrontend/fs.go +++ b/internal/fusefrontend/fs.go @@ -10,6 +10,8 @@ import ( "syscall" "time" + "golang.org/x/sys/unix" + "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" "github.com/hanwen/go-fuse/fuse/pathfs" @@ -280,15 +282,14 @@ func (fs *FS) Mknod(path string, mode uint32, dev uint32, context *fuse.Context) if err != nil { return fuse.ToStatus(err) } + dirfd, err := os.Open(filepath.Dir(cPath)) + if err != nil { + return fuse.ToStatus(err) + } + defer dirfd.Close() // Create ".name" file to store long file name cName := filepath.Base(cPath) if nametransform.IsLongContent(cName) { - var dirfd *os.File - dirfd, err = os.Open(filepath.Dir(cPath)) - if err != nil { - return fuse.ToStatus(err) - } - defer dirfd.Close() err = fs.nameTransform.WriteLongName(dirfd, cName, path) if err != nil { return fuse.ToStatus(err) @@ -300,16 +301,17 @@ func (fs *FS) Mknod(path string, mode uint32, dev uint32, context *fuse.Context) } } else { // Create regular device node - err = syscall.Mknod(cPath, mode, int(dev)) + err = syscallcompat.Mknodat(int(dirfd.Fd()), cName, mode, int(dev)) } if err != nil { return fuse.ToStatus(err) } // Set owner if fs.args.PreserveOwner { - err = os.Lchown(cPath, int(context.Owner.Uid), int(context.Owner.Gid)) + err = syscallcompat.Fchownat(int(dirfd.Fd()), cName, int(context.Owner.Uid), + int(context.Owner.Gid), unix.AT_SYMLINK_NOFOLLOW) if err != nil { - tlog.Warn.Printf("Mknod: Lchown failed: %v", err) + tlog.Warn.Printf("Mknod: Fchownat failed: %v", err) } } return fuse.OK diff --git a/internal/syscallcompat/sys_darwin.go b/internal/syscallcompat/sys_darwin.go index e77cd98..5bc5fba 100644 --- a/internal/syscallcompat/sys_darwin.go +++ b/internal/syscallcompat/sys_darwin.go @@ -131,3 +131,19 @@ func Dup3(oldfd int, newfd int, flags int) (err error) { } return syscall.Dup2(oldfd, newfd) } + +// Poor man's Fchownat. +func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) { + cwd, err := syscall.Open(".", syscall.O_RDONLY, 0) + if err != nil { + return err + } + chdirMutex.Lock() + defer chdirMutex.Unlock() + err = syscall.Fchdir(dirfd) + if err != nil { + return err + } + defer syscall.Fchdir(cwd) + return syscall.Lchown(path, uid, gid) +} diff --git a/internal/syscallcompat/sys_linux.go b/internal/syscallcompat/sys_linux.go index 441f904..8a64a81 100644 --- a/internal/syscallcompat/sys_linux.go +++ b/internal/syscallcompat/sys_linux.go @@ -68,3 +68,8 @@ func Mknodat(dirfd int, path string, mode uint32, dev int) (err error) { func Dup3(oldfd int, newfd int, flags int) (err error) { return syscall.Dup3(oldfd, newfd, flags) } + +// Fchownat syscall. +func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) { + return syscall.Fchownat(dirfd, path, uid, gid, flags) +} diff --git a/tests/matrix/matrix_test.go b/tests/matrix/matrix_test.go index 9b9cb1b..170f8ba 100644 --- a/tests/matrix/matrix_test.go +++ b/tests/matrix/matrix_test.go @@ -781,3 +781,12 @@ func TestUtimesNanoFd(t *testing.T) { procPath := fmt.Sprintf("/proc/self/fd/%d", f.Fd()) doTestUtimesNano(t, procPath) } + +// Make sure the Mknod call works by creating a fifo (named pipe) +func TestMkfifo(t *testing.T) { + path := test_helpers.DefaultPlainDir + "/fifo1" + err := syscall.Mkfifo(path, 0700) + if err != nil { + t.Fatal(err) + } +}