From 60e7a0ca9f2a2bcf8a727f606db134d60e9a5e18 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Mon, 12 Nov 2018 22:22:10 +0100 Subject: [PATCH] fusefrontend: xattr: fix hang on FIFOs An Open() a fifo blocks until it is opened for writing. This meant that xattr operations on FIFOs would block. Pass O_NONBLOCK to fix that, and add a test. --- internal/fusefrontend/xattr_linux.go | 5 ++- tests/xattr/xattr_integration_test.go | 58 ++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/internal/fusefrontend/xattr_linux.go b/internal/fusefrontend/xattr_linux.go index 915713d..a96a147 100644 --- a/internal/fusefrontend/xattr_linux.go +++ b/internal/fusefrontend/xattr_linux.go @@ -35,11 +35,12 @@ func procFd(fd int) string { // getFileFd calls fs.Open() on relative plaintext path "relPath" and returns // the resulting fusefrontend.*File along with the underlying fd. The caller -// MUST call file.Release() when done with the file. +// MUST call file.Release() when done with the file. The O_NONBLOCK flag is +// used to not block on FIFOs. // // Used by xattrGet() and friends. func (fs *FS) getFileFd(relPath string, context *fuse.Context) (*File, int, fuse.Status) { - fuseFile, status := fs.Open(relPath, syscall.O_RDONLY, context) + fuseFile, status := fs.Open(relPath, syscall.O_RDONLY|syscall.O_NONBLOCK, context) if !status.Ok() { return nil, -1, status } diff --git a/tests/xattr/xattr_integration_test.go b/tests/xattr/xattr_integration_test.go index 8e48399..5c402cc 100644 --- a/tests/xattr/xattr_integration_test.go +++ b/tests/xattr/xattr_integration_test.go @@ -49,37 +49,75 @@ func TestMain(m *testing.M) { os.Exit(r) } -func TestXattrSetGetRm(t *testing.T) { - attr := "user.foo" - fn := test_helpers.DefaultPlainDir + "/TestXattrSetGetRm" - err := ioutil.WriteFile(fn, nil, 0700) +func setGetRmList(fn string) error { + // List + list, err := xattr.LList(fn) if err != nil { - t.Fatalf("creating empty file failed: %v", err) + return err } + if len(list) > 0 { + return fmt.Errorf("Should have gotten empty result, got %v", list) + } + attr := "user.foo" // Set val1 := []byte("123456789") err = xattr.LSet(fn, attr, val1) if err != nil { - t.Fatal(err) + return err } // Read back val2, err := xattr.LGet(fn, attr) if err != nil { - t.Fatal(err) + return err } if !bytes.Equal(val1, val2) { - t.Fatalf("wrong readback value: %v != %v", val1, val2) + return fmt.Errorf("wrong readback value: %v != %v", val1, val2) } // Remove err = xattr.LRemove(fn, attr) if err != nil { - t.Fatal(err) + return err } // Read back val3, err := xattr.LGet(fn, attr) if err == nil { - t.Fatalf("attr is still there after deletion!? val3=%v", val3) + return fmt.Errorf("attr is still there after deletion!? val3=%v", val3) } + // List + list, err = xattr.LList(fn) + if err != nil { + return err + } + if len(list) > 0 { + return fmt.Errorf("Should have gotten empty result, got %v", list) + } + return nil +} + +// Test xattr set, get, rm on a regular file. +func TestSetGetRmRegularFile(t *testing.T) { + fn := test_helpers.DefaultPlainDir + "/TestSetGetRmRegularFile" + err := ioutil.WriteFile(fn, nil, 0700) + if err != nil { + t.Fatalf("creating empty file failed: %v", err) + } + err = setGetRmList(fn) + if err != nil { + t.Error(err) + } +} + +// Test xattr set, get, rm on a fifo. This should not hang. +func TestSetGetRmFifo(t *testing.T) { + fn := test_helpers.DefaultPlainDir + "/TestSetGetRmFifo" + err := syscall.Mkfifo(fn, 0700) + if err != nil { + t.Fatalf("creating fifo failed: %v", err) + } + // We expect to get EPERM, but we should not hang: + // $ setfattr -n user.foo -v XXXXX fifo + // setfattr: fifo: Operation not permitted + setGetRmList(fn) } func TestXattrSetEmpty(t *testing.T) {