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