Commit d650fe34 authored by Ka-Hing Cheung's avatar Ka-Hing Cheung

added tests and enhanced comments

parent cbeaa550
......@@ -475,9 +475,10 @@ func convertInMessage(
return
}
o = &fuseops.ListXattrOp{
to := &fuseops.ListXattrOp{
Inode: fuseops.InodeID(inMsg.Header().Nodeid),
}
o = to
readSize := int(in.Size)
if readSize != 0 {
......@@ -486,6 +487,10 @@ func convertInMessage(
err = fmt.Errorf("Can't grow for %d-byte read", readSize)
return
}
sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst))
sh.Data = uintptr(p)
sh.Len = readSize
sh.Cap = readSize
}
case fusekernel.OpSetxattr:
type input fusekernel.SetxattrIn
......@@ -508,12 +513,11 @@ func convertInMessage(
}
name, value := payload[:i], payload[i+1:len(payload)]
fmt.Printf("Setting %v to %v\n", name, value)
o = &fuseops.SetXattrOp{
Inode: fuseops.InodeID(inMsg.Header().Nodeid),
Name: string(name),
Data: value,
Value: value,
Flags: in.Flags,
}
......
......@@ -22,6 +22,7 @@ const (
EEXIST = syscall.EEXIST
EINVAL = syscall.EINVAL
EIO = syscall.EIO
ENOATTR = syscall.ENODATA
ENOENT = syscall.ENOENT
ENOSYS = syscall.ENOSYS
ENOTDIR = syscall.ENOTDIR
......
......@@ -772,21 +772,27 @@ type ReadSymlinkOp struct {
// eXtended attributes
////////////////////////////////////////////////////////////////////////
// Remove an extended attribute
// Remove an extended attribute.
//
// This is sent in response to removexattr(2). Return ENOATTR if the
// extended attribute does not exist.
type RemoveXattrOp struct {
// The inode that we are reading
// The inode that we are removing an extended attribute from.
Inode InodeID
// The name of the extended attribute
// The name of the extended attribute.
Name string
}
// Get an extended attribute
// Get an extended attribute.
//
// This is sent in response to getxattr(2). Return ENOATTR if the
// extended attribute does not exist.
type GetXattrOp struct {
// The inode that we are reading
// The inode whose extended attribute we are reading.
Inode InodeID
// The name of the extended attribute
// The name of the extended attribute.
Name string
// The destination buffer. If the size is too small for the
......@@ -795,38 +801,47 @@ type GetXattrOp struct {
// Set by the file system: the number of bytes read into Dst, or
// the number of bytes that would have been read into Dst if Dst was
// big enough
// big enough (return ERANGE in this case).
BytesRead int
}
// List all the extended attributes for a file.
//
// This is sent in response to listxattr(2).
type ListXattrOp struct {
// The inode that we are reading
// The inode whose extended attributes we are listing.
Inode InodeID
// The destination buffer. If the size is too small for the
// value, the ERANGE error should be sent.
//
// The output data should consist of a sequence of NUL-terminated strings,
// one for each xattr
// one for each xattr.
Dst []byte
// Set by the file system: the number of bytes read into Dst, or
// the number of bytes that would have been read into Dst if Dst was
// big enough
// big enough (return ERANGE in this case).
BytesRead int
}
// Set an extended attribute.
//
// This is sent in response to setxattr(2). Return ENOSPC if there is
// insufficient space remaining to store the extended attribute.
type SetXattrOp struct {
// The inode that we are changing
// The inode whose extended attribute we are setting.
Inode InodeID
// The name of the extended attribute
Name string
// The data to for the extened attribute.
Data []byte
// The value to for the extened attribute.
Value []byte
// If Flags is 0x1, and the attribute exists already, EEXIST should be returned.
// If Flags is 0x2, and the attribute does not exist, ENOATTR should be returned.
// If Flags is 0x0, the extended attribute will be created if need be, or will
// simply replace the value if the attribute exists.
Flags uint32
}
......@@ -61,6 +61,9 @@ type inode struct {
//
// INVARIANT: If !isSymlink(), len(target) == 0
target string
// extended attributes and values
xattrs map[string][]byte
}
////////////////////////////////////////////////////////////////////////
......@@ -78,7 +81,8 @@ func newInode(
// Create the object.
in = &inode{
attrs: attrs,
attrs: attrs,
xattrs: make(map[string][]byte),
}
return
......
......@@ -18,6 +18,7 @@ import (
"fmt"
"io"
"os"
"syscall"
"time"
"golang.org/x/net/context"
......@@ -628,3 +629,88 @@ func (fs *memFS) ReadSymlink(
return
}
func (fs *memFS) GetXattr(ctx context.Context,
op *fuseops.GetXattrOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
inode := fs.getInodeOrDie(op.Inode)
if value, ok := inode.xattrs[op.Name]; ok {
op.BytesRead = len(value)
if len(op.Dst) >= len(value) {
copy(op.Dst, value)
} else {
err = syscall.ERANGE
}
} else {
err = fuse.ENOATTR
}
return
}
func (fs *memFS) ListXattr(ctx context.Context,
op *fuseops.ListXattrOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
inode := fs.getInodeOrDie(op.Inode)
dst := op.Dst[:]
for key := range inode.xattrs {
keyLen := len(key) + 1
if err == nil && len(dst) >= keyLen {
copy(dst, key)
dst = dst[keyLen:]
} else {
err = syscall.ERANGE
}
op.BytesRead += keyLen
}
return
}
func (fs *memFS) RemoveXattr(ctx context.Context,
op *fuseops.RemoveXattrOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
inode := fs.getInodeOrDie(op.Inode)
if _, ok := inode.xattrs[op.Name]; ok {
delete(inode.xattrs, op.Name)
} else {
err = fuse.ENOATTR
}
return
}
func (fs *memFS) SetXattr(ctx context.Context,
op *fuseops.SetXattrOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
inode := fs.getInodeOrDie(op.Inode)
_, ok := inode.xattrs[op.Name]
switch op.Flags {
case 0x1:
if ok {
err = fuse.EEXIST
}
case 0x2:
if !ok {
err = fuse.ENOATTR
}
}
if err == nil {
value := make([]byte, len(op.Value))
copy(value, op.Value)
inode.xattrs[op.Name] = value
}
return
}
......@@ -27,6 +27,8 @@ import (
"testing"
"time"
"github.com/ivaxer/go-xattr"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fusetesting"
"github.com/jacobsa/fuse/samples"
"github.com/jacobsa/fuse/samples/memfs"
......@@ -1611,6 +1613,83 @@ func (t *MemFSTest) RenameNonExistentFile() {
ExpectThat(err, Error(HasSubstr("no such file")))
}
func (t *MemFSTest) GetListNoXAttr() {
var err error
// Create a file
filePath := path.Join(t.Dir, "foo")
err = ioutil.WriteFile(filePath, []byte("taco"), 0400)
AssertEq(nil, err)
names, err := xattr.List(filePath)
AssertEq(nil, err)
AssertEq(0, len(names))
_, err = xattr.Getxattr(filePath, "foo", nil)
AssertEq(fuse.ENOATTR, err)
}
func (t *MemFSTest) SetXAttr() {
var err error
// Create a file
filePath := path.Join(t.Dir, "foo")
err = ioutil.WriteFile(filePath, []byte("taco"), 0600)
AssertEq(nil, err)
err = xattr.Setxattr(filePath, "foo", []byte("bar"), 0x2)
AssertEq(fuse.ENOATTR, err)
err = xattr.Setxattr(filePath, "foo", []byte("bar"), 0x1)
AssertEq(nil, err)
value, err := xattr.Get(filePath, "foo")
AssertEq(nil, err)
AssertEq("bar", string(value))
err = xattr.Setxattr(filePath, "foo", []byte("hello world"), 0x2)
AssertEq(nil, err)
value, err = xattr.Get(filePath, "foo")
AssertEq(nil, err)
AssertEq("hello world", string(value))
names, err := xattr.List(filePath)
AssertEq(nil, err)
AssertEq(1, len(names))
AssertEq("foo", names[0])
err = xattr.Setxattr(filePath, "bar", []byte("hello world"), 0x0)
AssertEq(nil, err)
names, err = xattr.List(filePath)
AssertEq(nil, err)
AssertEq(2, len(names))
ExpectThat(names, Contains("foo"))
ExpectThat(names, Contains("bar"))
}
func (t *MemFSTest) RemoveXAttr() {
var err error
// Create a file
filePath := path.Join(t.Dir, "foo")
err = ioutil.WriteFile(filePath, []byte("taco"), 0600)
AssertEq(nil, err)
err = xattr.Removexattr(filePath, "foo")
AssertEq(fuse.ENOATTR, err)
err = xattr.Setxattr(filePath, "foo", []byte("bar"), 0x1)
AssertEq(nil, err)
err = xattr.Removexattr(filePath, "foo")
AssertEq(nil, err)
_, err = xattr.Getxattr(filePath, "foo", nil)
AssertEq(fuse.ENOATTR, err)
}
////////////////////////////////////////////////////////////////////////
// Mknod
////////////////////////////////////////////////////////////////////////
......
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