From 93ee36a39859193a4af34194b3a7b196a808d995 Mon Sep 17 00:00:00 2001 From: greatroar <61184462+greatroar@users.noreply.github.com> Date: Sun, 6 Nov 2022 13:57:37 +0100 Subject: [PATCH 1/3] Fix FIFO handling on Solaris On Solaris, files need to be opened to obtain or change their attributes. But opening a FIFO file causes the calling thread to block until the write end is opened. Files are now opened using O_NONBLOCK to prevent this from happening. Should fix restic/restic#4003. Also fix comment on stringsFromByteSlice. --- xattr_solaris.go | 8 ++++---- xattr_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/xattr_solaris.go b/xattr_solaris.go index 8d65b8d..af00627 100644 --- a/xattr_solaris.go +++ b/xattr_solaris.go @@ -24,7 +24,7 @@ const ( ) func getxattr(path string, name string, data []byte) (int, error) { - f, err := os.OpenFile(path, os.O_RDONLY, 0) + f, err := os.OpenFile(path, os.O_RDONLY|unix.O_NONBLOCK, 0) if err != nil { return 0, err } @@ -50,7 +50,7 @@ func fgetxattr(f *os.File, name string, data []byte) (int, error) { } func setxattr(path string, name string, data []byte, flags int) error { - f, err := os.OpenFile(path, os.O_RDONLY, 0) + f, err := os.OpenFile(path, os.O_RDONLY|unix.O_NONBLOCK, 0) if err != nil { return err } @@ -114,7 +114,7 @@ func fremovexattr(f *os.File, name string) error { } func listxattr(path string, data []byte) (int, error) { - f, err := os.OpenFile(path, os.O_RDONLY, 0) + f, err := os.OpenFile(path, os.O_RDONLY|unix.O_NONBLOCK, 0) if err != nil { return 0, err } @@ -152,7 +152,7 @@ func flistxattr(f *os.File, data []byte) (int, error) { } // stringsFromByteSlice converts a sequence of attributes to a []string. -// On Darwin and Linux, each entry is a NULL-terminated string. +// We simulate Linux/Darwin, where each entry is a NULL-terminated string. func stringsFromByteSlice(buf []byte) (result []string) { offset := 0 for index, b := range buf { diff --git a/xattr_test.go b/xattr_test.go index b6b2048..2088288 100644 --- a/xattr_test.go +++ b/xattr_test.go @@ -8,9 +8,12 @@ import ( "io/ioutil" "log" "os" + "path/filepath" "runtime" "syscall" "testing" + + "golang.org/x/sys/unix" ) const UserPrefix = "user." @@ -260,6 +263,30 @@ func TestLargeVal(t *testing.T) { } } +// Get should work on a FIFO that is not opened by any other process. +// This is mainly relevant on Solaris, where getting the attributes +// requires opening the file and doing that the wrong way blocks the caller. +func TestFIFO(t *testing.T) { + d, err := ioutil.TempDir("", "pkg-xattr-testfifo-*") + if err != nil { + t.Fatal(err) + } + t.Log("tempdir:", d) + defer os.RemoveAll(d) + + fifo := filepath.Join(d, "fifo") + err = unix.Mkfifo(fifo, 0777) + if err != nil { + t.Fatal(err) + } + + // We only care about this not blocking. + _ = Set(fifo, "foo", []byte("bar")) + _, _ = Get(fifo, "foo") + _, _ = List(fifo) + _ = Remove(fifo, "foo") +} + // checkIfError calls t.Skip() if the underlying syscall.Errno is // ENOTSUP or EOPNOTSUPP. It calls t.Fatal() on any other non-zero error. func checkIfError(t *testing.T, err error) { From fc35f5d6ae520c9e7972638db9af4ce1875f091f Mon Sep 17 00:00:00 2001 From: greatroar <61184462+greatroar@users.noreply.github.com> Date: Wed, 9 Nov 2022 18:43:14 +0100 Subject: [PATCH 2/3] (Solaris) Open O_NONBLOCK with unix.Open, not os.Open Passing this flag to os.Open works, but this relies on undocumented implementation details of package os. --- xattr_solaris.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/xattr_solaris.go b/xattr_solaris.go index af00627..57afaee 100644 --- a/xattr_solaris.go +++ b/xattr_solaris.go @@ -24,7 +24,7 @@ const ( ) func getxattr(path string, name string, data []byte) (int, error) { - f, err := os.OpenFile(path, os.O_RDONLY|unix.O_NONBLOCK, 0) + f, err := openNonblock(path) if err != nil { return 0, err } @@ -50,7 +50,7 @@ func fgetxattr(f *os.File, name string, data []byte) (int, error) { } func setxattr(path string, name string, data []byte, flags int) error { - f, err := os.OpenFile(path, os.O_RDONLY|unix.O_NONBLOCK, 0) + f, err := openNonblock(path) if err != nil { return err } @@ -114,7 +114,7 @@ func fremovexattr(f *os.File, name string) error { } func listxattr(path string, data []byte) (int, error) { - f, err := os.OpenFile(path, os.O_RDONLY|unix.O_NONBLOCK, 0) + f, err := openNonblock(path) if err != nil { return 0, err } @@ -151,6 +151,15 @@ func flistxattr(f *os.File, data []byte) (int, error) { return copy(data, buf), nil } +// Like os.Open, but passes O_NONBLOCK to the open(2) syscall. +func openNonblock(path string) (*os.File, error) { + fd, err := unix.Open(path, unix.O_RDONLY|unix.O_CLOEXEC|unix.O_NONBLOCK, 0) + if err != nil { + return nil, err + } + return os.NewFile(uintptr(fd), path), err +} + // stringsFromByteSlice converts a sequence of attributes to a []string. // We simulate Linux/Darwin, where each entry is a NULL-terminated string. func stringsFromByteSlice(buf []byte) (result []string) { From a146e14b3c90fe465b594da12283777692b0e5b5 Mon Sep 17 00:00:00 2001 From: greatroar <61184462+greatroar@users.noreply.github.com> Date: Fri, 11 Nov 2022 22:42:31 +0100 Subject: [PATCH 3/3] (Solaris) Fix removexattr/hanging test --- xattr_solaris.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xattr_solaris.go b/xattr_solaris.go index 57afaee..7c98b4a 100644 --- a/xattr_solaris.go +++ b/xattr_solaris.go @@ -87,7 +87,8 @@ func fsetxattr(f *os.File, name string, data []byte, flags int) error { } func removexattr(path string, name string) error { - fd, err := unix.Open(path, unix.O_RDONLY|unix.O_XATTR, 0) + mode := unix.O_RDONLY | unix.O_XATTR | unix.O_NONBLOCK | unix.O_CLOEXEC + fd, err := unix.Open(path, mode, 0) if err != nil { return err }