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.
This commit is contained in:
Jakob Unterwurzacher 2018-11-12 22:22:10 +01:00
parent 1d5500c3db
commit 60e7a0ca9f
2 changed files with 51 additions and 12 deletions

View File

@ -35,11 +35,12 @@ func procFd(fd int) string {
// getFileFd calls fs.Open() on relative plaintext path "relPath" and returns // getFileFd calls fs.Open() on relative plaintext path "relPath" and returns
// the resulting fusefrontend.*File along with the underlying fd. The caller // 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. // Used by xattrGet() and friends.
func (fs *FS) getFileFd(relPath string, context *fuse.Context) (*File, int, fuse.Status) { 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() { if !status.Ok() {
return nil, -1, status return nil, -1, status
} }

View File

@ -49,37 +49,75 @@ func TestMain(m *testing.M) {
os.Exit(r) os.Exit(r)
} }
func TestXattrSetGetRm(t *testing.T) { func setGetRmList(fn string) error {
attr := "user.foo" // List
fn := test_helpers.DefaultPlainDir + "/TestXattrSetGetRm" list, err := xattr.LList(fn)
err := ioutil.WriteFile(fn, nil, 0700)
if err != nil { 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 // Set
val1 := []byte("123456789") val1 := []byte("123456789")
err = xattr.LSet(fn, attr, val1) err = xattr.LSet(fn, attr, val1)
if err != nil { if err != nil {
t.Fatal(err) return err
} }
// Read back // Read back
val2, err := xattr.LGet(fn, attr) val2, err := xattr.LGet(fn, attr)
if err != nil { if err != nil {
t.Fatal(err) return err
} }
if !bytes.Equal(val1, val2) { 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 // Remove
err = xattr.LRemove(fn, attr) err = xattr.LRemove(fn, attr)
if err != nil { if err != nil {
t.Fatal(err) return err
} }
// Read back // Read back
val3, err := xattr.LGet(fn, attr) val3, err := xattr.LGet(fn, attr)
if err == nil { 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) { func TestXattrSetEmpty(t *testing.T) {