added tests and enhanced comments
parent
cbeaa55087
commit
d650fe34ba
|
@ -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
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
@ -79,6 +82,7 @@ func newInode(
|
|||
// Create the object.
|
||||
in = &inode{
|
||||
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
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Reference in New Issue