From e33593d30d9dee6fd6b0e7c0d01832e478815d88 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sun, 3 Dec 2017 17:53:14 +0100 Subject: [PATCH] syscallcompat: add Fstatat + emulation + test Fstatat has recently been added to x/sys/unix. Make it available for use in gocryptfs. --- internal/syscallcompat/emulate.go | 20 +++++++++++++++ internal/syscallcompat/emulate_test.go | 35 ++++++++++++++++++++++++++ internal/syscallcompat/sys_darwin.go | 12 +++++++++ internal/syscallcompat/sys_linux.go | 10 ++++++++ 4 files changed, 77 insertions(+) diff --git a/internal/syscallcompat/emulate.go b/internal/syscallcompat/emulate.go index 96776e2..e5aeae7 100644 --- a/internal/syscallcompat/emulate.go +++ b/internal/syscallcompat/emulate.go @@ -210,3 +210,23 @@ func emulateMkdirat(dirfd int, path string, mode uint32) (err error) { } return syscall.Mkdir(path, mode) } + +// emulateFstatat emulates the syscall for platforms that don't have it +// in the kernel (darwin). +func emulateFstatat(dirfd int, path string, stat *unix.Stat_t, flags int) (err error) { + if !filepath.IsAbs(path) { + chdirMutex.Lock() + defer chdirMutex.Unlock() + cwd, err := syscall.Open(".", syscall.O_RDONLY, 0) + if err != nil { + return err + } + defer syscall.Close(cwd) + err = syscall.Fchdir(dirfd) + if err != nil { + return err + } + defer syscall.Fchdir(cwd) + } + return unix.Lstat(path, stat) +} diff --git a/internal/syscallcompat/emulate_test.go b/internal/syscallcompat/emulate_test.go index 9b45672..c259778 100644 --- a/internal/syscallcompat/emulate_test.go +++ b/internal/syscallcompat/emulate_test.go @@ -256,3 +256,38 @@ func TestEmulateMkdirat(t *testing.T) { t.Fatalf("mkdirat did not create a directory") } } + +func TestEmulateFstatat(t *testing.T) { + var st unix.Stat_t + // stat a normal file (size 3) + f, err := os.Create(tmpDir + "/fstatat") + if err != nil { + t.Fatal(err) + } + _, err = f.Write([]byte("foo")) + if err != nil { + t.Fatal(err) + } + f.Close() + err = emulateFstatat(tmpDirFd, "fstatat", &st, unix.AT_SYMLINK_NOFOLLOW) + if err != nil { + t.Fatal(err) + } + if st.Size != 3 { + t.Errorf("wrong file size: %d", st.Size) + } + // stat a symlink and check that the size matches the length of the + // symlink target. This proves that we have stat'ed the symlink itself. + err = os.Symlink(tmpDir+"/fstatat", tmpDir+"/fstatatSymlink") + if err != nil { + t.Fatal(err) + } + err = emulateFstatat(tmpDirFd, "fstatatSymlink", &st, unix.AT_SYMLINK_NOFOLLOW) + if err != nil { + t.Fatal(err) + } + expectedSize := int64(len(tmpDir + "/fstatat")) + if st.Size != expectedSize { + t.Errorf("symlink size: expected=%d, got=%d", expectedSize, st.Size) + } +} diff --git a/internal/syscallcompat/sys_darwin.go b/internal/syscallcompat/sys_darwin.go index 6238940..7f2c541 100644 --- a/internal/syscallcompat/sys_darwin.go +++ b/internal/syscallcompat/sys_darwin.go @@ -3,6 +3,10 @@ package syscallcompat import ( "log" "syscall" + + "golang.org/x/sys/unix" + + "github.com/hanwen/go-fuse/fuse" ) // Sorry, fallocate is not available on OSX at all and @@ -60,3 +64,11 @@ func Symlinkat(oldpath string, newdirfd int, newpath string) (err error) { func Mkdirat(dirfd int, path string, mode uint32) (err error) { return emulateMkdirat(dirfd, path, mode) } + +func Fstatat(dirfd int, path string, stat *unix.Stat_t, flags int) (err error) { + return emulateFstatat(dirfd, path, stat, flags) +} + +func Getdents(fd int) ([]fuse.DirEntry, error) { + return emulateGetdents(fd) +} diff --git a/internal/syscallcompat/sys_linux.go b/internal/syscallcompat/sys_linux.go index 0e03143..fb8d8b3 100644 --- a/internal/syscallcompat/sys_linux.go +++ b/internal/syscallcompat/sys_linux.go @@ -105,3 +105,13 @@ func Symlinkat(oldpath string, newdirfd int, newpath string) (err error) { func Mkdirat(dirfd int, path string, mode uint32) (err error) { return syscall.Mkdirat(dirfd, path, mode) } + +// Fstatat syscall. +func Fstatat(dirfd int, path string, stat *unix.Stat_t, flags int) (err error) { + // Why would we ever want to call this without AT_SYMLINK_NOFOLLOW? + if flags&unix.AT_SYMLINK_NOFOLLOW == 0 { + tlog.Warn.Printf("Fstatat: adding missing AT_SYMLINK_NOFOLLOW flag") + flags |= unix.AT_SYMLINK_NOFOLLOW + } + return unix.Fstatat(dirfd, path, stat, flags) +}