Commit b1becd41 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Implement GetXAttr.

parent 79426f97
......@@ -511,14 +511,13 @@ func (me *SubmountFileSystem) SetXAttr(header *fuse.InHeader, input *fuse.SetXAt
return subfs.Fs.SetXAttr(header, input)
}
func (me *SubmountFileSystem) GetXAttr(header *fuse.InHeader, input *fuse.GetXAttrIn) (out *fuse.GetXAttrOut, code fuse.Status) {
func (me *SubmountFileSystem) GetXAttr(header *fuse.InHeader, attr string) (data []byte, code fuse.Status) {
var subfs *subFsInfo
header.NodeId, subfs = me.getSubFs(header.NodeId)
if subfs == nil {
return nil, fuse.ENOENT
}
out, code = subfs.Fs.GetXAttr(header, input)
return out, code
return subfs.Fs.GetXAttr(header, attr)
}
func (me *SubmountFileSystem) Access(header *fuse.InHeader, input *fuse.AccessIn) (code fuse.Status) {
......
......@@ -16,7 +16,8 @@ GOFILES=misc.go\
loopback.go \
wrappedfs.go \
timingfs.go \
timingrawfs.go
timingrawfs.go \
xattr.go
include $(GOROOT)/src/Make.pkg
......@@ -69,7 +69,7 @@ func (me *DefaultRawFuseFileSystem) SetXAttr(header *InHeader, input *SetXAttrIn
return ENOSYS
}
func (me *DefaultRawFuseFileSystem) GetXAttr(header *InHeader, input *GetXAttrIn) (out *GetXAttrOut, code Status) {
func (me *DefaultRawFuseFileSystem) GetXAttr(header *InHeader, attr string) (data []byte, code Status) {
return nil, ENOSYS
}
......@@ -149,6 +149,10 @@ func (me *DefaultPathFilesystem) GetAttr(name string) (*Attr, Status) {
return nil, ENOSYS
}
func (me *DefaultPathFilesystem) GetXAttr(name string, attr string) ([]byte, Status) {
return nil, ENOSYS
}
func (me *DefaultPathFilesystem) Readlink(name string) (string, Status) {
return "", ENOSYS
}
......
......@@ -81,6 +81,8 @@ type MountState struct {
statisticsMutex sync.Mutex
operationCounts map[string]int64
// In nanoseconds.
operationLatencies map[string]int64
}
......@@ -197,10 +199,9 @@ func (me *MountState) Write(req *fuseRequest) {
_, err := Writev(me.mountFile.Fd(), req.serialized)
if err != nil {
me.Error(os.NewError(fmt.Sprintf("writer: Writev %v failed, err: %v", req.serialized, err)))
me.Error(os.NewError(fmt.Sprintf("writer: Writev %v failed, err: %v. Opcode: %v",
req.serialized, err, operationName(req.inHeader.Opcode))))
}
me.discardFuseRequest(req)
}
func NewMountState(fs RawFileSystem) *MountState {
......@@ -221,7 +222,7 @@ func (me *MountState) Latencies() map[string]float64 {
r := make(map[string]float64)
for k, v := range me.operationCounts {
r[k] = float64(me.operationLatencies[k]) / float64(v)
r[k] = 1e-6 * float64(me.operationLatencies[k]) / float64(v)
}
return r
......@@ -282,14 +283,14 @@ func (me *MountState) discardFuseRequest(req *fuseRequest) {
opname := operationName(req.inHeader.Opcode)
key := opname
me.operationCounts[key] += 1
me.operationLatencies[key] += dt / 1e6
me.operationLatencies[key] += dt
key += "-dispatch"
me.operationLatencies[key] += (req.dispatchNs - req.startNs) / 1e6
me.operationLatencies[key] += (req.dispatchNs - req.startNs)
me.operationCounts[key] += 1
key = opname + "-write"
me.operationLatencies[key] += (endNs - req.preWriteNs) / 1e6
me.operationLatencies[key] += (endNs - req.preWriteNs)
me.operationCounts[key] += 1
me.buffers.FreeBuffer(req.inputBuf)
......@@ -347,8 +348,12 @@ func (me *MountState) handle(req *fuseRequest) {
return
}
me.dispatch(req)
if req.inHeader.Opcode != FUSE_FORGET {
serialize(req, me.Debug)
req.preWriteNs = time.Nanoseconds()
me.Write(req)
}
me.discardFuseRequest(req)
}
......@@ -359,7 +364,6 @@ func (me *MountState) dispatch(req *fuseRequest) {
input := newInput(h.Opcode)
if input != nil && !parseLittleEndian(req.arg, input) {
req.status = EIO
serialize(req, me.Debug)
return
}
......@@ -372,7 +376,7 @@ func (me *MountState) dispatch(req *fuseRequest) {
if h.Opcode == FUSE_UNLINK || h.Opcode == FUSE_RMDIR ||
h.Opcode == FUSE_LOOKUP || h.Opcode == FUSE_MKDIR ||
h.Opcode == FUSE_MKNOD || h.Opcode == FUSE_CREATE ||
h.Opcode == FUSE_LINK {
h.Opcode == FUSE_LINK || h.Opcode == FUSE_GETXATTR {
filename = strings.TrimRight(string(req.arg.Bytes()), "\x00")
}
if me.Debug {
......@@ -396,7 +400,6 @@ func (me *MountState) dispatch(req *fuseRequest) {
// If we try to write OK, nil, we will get
// error: writer: Writev [[16 0 0 0 0 0 0 0 17 0 0 0 0 0 0 0]]
// failed, err: writev: no such file or directory
me.discardFuseRequest(req)
return
case FUSE_GETATTR:
// TODO - if input.Fh is set, do file.GetAttr
......@@ -454,8 +457,9 @@ func (me *MountState) dispatch(req *fuseRequest) {
// TODO - implement XAttr routines.
// case FUSE_SETXATTR:
// status = fs.SetXAttr(h, input.(*SetXAttrIn))
// case FUSE_GETXATTR:
// out, status = fs.GetXAttr(h, input.(*GetXAttrIn))
case FUSE_GETXATTR:
out, req.flatData, status = doGetXAttr(me, h, input.(*GetXAttrIn), filename)
// case FUSE_LISTXATTR:
// case FUSE_REMOVEXATTR
......@@ -478,14 +482,11 @@ func (me *MountState) dispatch(req *fuseRequest) {
default:
me.Error(os.NewError(fmt.Sprintf("Unsupported OpCode: %d=%v", h.Opcode, operationName(h.Opcode))))
req.status = ENOSYS
serialize(req, me.Debug)
return
}
req.status = status
req.data = out
serialize(req, me.Debug)
}
func serialize(req *fuseRequest, debug bool) {
......@@ -613,6 +614,26 @@ func doSetattr(state *MountState, header *InHeader, input *SetAttrIn) (out *Attr
return state.fileSystem.SetAttr(header, input)
}
func doGetXAttr(state *MountState, header *InHeader, input *GetXAttrIn, attr string) (out Empty, data []byte, code Status) {
data, code = state.fileSystem.GetXAttr(header, attr)
if code != OK {
return nil, nil, code
}
size := uint32(len(data))
if input.Size == 0 {
out := new(GetXAttrOut)
out.Size = size
return out, nil, OK
}
if size > input.Size {
return nil, nil, ERANGE
}
return nil, data, OK
}
////////////////////////////////////////////////////////////////
// Handling directories
......
......@@ -146,6 +146,12 @@ func (me *LoopbackFileSystem) Create(path string, flags uint32, mode uint32) (fu
return &LoopbackFile{file: f}, OsErrorToFuseError(err)
}
func (me *LoopbackFileSystem) GetXAttr(name string, attr string) ([]byte, Status) {
data, errNo := GetXAttr(me.GetPath(name), attr)
return data, Status(errNo)
}
func (me *LoopbackFileSystem) SetOptions(options *PathFileSystemConnectorOptions) {
options.NegativeTimeout = 100.0
options.AttrTimeout = 100.0
......
......@@ -716,6 +716,16 @@ func (me *PathFileSystemConnector) ReleaseDir(header *InHeader, f RawFuseDir) {
}
}
func (me *PathFileSystemConnector) GetXAttr(header *InHeader, attribute string) (data []byte, code Status) {
path, mount := me.GetPath(header.NodeId)
if mount == nil {
return nil, ENOENT
}
data, code = mount.fs.GetXAttr(path, attribute)
return data, code
}
////////////////////////////////////////////////////////////////
// unimplemented.
......@@ -723,10 +733,6 @@ func (me *PathFileSystemConnector) SetXAttr(header *InHeader, input *SetXAttrIn)
return ENOSYS
}
func (me *PathFileSystemConnector) GetXAttr(header *InHeader, input *GetXAttrIn) (out *GetXAttrOut, code Status) {
return nil, ENOSYS
}
func (me *PathFileSystemConnector) Bmap(header *InHeader, input *BmapIn) (out *BmapOut, code Status) {
return nil, ENOSYS
}
......
......@@ -3,8 +3,11 @@ package fuse
import (
"sync"
"time"
"log"
)
var _ = log.Print
// TimingPathFilesystem is a wrapper to collect timings for a PathFilesystem
type TimingPathFilesystem struct {
original PathFilesystem
......@@ -51,6 +54,12 @@ func (me *TimingPathFilesystem) GetAttr(name string) (*Attr, Status) {
return me.original.GetAttr(name)
}
func (me *TimingPathFilesystem) GetXAttr(name string, attr string) ([]byte, Status) {
defer me.startTimer("GetXAttr")()
return me.original.GetXAttr(name, attr)
}
func (me *TimingPathFilesystem) Readlink(name string) (string, Status) {
defer me.startTimer("Readlink")()
return me.original.Readlink(name)
......
......@@ -126,9 +126,9 @@ func (me *TimingRawFilesystem) SetXAttr(header *InHeader, input *SetXAttrIn) Sta
return me.original.SetXAttr(header, input)
}
func (me *TimingRawFilesystem) GetXAttr(header *InHeader, input *GetXAttrIn) (out *GetXAttrOut, code Status) {
func (me *TimingRawFilesystem) GetXAttr(header *InHeader, attr string) (data []byte, code Status) {
defer me.startTimer("GetXAttr")()
return me.original.GetXAttr(header, input)
return me.original.GetXAttr(header, attr)
}
func (me *TimingRawFilesystem) Access(header *InHeader, input *AccessIn) (code Status) {
......
......@@ -86,14 +86,15 @@ type Status int32
const (
OK = Status(0)
EACCES = Status(syscall.EACCES)
EBUSY = Status(syscall.EBUSY)
EINVAL = Status(syscall.EINVAL)
EIO = Status(syscall.EIO)
ENOSYS = Status(syscall.ENOSYS)
ENOENT = Status(syscall.ENOENT)
ENOSYS = Status(syscall.ENOSYS)
ENOTDIR = Status(syscall.ENOTDIR)
EACCES = Status(syscall.EACCES)
EPERM = Status(syscall.EPERM)
EBUSY = Status(syscall.EBUSY)
EINVAL = Status(syscall.EINVAL)
ERANGE = Status(syscall.ERANGE)
EXDEV = Status(syscall.EXDEV)
)
......@@ -518,9 +519,10 @@ type RawFileSystem interface {
Rename(header *InHeader, input *RenameIn, oldName string, newName string) (code Status)
Link(header *InHeader, input *LinkIn, filename string) (out *EntryOut, code Status)
GetXAttr(header *InHeader, attr string) (data []byte, code Status)
// Unused:
SetXAttr(header *InHeader, input *SetXAttrIn) Status
GetXAttr(header *InHeader, input *GetXAttrIn) (out *GetXAttrOut, code Status)
Access(header *InHeader, input *AccessIn) (code Status)
Create(header *InHeader, input *CreateIn, name string) (flags uint32, fuseFile RawFuseFile, out *EntryOut, code Status)
......@@ -566,6 +568,8 @@ type PathFilesystem interface {
Truncate(name string, offset uint64) (code Status)
Open(name string, flags uint32) (file RawFuseFile, code Status)
GetXAttr(name string, attribute string) (data []byte, code Status)
// Where to hook up statfs?
//
// Unimplemented:
......
......@@ -153,8 +153,8 @@ func (me *WrappingRawFilesystem) SetXAttr(header *InHeader, input *SetXAttrIn) S
return me.original.SetXAttr(header, input)
}
func (me *WrappingRawFilesystem) GetXAttr(header *InHeader, input *GetXAttrIn) (out *GetXAttrOut, code Status) {
return me.original.GetXAttr(header, input)
func (me *WrappingRawFilesystem) GetXAttr(header *InHeader, attr string) (data []byte, code Status) {
return me.original.GetXAttr(header, attr)
}
func (me *WrappingRawFilesystem) Access(header *InHeader, input *AccessIn) (code Status) {
......
package fuse
import (
"bytes"
"syscall"
"fmt"
"unsafe"
)
var _ = fmt.Print
// TODO - move this into the Go distribution.
func getxattr(path string, attr string, dest []byte) (sz int, errno int) {
pathBs := []byte(path)
attrBs := []byte(attr)
size, _, errNo := syscall.Syscall6(
syscall.SYS_GETXATTR,
uintptr(unsafe.Pointer(&pathBs[0])),
uintptr(unsafe.Pointer(&attrBs[0])),
uintptr(unsafe.Pointer(&dest[0])),
uintptr(len(dest)),
0, 0)
return int(size), int(errNo)
}
func GetXAttr(path string, attr string) (value []byte, errno int) {
dest := make([]byte, 1024)
sz, errno := getxattr(path, attr, dest)
for sz > cap(dest) && errno == 0 {
dest = make([]byte, sz)
sz, errno = getxattr(path, attr, dest)
}
if errno != 0 {
return nil, errno
}
return dest[:sz], errno
}
func listxattr(path string, dest []byte) (sz int, errno int) {
pathbs := []byte(path)
size, _, errNo := syscall.Syscall(
syscall.SYS_LISTXATTR,
uintptr(unsafe.Pointer(&pathbs[0])),
uintptr(unsafe.Pointer(&dest[0])),
uintptr(len(dest)))
return int(size), int(errNo)
}
func ListXAttr(path string) (attributes [][]byte, errno int) {
dest := make([]byte, 1024)
sz, errno := listxattr(path, dest)
if errno != 0 {
return nil, errno
}
for sz > cap(dest) && errno == 0 {
dest = make([]byte, sz)
sz, errno = listxattr(path, dest)
}
// -1 to drop the final empty slice.
dest = dest[:sz-1]
attributes = bytes.Split(dest, []byte{0}, -1)
return attributes, errno
}
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