From f30522a0c14218ad236831d1c229968ea46ec8aa Mon Sep 17 00:00:00 2001 From: Sebastian Lackner Date: Thu, 30 Nov 2017 21:13:46 +0100 Subject: [PATCH] syscallcompat: Fix syscall emulation for absolute paths For absolute paths, the file descriptor should be ignored. In such a case there is also no need to hold the lock or change the working directory. --- internal/syscallcompat/emulate.go | 120 +++++++++++++------------ internal/syscallcompat/emulate_test.go | 76 ++++++++++++++++ 2 files changed, 141 insertions(+), 55 deletions(-) diff --git a/internal/syscallcompat/emulate.go b/internal/syscallcompat/emulate.go index 3af45f8..0aca28e 100644 --- a/internal/syscallcompat/emulate.go +++ b/internal/syscallcompat/emulate.go @@ -64,18 +64,20 @@ func emulateRenameat(olddirfd int, oldpath string, newdirfd int, newpath string) // emulateUnlinkat emulates the syscall for platforms that don't have it // in the kernel (darwin). func emulateUnlinkat(dirfd int, path string, flags int) (err error) { - chdirMutex.Lock() - defer chdirMutex.Unlock() - cwd, err := syscall.Open(".", syscall.O_RDONLY, 0) - if err != nil { - return err + 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) } - defer syscall.Close(cwd) - err = syscall.Fchdir(dirfd) - if err != nil { - return err - } - defer syscall.Fchdir(cwd) if (flags & unix.AT_REMOVEDIR) != 0 { return syscall.Rmdir(path) } else { @@ -123,18 +125,20 @@ func dirfdAbs(dirfd int, path string) (string, error) { // emulateFchmodat emulates the syscall for platforms that don't have it // in the kernel (darwin). func emulateFchmodat(dirfd int, path string, mode uint32, flags int) (err error) { - chdirMutex.Lock() - defer chdirMutex.Unlock() - cwd, err := syscall.Open(".", syscall.O_RDONLY, 0) - if err != nil { - return err + 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) } - defer syscall.Close(cwd) - err = syscall.Fchdir(dirfd) - if err != nil { - return err - } - defer syscall.Fchdir(cwd) // We also don't have Lchmod, so emulate it (poorly). if flags&unix.AT_SYMLINK_NOFOLLOW != 0 { fi, err := os.Lstat(path) @@ -151,53 +155,59 @@ func emulateFchmodat(dirfd int, path string, mode uint32, flags int) (err error) // emulateFchownat emulates the syscall for platforms that don't have it // in the kernel (darwin). func emulateFchownat(dirfd int, path string, uid int, gid int, flags int) (err error) { - chdirMutex.Lock() - defer chdirMutex.Unlock() - cwd, err := syscall.Open(".", syscall.O_RDONLY, 0) - if err != nil { - return err + 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) } - defer syscall.Close(cwd) - err = syscall.Fchdir(dirfd) - if err != nil { - return err - } - defer syscall.Fchdir(cwd) return syscall.Lchown(path, uid, gid) } // emulateSymlinkat emulates the syscall for platforms that don't have it // in the kernel (darwin). func emulateSymlinkat(oldpath string, newdirfd int, newpath string) (err error) { - chdirMutex.Lock() - defer chdirMutex.Unlock() - cwd, err := syscall.Open(".", syscall.O_RDONLY, 0) - if err != nil { - return err + if !filepath.IsAbs(newpath) { + 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(newdirfd) + if err != nil { + return err + } + defer syscall.Fchdir(cwd) } - defer syscall.Close(cwd) - err = syscall.Fchdir(newdirfd) - if err != nil { - return err - } - defer syscall.Fchdir(cwd) return syscall.Symlink(oldpath, newpath) } // emulateMkdirat emulates the syscall for platforms that don't have it // in the kernel (darwin). func emulateMkdirat(dirfd int, path string, mode uint32) (err error) { - chdirMutex.Lock() - defer chdirMutex.Unlock() - cwd, err := syscall.Open(".", syscall.O_RDONLY, 0) - if err != nil { - return err + 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) } - defer syscall.Close(cwd) - err = syscall.Fchdir(dirfd) - if err != nil { - return err - } - defer syscall.Fchdir(cwd) return syscall.Mkdir(path, mode) } diff --git a/internal/syscallcompat/emulate_test.go b/internal/syscallcompat/emulate_test.go index 35b2b40..0753a45 100644 --- a/internal/syscallcompat/emulate_test.go +++ b/internal/syscallcompat/emulate_test.go @@ -67,6 +67,15 @@ func TestEmulateOpenat(t *testing.T) { if rawFd < 0 { t.Fatalf("rawFd=%d", rawFd) } + // Test with absolute path + rawFd, err = emulateOpenat(-1, tmpDir + "/testOpenAt", 0, 0) + if err != nil { + t.Fatal(err) + } + defer syscall.Close(rawFd) + if rawFd < 0 { + t.Fatalf("rawFd=%d", rawFd) + } } func TestEmulateRenameat(t *testing.T) { @@ -95,6 +104,15 @@ func TestEmulateRenameat(t *testing.T) { if err != nil { t.Fatal(err) } + // Test with absolute path + err = emulateRenameat(-1, tmpDir + "/dir2/f2", -1, tmpDir + "/dir2/f1") + if err != nil { + t.Fatal(err) + } + _, err = os.Stat(tmpDir + "/dir2/f1") + if err != nil { + t.Fatal(err) + } } func TestEmulateUnlinkat(t *testing.T) { @@ -135,6 +153,19 @@ func TestEmulateUnlinkat(t *testing.T) { if err == nil { t.Fatalf("dir not deleted!") } + // Test with absolute path + err = os.Mkdir(tmpDir + "/unlink1/d1", 0700) + if err != nil { + t.Fatal(err) + } + err = emulateUnlinkat(-1, tmpDir + "/unlink1/d1", unix.AT_REMOVEDIR) + if err != nil { + t.Fatal(err) + } + _, err = os.Stat(tmpDir + "/unlink1/d1") + if err == nil { + t.Fatalf("dir not deleted!") + } } func TestEmulateMknodat(t *testing.T) { @@ -146,6 +177,15 @@ func TestEmulateMknodat(t *testing.T) { if err != nil { t.Fatal(err) } + // Test with absolute path + err = emulateMknodat(-1, tmpDir + "/fifo2", unix.S_IFIFO, 0) + if err != nil { + t.Fatal(err) + } + _, err = os.Stat(tmpDir + "/fifo2") + if err != nil { + t.Fatal(err) + } } func TestEmulateFchmodat(t *testing.T) { @@ -187,6 +227,18 @@ func TestEmulateFchmodat(t *testing.T) { if st.Mode != 0100600 { t.Fatalf("Wrong mode: have %o, want %o", st.Mode, 0100600) } + // Test with absolute path + err = emulateFchmodat(-1, tmpDir + "/chmod", 0400, 0) + if err != nil { + t.Fatal(err) + } + err = syscall.Lstat(tmpDir + "/chmod", &st) + if err != nil { + t.Fatal(err) + } + if st.Mode != 0100400 { + t.Fatalf("Wrong mode: have %o, want %o", st.Mode, 0100400) + } } func TestEmulateFchownat(t *testing.T) { @@ -206,6 +258,18 @@ func TestEmulateSymlinkat(t *testing.T) { if st.Mode != 0120777 { t.Fatalf("Wrong mode, have %o, want 0120777", st.Mode) } + // Test with absolute path + err = emulateSymlinkat("/foo/bar/baz", -1, tmpDir + "/symlink2") + if err != nil { + t.Fatal(err) + } + err = syscall.Lstat(tmpDir + "/symlink2", &st) + if err != nil { + t.Fatal(err) + } + if st.Mode != 0120777 { + t.Fatalf("Wrong mode, have %o, want 0120777", st.Mode) + } } func TestEmulateMkdirat(t *testing.T) { @@ -220,4 +284,16 @@ func TestEmulateMkdirat(t *testing.T) { if !fi.IsDir() { t.Fatalf("mkdirat did not create a directory") } + // Test with absolute path + err = emulateMkdirat(-1, tmpDir + "/mkdirat2", 0100) + if err != nil { + t.Fatal(err) + } + fi, err = os.Stat(tmpDir + "/mkdirat2") + if err != nil { + t.Fatal(err) + } + if !fi.IsDir() { + t.Fatalf("mkdirat did not create a directory") + } }