Commit 962c5624 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

nodefs: XAttr support

parent 0f169dda
......@@ -107,6 +107,13 @@ type Operations interface {
SetLk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) (status fuse.Status)
SetLkw(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) (status fuse.Status)
// Extended attributes
GetXAttr(ctx context.Context, attr string, dest []byte) (uint32, fuse.Status)
SetXAttr(ctx context.Context, attr string, data []byte, flags uint32) fuse.Status
RemoveXAttr(ctx context.Context, attr string) fuse.Status
ListXAttr(ctx context.Context, dest []byte) (uint32, fuse.Status)
// The methods below may be called on closed files, due to
// concurrency. In that case, you should return EBADF.
GetAttr(ctx context.Context, f FileHandle, out *fuse.AttrOut) fuse.Status
......
......@@ -412,24 +412,26 @@ func (b *rawBridge) Access(cancel <-chan struct{}, input *fuse.AccessIn) (status
}
// Extended attributes.
func (b *rawBridge) GetXAttrSize(cancel <-chan struct{}, header *fuse.InHeader, attr string) (sz int, status fuse.Status) {
return 0, fuse.ENOSYS
}
func (b *rawBridge) GetXAttrData(cancel <-chan struct{}, header *fuse.InHeader, attr string) (data []byte, status fuse.Status) {
return nil, fuse.ENOSYS
func (b *rawBridge) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string, data []byte) (uint32, fuse.Status) {
n, _ := b.inode(header.NodeId, 0)
return n.node.GetXAttr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr, data)
}
func (b *rawBridge) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader) (attributes []byte, status fuse.Status) {
return nil, fuse.ENOSYS
func (b *rawBridge) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader, dest []byte) (sz uint32, status fuse.Status) {
n, _ := b.inode(header.NodeId, 0)
return n.node.ListXAttr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, dest)
}
func (b *rawBridge) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, attr string, data []byte) fuse.Status {
return fuse.ENOSYS
n, _ := b.inode(input.NodeId, 0)
return n.node.SetXAttr(&fuse.Context{Caller: input.Caller, Cancel: cancel}, attr, data, input.Flags)
}
func (b *rawBridge) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string) (status fuse.Status) {
return
n, _ := b.inode(header.NodeId, 0)
return n.node.RemoveXAttr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr)
}
func (b *rawBridge) Open(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) (status fuse.Status) {
......
......@@ -215,6 +215,22 @@ func (n *DefaultOperations) Link(ctx context.Context, target Operations, name st
return nil, fuse.ENOSYS
}
func (n *DefaultOperations) GetXAttr(ctx context.Context, attr string, dest []byte) (uint32, fuse.Status) {
return 0, fuse.ENOATTR
}
func (n *DefaultOperations) SetXAttr(ctx context.Context, attr string, data []byte, flags uint32) fuse.Status {
return fuse.ENOATTR
}
func (n *DefaultOperations) RemoveXAttr(ctx context.Context, attr string) fuse.Status {
return fuse.ENOATTR
}
func (n *DefaultOperations) ListXAttr(ctx context.Context, dest []byte) (uint32, fuse.Status) {
return 0, fuse.OK
}
type DefaultFile struct {
}
......
......@@ -319,3 +319,23 @@ func NewLoopback(root string) Operations {
n.openFiles = map[*loopbackFile]uint32{}
return n
}
func (n *loopbackNode) GetXAttr(ctx context.Context, attr string, dest []byte) (uint32, fuse.Status) {
sz, err := syscall.Getxattr(n.path(), attr, dest)
return uint32(sz), fuse.ToStatus(err)
}
func (n *loopbackNode) SetXAttr(ctx context.Context, attr string, data []byte, flags uint32) fuse.Status {
err := syscall.Setxattr(n.path(), attr, data, int(flags))
return fuse.ToStatus(err)
}
func (n *loopbackNode) RemoveXAttr(ctx context.Context, attr string) fuse.Status {
err := syscall.Removexattr(n.path(), attr)
return fuse.ToStatus(err)
}
func (n *loopbackNode) ListXAttr(ctx context.Context, dest []byte) (uint32, fuse.Status) {
sz, err := syscall.Listxattr(n.path(), dest)
return uint32(sz), fuse.ToStatus(err)
}
......@@ -58,7 +58,6 @@ func newTestCase(t *testing.T) *testCase {
dir: testutil.TempDir(),
T: t,
}
tc.origDir = tc.dir + "/orig"
tc.mntDir = tc.dir + "/mnt"
if err := os.Mkdir(tc.origDir, 0755); err != nil {
......@@ -563,3 +562,38 @@ func TestStatFs(t *testing.T) {
t.Errorf("Got %#v, want %#v", mntFuse, origFuse)
}
}
func TestXAttr(t *testing.T) {
tc := newTestCase(t)
defer tc.Clean()
tc.writeOrig("file", "", 0644)
buf := make([]byte, 1024)
attr := "user.xattrtest"
if _, err := syscall.Getxattr(tc.mntDir+"/file", attr, buf); err == syscall.ENOTSUP {
t.Skip("$TMP does not support xattrs. Rerun this test with a $TMPDIR override")
}
if _, err := syscall.Getxattr(tc.mntDir+"/file", attr, buf); err != syscall.ENODATA {
t.Fatalf("got %v want ENOATTR", err)
}
value := []byte("value")
if err := syscall.Setxattr(tc.mntDir+"/file", attr, value, 0); err != nil {
t.Fatalf("Setxattr: %v", err)
}
sz, err := syscall.Getxattr(tc.mntDir+"/file", attr, buf)
if err != nil {
t.Fatalf("Getxattr: %v", err)
}
if bytes.Compare(buf[:sz], value) != 0 {
t.Fatalf("Getxattr got %q want %q", buf[:sz], value)
}
if err := syscall.Removexattr(tc.mntDir+"/file", attr); err != nil {
t.Fatalf("Removexattr: %v", err)
}
if _, err := syscall.Getxattr(tc.mntDir+"/file", attr, buf); err != syscall.ENODATA {
t.Fatalf("got %v want 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