Commit e7bcad20 authored by Ka-Hing Cheung's avatar Ka-Hing Cheung Committed by Michael Stapelberg

fix getxattr and listxattr (#72)

previously, this will fail if /mnt/file doesn't have an xattr:

```
listxattr("/mnt/file", 0x7fe8b3686830, 256) = -1 EIO (Input/output error)
```

We should be returning the actual size only if the input size is
zero. Related issue is if the filesystem returns ERANGE, we should
propagate that error instead of returning the actual size.

Replaced go-xattr usage with x/sys/unix so we can test this.
parent 4ee1cf7f
...@@ -603,17 +603,6 @@ func (c *Connection) kernelResponse( ...@@ -603,17 +603,6 @@ func (c *Connection) kernelResponse(
if opErr != nil { if opErr != nil {
handled := false handled := false
if opErr == syscall.ERANGE {
switch o := op.(type) {
case *fuseops.GetXattrOp:
writeXattrSize(m, uint32(o.BytesRead))
handled = true
case *fuseops.ListXattrOp:
writeXattrSize(m, uint32(o.BytesRead))
handled = true
}
}
if !handled { if !handled {
m.OutHeader().Error = -int32(syscall.EIO) m.OutHeader().Error = -int32(syscall.EIO)
if errno, ok := opErr.(syscall.Errno); ok { if errno, ok := opErr.(syscall.Errno); ok {
...@@ -792,14 +781,14 @@ func (c *Connection) kernelResponseForOp( ...@@ -792,14 +781,14 @@ func (c *Connection) kernelResponseForOp(
// convertInMessage already set up the destination buffer to be at the end // convertInMessage already set up the destination buffer to be at the end
// of the out message. We need only shrink to the right size based on how // of the out message. We need only shrink to the right size based on how
// much the user read. // much the user read.
if o.BytesRead == 0 { if len(o.Dst) == 0 {
writeXattrSize(m, uint32(o.BytesRead)) writeXattrSize(m, uint32(o.BytesRead))
} else { } else {
m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead) m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead)
} }
case *fuseops.ListXattrOp: case *fuseops.ListXattrOp:
if o.BytesRead == 0 { if len(o.Dst) == 0 {
writeXattrSize(m, uint32(o.BytesRead)) writeXattrSize(m, uint32(o.BytesRead))
} else { } else {
m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead) m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead)
......
...@@ -26,6 +26,7 @@ import ( ...@@ -26,6 +26,7 @@ import (
"github.com/jacobsa/fuse/fuseops" "github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/fuseutil" "github.com/jacobsa/fuse/fuseutil"
"github.com/jacobsa/syncutil" "github.com/jacobsa/syncutil"
"golang.org/x/sys/unix"
) )
type memFS struct { type memFS struct {
...@@ -679,7 +680,7 @@ func (fs *memFS) GetXattr(ctx context.Context, ...@@ -679,7 +680,7 @@ func (fs *memFS) GetXattr(ctx context.Context,
op.BytesRead = len(value) op.BytesRead = len(value)
if len(op.Dst) >= len(value) { if len(op.Dst) >= len(value) {
copy(op.Dst, value) copy(op.Dst, value)
} else { } else if len(op.Dst) != 0 {
err = syscall.ERANGE err = syscall.ERANGE
} }
} else { } else {
...@@ -703,8 +704,8 @@ func (fs *memFS) ListXattr(ctx context.Context, ...@@ -703,8 +704,8 @@ func (fs *memFS) ListXattr(ctx context.Context,
if err == nil && len(dst) >= keyLen { if err == nil && len(dst) >= keyLen {
copy(dst, key) copy(dst, key)
dst = dst[keyLen:] dst = dst[keyLen:]
} else { } else if len(op.Dst) != 0 {
err = syscall.ERANGE return syscall.ERANGE
} }
op.BytesRead += keyLen op.BytesRead += keyLen
} }
...@@ -735,11 +736,11 @@ func (fs *memFS) SetXattr(ctx context.Context, ...@@ -735,11 +736,11 @@ func (fs *memFS) SetXattr(ctx context.Context,
_, ok := inode.xattrs[op.Name] _, ok := inode.xattrs[op.Name]
switch op.Flags { switch op.Flags {
case 0x1: case unix.XATTR_CREATE:
if ok { if ok {
err = fuse.EEXIST err = fuse.EEXIST
} }
case 0x2: case unix.XATTR_REPLACE:
if !ok { if !ok {
err = fuse.ENOATTR err = fuse.ENOATTR
} }
......
...@@ -35,7 +35,7 @@ import ( ...@@ -35,7 +35,7 @@ import (
"github.com/jacobsa/fuse/samples/memfs" "github.com/jacobsa/fuse/samples/memfs"
. "github.com/jacobsa/oglematchers" . "github.com/jacobsa/oglematchers"
. "github.com/jacobsa/ogletest" . "github.com/jacobsa/ogletest"
"github.com/kahing/go-xattr" "golang.org/x/sys/unix"
) )
func TestMemFS(t *testing.T) { RunTests(t) } func TestMemFS(t *testing.T) { RunTests(t) }
...@@ -1792,6 +1792,8 @@ func (t *MemFSTest) RenameNonExistentFile() { ...@@ -1792,6 +1792,8 @@ func (t *MemFSTest) RenameNonExistentFile() {
func (t *MemFSTest) NoXattrs() { func (t *MemFSTest) NoXattrs() {
var err error var err error
var sz int
var smallBuf [1]byte
// Create a file. // Create a file.
filePath := path.Join(t.Dir, "foo") filePath := path.Join(t.Dir, "foo")
...@@ -1799,53 +1801,69 @@ func (t *MemFSTest) NoXattrs() { ...@@ -1799,53 +1801,69 @@ func (t *MemFSTest) NoXattrs() {
AssertEq(nil, err) AssertEq(nil, err)
// List xattr names. // List xattr names.
names, err := xattr.List(filePath) sz, err = unix.Listxattr(filePath, nil)
AssertEq(nil, err) AssertEq(nil, err)
ExpectThat(names, ElementsAre()) AssertEq(0, sz)
// Attempt to read a non-existent xattr. // Attempt to read a non-existent xattr.
_, err = xattr.Getxattr(filePath, "foo", nil) _, err = unix.Getxattr(filePath, "foo", nil)
ExpectEq(fuse.ENOATTR, err)
// Attempt to read a non-existent xattr with a buf.
_, err = unix.Getxattr(filePath, "foo", smallBuf[:])
ExpectEq(fuse.ENOATTR, err) ExpectEq(fuse.ENOATTR, err)
// List xattr names with a buf.
sz, err = unix.Listxattr(filePath, smallBuf[:])
AssertEq(nil, err)
ExpectEq(0, sz)
} }
func (t *MemFSTest) SetXAttr() { func (t *MemFSTest) SetXAttr() {
var err error var err error
var sz int
var buf [1024]byte
// Create a file. // Create a file.
filePath := path.Join(t.Dir, "foo") filePath := path.Join(t.Dir, "foo")
err = ioutil.WriteFile(filePath, []byte("taco"), 0600) err = ioutil.WriteFile(filePath, []byte("taco"), 0600)
AssertEq(nil, err) AssertEq(nil, err)
err = xattr.Setxattr(filePath, "foo", []byte("bar"), xattr.REPLACE) err = unix.Setxattr(filePath, "foo", []byte("bar"), unix.XATTR_REPLACE)
AssertEq(fuse.ENOATTR, err) AssertEq(fuse.ENOATTR, err)
err = xattr.Setxattr(filePath, "foo", []byte("bar"), xattr.CREATE) err = unix.Setxattr(filePath, "foo", []byte("bar"), unix.XATTR_CREATE)
AssertEq(nil, err) AssertEq(nil, err)
value, err := xattr.Get(filePath, "foo") // List xattr with a buf that is too small.
AssertEq(nil, err) _, err = unix.Listxattr(filePath, buf[:1])
AssertEq("bar", string(value)) ExpectEq(unix.ERANGE, err)
err = xattr.Setxattr(filePath, "foo", []byte("hello world"), xattr.REPLACE) // List xattr to ask for name size.
sz, err = unix.Listxattr(filePath, nil)
AssertEq(nil, err) AssertEq(nil, err)
AssertEq(4, sz)
value, err = xattr.Get(filePath, "foo") // List xattr names.
sz, err = unix.Listxattr(filePath, buf[:sz])
AssertEq(nil, err) AssertEq(nil, err)
AssertEq("hello world", string(value)) AssertEq(4, sz)
AssertEq("foo\000", string(buf[:sz]))
names, err := xattr.List(filePath) // Read xattr with a buf that is too small.
AssertEq(nil, err) _, err = unix.Getxattr(filePath, "foo", buf[:1])
AssertEq(1, len(names)) ExpectEq(unix.ERANGE, err)
AssertEq("foo", names[0])
err = xattr.Setxattr(filePath, "bar", []byte("hello world"), 0x0) // Read xattr to ask for value size.
sz, err = unix.Getxattr(filePath, "foo", nil)
AssertEq(nil, err) AssertEq(nil, err)
AssertEq(3, sz)
names, err = xattr.List(filePath) // Read xattr value.
sz, err = unix.Getxattr(filePath, "foo", buf[:sz])
AssertEq(nil, err) AssertEq(nil, err)
AssertEq(2, len(names)) AssertEq(3, sz)
ExpectThat(names, Contains("foo")) AssertEq("bar", string(buf[:sz]))
ExpectThat(names, Contains("bar"))
} }
func (t *MemFSTest) RemoveXAttr() { func (t *MemFSTest) RemoveXAttr() {
...@@ -1856,16 +1874,16 @@ func (t *MemFSTest) RemoveXAttr() { ...@@ -1856,16 +1874,16 @@ func (t *MemFSTest) RemoveXAttr() {
err = ioutil.WriteFile(filePath, []byte("taco"), 0600) err = ioutil.WriteFile(filePath, []byte("taco"), 0600)
AssertEq(nil, err) AssertEq(nil, err)
err = xattr.Removexattr(filePath, "foo") err = unix.Removexattr(filePath, "foo")
AssertEq(fuse.ENOATTR, err) AssertEq(fuse.ENOATTR, err)
err = xattr.Setxattr(filePath, "foo", []byte("bar"), xattr.CREATE) err = unix.Setxattr(filePath, "foo", []byte("bar"), unix.XATTR_CREATE)
AssertEq(nil, err) AssertEq(nil, err)
err = xattr.Removexattr(filePath, "foo") err = unix.Removexattr(filePath, "foo")
AssertEq(nil, err) AssertEq(nil, err)
_, err = xattr.Getxattr(filePath, "foo", nil) _, err = unix.Getxattr(filePath, "foo", nil)
AssertEq(fuse.ENOATTR, err) AssertEq(fuse.ENOATTR, err)
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment