Commit 5f24a9ec authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Implement ListXAttr.

parent 133852b3
......@@ -87,6 +87,8 @@ type SubmountFileSystem struct {
nextFreeInode uint64
Options SubmountFileSystemOptions
fuse.DefaultRawFuseFileSystem
}
type SubmountFileSystemOptions struct {
......
......@@ -73,6 +73,10 @@ func (me *DefaultRawFuseFileSystem) GetXAttr(header *InHeader, attr string) (dat
return nil, ENOSYS
}
func (me *DefaultRawFuseFileSystem) ListXAttr(header *InHeader) (data []byte, code Status) {
return nil, ENOSYS
}
func (me *DefaultRawFuseFileSystem) Access(header *InHeader, input *AccessIn) (code Status) {
return ENOSYS
}
......@@ -153,6 +157,10 @@ func (me *DefaultPathFilesystem) GetXAttr(name string, attr string) ([]byte, Sta
return nil, ENOSYS
}
func (me *DefaultPathFilesystem) ListXAttr(name string) ([]string, Status) {
return nil, ENOSYS
}
func (me *DefaultPathFilesystem) Readlink(name string) (string, Status) {
return "", ENOSYS
}
......
......@@ -475,9 +475,9 @@ func (me *MountState) dispatch(req *fuseRequest) {
// case FUSE_SETXATTR:
// status = fs.SetXAttr(h, (*SetXAttrIn)(inData))
case FUSE_GETXATTR:
req.data, req.flatData, status = doGetXAttr(me, h, (*GetXAttrIn)(inData), filename)
// case FUSE_LISTXATTR:
req.data, req.flatData, status = doGetXAttr(me, h, (*GetXAttrIn)(inData), filename, h.Opcode)
case FUSE_LISTXATTR:
req.data, req.flatData, status = doGetXAttr(me, h, (*GetXAttrIn)(inData), filename, h.Opcode)
// case FUSE_REMOVEXATTR
case FUSE_ACCESS:
......@@ -642,8 +642,12 @@ func doSetattr(state *MountState, header *InHeader, input *SetAttrIn) (out unsaf
return unsafe.Pointer(o), s
}
func doGetXAttr(state *MountState, header *InHeader, input *GetXAttrIn, attr string) (out unsafe.Pointer, data []byte, code Status) {
func doGetXAttr(state *MountState, header *InHeader, input *GetXAttrIn, attr string, opcode uint32) (out unsafe.Pointer, data []byte, code Status) {
if opcode == FUSE_GETXATTR {
data, code = state.fileSystem.GetXAttr(header, attr)
} else {
data, code = state.fileSystem.ListXAttr(header)
}
if code != OK {
return nil, nil, code
}
......
......@@ -152,6 +152,12 @@ func (me *LoopbackFileSystem) GetXAttr(name string, attr string) ([]byte, Status
return data, Status(errNo)
}
func (me *LoopbackFileSystem) ListXAttr(name string) ([]string, Status) {
data, errNo := ListXAttr(me.GetPath(name))
return data, Status(errNo)
}
func (me *LoopbackFileSystem) FillOptions(options *PathFileSystemConnectorOptions) {
options.NegativeTimeout = 3.0
options.AttrTimeout = 3.0
......
......@@ -281,7 +281,7 @@ func init() {
FUSE_FSYNC: unsafe.Sizeof(FsyncIn{}),
FUSE_SETXATTR: unsafe.Sizeof(SetXAttrIn{}),
FUSE_GETXATTR: unsafe.Sizeof(GetXAttrIn{}),
FUSE_LISTXATTR: 0,
FUSE_LISTXATTR: unsafe.Sizeof(GetXAttrIn{}),
FUSE_REMOVEXATTR: 0,
FUSE_FLUSH: unsafe.Sizeof(FlushIn{}),
FUSE_INIT: unsafe.Sizeof(InitIn{}),
......@@ -322,7 +322,7 @@ func init() {
FUSE_FSYNC: 0,
FUSE_SETXATTR: 0,
FUSE_GETXATTR: unsafe.Sizeof(GetXAttrOut{}),
FUSE_LISTXATTR: 0,
FUSE_LISTXATTR: unsafe.Sizeof(GetXAttrOut{}),
FUSE_REMOVEXATTR: 0,
FUSE_FLUSH: 0,
FUSE_INIT: unsafe.Sizeof(InitOut{}),
......
......@@ -728,6 +728,26 @@ func (me *PathFileSystemConnector) GetXAttr(header *InHeader, attribute string)
return data, code
}
func (me *PathFileSystemConnector) ListXAttr(header *InHeader) (data []byte, code Status) {
path, mount := me.GetPath(header.NodeId)
if mount == nil {
return nil, ENOENT
}
attrs, code := mount.fs.ListXAttr(path)
if code != OK {
return nil, code
}
b := bytes.NewBuffer([]byte{})
for _, v := range attrs {
b.Write([]byte(v))
b.WriteByte(0)
}
return b.Bytes(), code
}
////////////////////////////////////////////////////////////////
// unimplemented.
......
......@@ -96,6 +96,11 @@ func (me *TimingPathFilesystem) GetXAttr(name string, attr string) ([]byte, Stat
return me.original.GetXAttr(name, attr)
}
func (me *TimingPathFilesystem) ListXAttr(name string) ([]string, Status) {
defer me.startTimer("ListXAttr", name)()
return me.original.ListXAttr(name)
}
func (me *TimingPathFilesystem) Readlink(name string) (string, Status) {
defer me.startTimer("Readlink", name)()
return me.original.Readlink(name)
......@@ -185,3 +190,4 @@ func (me *TimingPathFilesystem) Utimens(name string, AtimeNs uint64, CtimeNs uin
defer me.startTimer("Utimens", name)()
return me.original.Utimens(name, AtimeNs, CtimeNs)
}
......@@ -131,6 +131,11 @@ func (me *TimingRawFilesystem) GetXAttr(header *InHeader, attr string) (data []b
return me.original.GetXAttr(header, attr)
}
func (me *TimingRawFilesystem) ListXAttr(header *InHeader) (data []byte, code Status) {
defer me.startTimer("ListXAttr")()
return me.original.ListXAttr(header)
}
func (me *TimingRawFilesystem) Access(header *InHeader, input *AccessIn) (code Status) {
defer me.startTimer("Access")()
return me.original.Access(header, input)
......@@ -170,3 +175,4 @@ func (me *TimingRawFilesystem) ReleaseDir(header *InHeader, f RawFuseDir) {
defer me.startTimer("ReleaseDir")()
me.original.ReleaseDir(header, f)
}
......@@ -517,6 +517,7 @@ type RawFileSystem interface {
Link(header *InHeader, input *LinkIn, filename string) (out *EntryOut, code Status)
GetXAttr(header *InHeader, attr string) (data []byte, code Status)
ListXAttr(header *InHeader) (attributes []byte, code Status)
// Unused:
SetXAttr(header *InHeader, input *SetXAttrIn) Status
......@@ -570,7 +571,8 @@ type PathFilesystem interface {
// Where to hook up statfs?
//
// Unimplemented:
// RemoveXAttr, SetXAttr, GetXAttr, ListXAttr.
// RemoveXAttr, SetXAttr,
ListXAttr(name string) (attributes []string, code Status)
OpenDir(name string) (stream chan DirEntry, code Status)
......
......@@ -80,6 +80,13 @@ func (me *WrappingPathFilesystem) Utimens(name string, AtimeNs uint64, CtimeNs u
return me.original.Utimens(name, AtimeNs, CtimeNs)
}
func (me *WrappingPathFilesystem) GetXAttr(name string, attr string) ([]byte, Status) {
return me.original.GetXAttr(name, attr)
}
func (me *WrappingPathFilesystem) ListXAttr(name string) ([]string, Status) {
return me.original.ListXAttr(name)
}
////////////////////////////////////////////////////////////////
// Wrapping raw FS.
......@@ -157,6 +164,10 @@ func (me *WrappingRawFilesystem) GetXAttr(header *InHeader, attr string) (data [
return me.original.GetXAttr(header, attr)
}
func (me *WrappingRawFilesystem) ListXAttr(header *InHeader) (data []byte, code Status) {
return me.original.ListXAttr(header)
}
func (me *WrappingRawFilesystem) Access(header *InHeader, input *AccessIn) (code Status) {
return me.original.Access(header, input)
}
......
......@@ -51,7 +51,7 @@ func listxattr(path string, dest []byte) (sz int, errno int) {
return int(size), int(errNo)
}
func ListXAttr(path string) (attributes [][]byte, errno int) {
func ListXAttr(path string) (attributes []string, errno int) {
dest := make([]byte, 1024)
sz, errno := listxattr(path, dest)
if errno != 0 {
......@@ -65,6 +65,10 @@ func ListXAttr(path string) (attributes [][]byte, errno int) {
// -1 to drop the final empty slice.
dest = dest[:sz-1]
attributes = bytes.Split(dest, []byte{0}, -1)
attributesBytes := bytes.Split(dest, []byte{0}, -1)
attributes = make([]string, len(attributesBytes))
for i, v := range attributesBytes {
attributes[i] = string(v)
}
return attributes, errno
}
package fuse
import (
"bytes"
"testing"
"path/filepath"
"os"
......@@ -33,6 +35,7 @@ func (me *XAttrTestFs) GetAttr(name string) (*Attr, Status) {
return nil, ENOENT
}
func (me *XAttrTestFs) GetXAttr(name string, attr string) ([]byte, Status) {
if name != me.filename {
return nil, ENOENT
......@@ -44,19 +47,31 @@ func (me *XAttrTestFs) GetXAttr(name string, attr string) ([]byte, Status) {
return v, OK
}
func (me *XAttrTestFs) ListXAttr(name string) (data []string, code Status) {
if name != me.filename {
return nil, ENOENT
}
for k, _ := range me.attrs {
data = append(data, k)
}
return data, OK
}
func TestXAttr(t *testing.T) {
func TestXAttrRead(t *testing.T) {
nm := "filename"
xfs := NewXAttrFs(nm,
map[string][]byte{
golden := map[string][]byte{
"user.attr1": []byte("val1"),
"user.attr2": []byte("val2")})
"user.attr2": []byte("val2")}
xfs := NewXAttrFs(nm, golden)
connector := NewPathFileSystemConnector(xfs)
mountPoint := MakeTempDir()
state := NewMountState(connector)
state.Mount(mountPoint)
state.Debug = true
defer state.Unmount()
go state.Loop(false)
......@@ -69,16 +84,32 @@ func TestXAttr(t *testing.T) {
val, errno := GetXAttr(mounted, "noexist")
if errno == 0 {
t.Error("Expected GetXAttr error")
t.Error("Expected GetXAttr error", val)
}
val, errno = GetXAttr(mounted, "user.attr1")
if err != nil {
attrs, errno := ListXAttr(mounted)
readback := make(map[string][]byte)
if errno != 0 {
t.Error("Unexpected ListXAttr error", errno)
} else {
for _, a := range attrs {
val, errno = GetXAttr(mounted, a)
if errno != 0 {
t.Error("Unexpected GetXAttr error", errno)
}
readback[a] = val
}
}
if string(val) != "val1" {
t.Error("Unexpected value", val)
if len(readback) != len(golden) {
t.Error("length mismatch", golden, readback)
} else {
for k, v := range(readback) {
if bytes.Compare(golden[k], v) != 0 {
t.Error("val mismatch", k, v, golden[k])
}
}
}
}
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