Commit 919f45da authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Use arrays for opcode dispatches; work in progress.

parent 88b3aa85
// Code that handles the control loop, and en/decoding messages
// to/from the kernel. Dispatches calls into RawFileSystem.
package fuse
import (
......@@ -28,8 +31,9 @@ type request struct {
inputBuf []byte
// These split up inputBuf.
inHeader *InHeader
arg []byte
inHeader *InHeader // generic header
inData unsafe.Pointer // per op data
arg []byte // flat data.
// Unstructured data, a pointer to the relevant XxxxOut struct.
data unsafe.Pointer
......@@ -46,6 +50,14 @@ type request struct {
preWriteNs int64
}
func (me *request) filename() string {
return strings.TrimRight(string(me.arg), "\x00")
}
func (me *request) filenames(count int) []string {
return strings.Split(string(me.arg), "\x00", count)
}
type MountState struct {
// Empty if unmounted.
mountPoint string
......@@ -261,35 +273,40 @@ func (me *MountState) handle(req *request) {
func (me *MountState) dispatch(req *request) {
h := req.inHeader
argumentSize, ok := inputSizeMap[int(h.Opcode)]
argSize, ok := inputSize(h.Opcode)
if !ok {
log.Println("Unknown opcode %d (input)", h.Opcode)
req.status = ENOSYS
return
}
if len(req.arg) < argumentSize {
if len(req.arg) < argSize {
log.Println("Short read for %v: %v", h.Opcode, req.arg)
req.status = EIO
return
}
var inData unsafe.Pointer
if argumentSize > 0 {
inData = unsafe.Pointer(&req.arg[0])
if argSize > 0 {
req.inData = unsafe.Pointer(&req.arg[0])
req.arg = req.arg[argSize:]
}
data := req.arg[argumentSize:]
var status Status = OK
fs := me.fileSystem
f := lookupOperation(h.Opcode)
if f != nil {
f(me, req)
return
}
req.status = OK
filename := ""
// Perhaps a map is faster?
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_GETXATTR ||
h.Opcode == FUSE_MKNOD ||
h.Opcode == FUSE_LINK ||
h.Opcode == FUSE_REMOVEXATTR {
filename = strings.TrimRight(string(data), "\x00")
filename = strings.TrimRight(string(req.arg), "\x00")
}
if me.Debug {
nm := ""
......@@ -297,43 +314,43 @@ func (me *MountState) dispatch(req *request) {
nm = "n: '" + filename + "'"
}
if h.Opcode == FUSE_RENAME {
nm = "n: '" + string(data) + "'"
nm = "n: '" + string(req.arg) + "'"
}
log.Printf("Dispatch: %v, NodeId: %v %s\n", operationName(h.Opcode), h.NodeId, nm)
}
// Follow ordering of fuse_lowlevel.h.
var status Status
fs := me.fileSystem
switch h.Opcode {
case FUSE_INIT:
req.data, status = me.init(h, (*InitIn)(inData))
req.data, status = me.init(h, (*InitIn)(req.inData))
case FUSE_DESTROY:
fs.Destroy(h, (*InitIn)(inData))
fs.Destroy(h, (*InitIn)(req.inData))
case FUSE_LOOKUP:
lookupOut, s := fs.Lookup(h, filename)
status = s
req.data = unsafe.Pointer(lookupOut)
case FUSE_FORGET:
fs.Forget(h, (*ForgetIn)(inData))
fs.Forget(h, (*ForgetIn)(req.inData))
// 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
return
case FUSE_GETATTR:
// TODO - if inData.Fh is set, do file.GetAttr
attrOut, s := fs.GetAttr(h, (*GetAttrIn)(inData))
// TODO - if req.inData.Fh is set, do file.GetAttr
attrOut, s := fs.GetAttr(h, (*GetAttrIn)(req.inData))
status = s
req.data = unsafe.Pointer(attrOut)
case FUSE_SETATTR:
req.data, status = doSetattr(me, h, (*SetAttrIn)(inData))
case FUSE_READLINK:
req.flatData, status = fs.Readlink(h)
case FUSE_MKNOD:
entryOut, s := fs.Mknod(h, (*MknodIn)(inData), filename)
entryOut, s := fs.Mknod(h, (*MknodIn)(req.inData), filename)
status = s
req.data = unsafe.Pointer(entryOut)
case FUSE_MKDIR:
entryOut, s := fs.Mkdir(h, (*MkdirIn)(inData), filename)
entryOut, s := fs.Mkdir(h, (*MkdirIn)(req.inData), filename)
status = s
req.data = unsafe.Pointer(entryOut)
case FUSE_UNLINK:
......@@ -341,7 +358,7 @@ func (me *MountState) dispatch(req *request) {
case FUSE_RMDIR:
status = fs.Rmdir(h, filename)
case FUSE_SYMLINK:
filenames := strings.Split(string(data), "\x00", 3)
filenames := req.filenames(3)
if len(filenames) >= 2 {
entryOut, s := fs.Symlink(h, filenames[1], filenames[0])
status = s
......@@ -350,63 +367,48 @@ func (me *MountState) dispatch(req *request) {
status = EIO
}
case FUSE_RENAME:
filenames := strings.Split(string(data), "\x00", 3)
filenames := strings.Split(string(req.arg), "\x00", 3)
if len(filenames) >= 2 {
status = fs.Rename(h, (*RenameIn)(inData), filenames[0], filenames[1])
status = fs.Rename(h, (*RenameIn)(req.inData), filenames[0], filenames[1])
} else {
status = EIO
}
case FUSE_LINK:
entryOut, s := fs.Link(h, (*LinkIn)(inData), filename)
entryOut, s := fs.Link(h, (*LinkIn)(req.inData), filename)
status = s
req.data = unsafe.Pointer(entryOut)
case FUSE_OPEN:
req.data, status = doOpen(me, h, (*OpenIn)(inData))
case FUSE_READ:
req.flatData, status = me.fileSystem.Read((*ReadIn)(inData), me.buffers)
case FUSE_WRITE:
req.data, status = doWrite(me, h, (*WriteIn)(inData), data)
req.flatData, status = me.fileSystem.Read((*ReadIn)(req.inData), me.buffers)
case FUSE_FLUSH:
status = me.fileSystem.Flush((*FlushIn)(inData))
status = me.fileSystem.Flush((*FlushIn)(req.inData))
case FUSE_RELEASE:
me.fileSystem.Release(h, (*ReleaseIn)(inData))
me.fileSystem.Release(h, (*ReleaseIn)(req.inData))
case FUSE_FSYNC:
status = me.fileSystem.Fsync((*FsyncIn)(inData))
case FUSE_OPENDIR:
req.data, status = doOpenDir(me, h, (*OpenIn)(inData))
case FUSE_READDIR:
req.flatData, status = doReadDir(me, h, (*ReadIn)(inData))
status = me.fileSystem.Fsync((*FsyncIn)(req.inData))
case FUSE_RELEASEDIR:
me.fileSystem.ReleaseDir(h, (*ReleaseIn)(inData))
me.fileSystem.ReleaseDir(h, (*ReleaseIn)(req.inData))
case FUSE_FSYNCDIR:
status = me.fileSystem.FsyncDir(h, (*FsyncIn)(inData))
status = me.fileSystem.FsyncDir(h, (*FsyncIn)(req.inData))
case FUSE_SETXATTR:
splits := bytes.Split(data, []byte{0}, 2)
status = fs.SetXAttr(h, (*SetXAttrIn)(inData), string(splits[0]), splits[1])
case FUSE_GETXATTR:
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)
splits := bytes.Split(req.arg, []byte{0}, 2)
status = fs.SetXAttr(h, (*SetXAttrIn)(req.inData), string(splits[0]), splits[1])
case FUSE_REMOVEXATTR:
status = fs.RemoveXAttr(h, filename)
case FUSE_ACCESS:
status = fs.Access(h, (*AccessIn)(inData))
case FUSE_CREATE:
req.data, status = doCreate(me, h, (*CreateIn)(inData), filename)
status = fs.Access(h, (*AccessIn)(req.inData))
// TODO - implement file locking.
// case FUSE_SETLK
// case FUSE_SETLKW
case FUSE_BMAP:
bmapOut, s := fs.Bmap(h, (*BmapIn)(inData))
bmapOut, s := fs.Bmap(h, (*BmapIn)(req.inData))
status = s
req.data = unsafe.Pointer(bmapOut)
case FUSE_IOCTL:
ioctlOut, s := fs.Ioctl(h, (*IoctlIn)(inData))
ioctlOut, s := fs.Ioctl(h, (*IoctlIn)(req.inData))
status = s
req.data = unsafe.Pointer(ioctlOut)
case FUSE_POLL:
pollOut, s := fs.Poll(h, (*PollIn)(inData))
pollOut, s := fs.Poll(h, (*PollIn)(req.inData))
status = s
req.data = unsafe.Pointer(pollOut)
......@@ -428,7 +430,7 @@ func asSlice(ptr unsafe.Pointer, byteCount int) []byte {
}
func serialize(req *request, debug bool) {
dataLength, ok := outputSizeMap[int(req.inHeader.Opcode)]
dataLength, ok := outputSize(req.inHeader.Opcode)
if !ok {
log.Println("Unknown opcode %d (output)", req.inHeader.Opcode)
req.status = ENOSYS
......@@ -487,92 +489,3 @@ func (me *MountState) init(h *InHeader, input *InitIn) (unsafe.Pointer, Status)
return unsafe.Pointer(out), OK
}
////////////////////////////////////////////////////////////////
// Handling files.
func doOpen(state *MountState, header *InHeader, input *OpenIn) (unsafe.Pointer, Status) {
flags, handle, status := state.fileSystem.Open(header, input)
if status != OK {
return nil, status
}
out := new(OpenOut)
out.Fh = handle
out.OpenFlags = flags
return unsafe.Pointer(out), status
}
func doCreate(state *MountState, header *InHeader, input *CreateIn, name string) (unsafe.Pointer, Status) {
flags, handle, entry, status := state.fileSystem.Create(header, input, name)
if status != OK {
return nil, status
}
out := new(CreateOut)
out.Entry = *entry
out.Open.Fh = handle
out.Open.OpenFlags = flags
return unsafe.Pointer(out), status
}
func doWrite(state *MountState, header *InHeader, input *WriteIn, data []byte) (out unsafe.Pointer, code Status) {
n, status := state.fileSystem.Write(input, data)
o := &WriteOut{
Size: n,
}
return unsafe.Pointer(o), status
}
func doSetattr(state *MountState, header *InHeader, input *SetAttrIn) (out unsafe.Pointer, code Status) {
// TODO - if Fh != 0, we should do a FSetAttr instead.
o, s := state.fileSystem.SetAttr(header, input)
return unsafe.Pointer(o), s
}
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
}
size := uint32(len(data))
if input.Size == 0 {
out := new(GetXAttrOut)
out.Size = size
return unsafe.Pointer(out), nil, OK
}
if size > input.Size {
return nil, nil, ERANGE
}
return nil, data, OK
}
////////////////////////////////////////////////////////////////
// Handling directories
func doOpenDir(state *MountState, header *InHeader, input *OpenIn) (unsafe.Pointer, Status) {
flags, handle, status := state.fileSystem.OpenDir(header, input)
if status != OK {
return nil, status
}
out := new(OpenOut)
out.Fh = handle
out.OpenFlags = flags
return unsafe.Pointer(out), status
}
func doReadDir(state *MountState, header *InHeader, input *ReadIn) (out []byte, code Status) {
entries, code := state.fileSystem.ReadDir(header, input)
if entries == nil {
return nil, code
}
return entries.Bytes(), code
}
......@@ -13,7 +13,7 @@ func (code Status) String() string {
return fmt.Sprintf("%d=%v", int(code), os.Errno(code))
}
func replyString(opcode uint32, ptr unsafe.Pointer) string {
func replyString(opcode Opcode, ptr unsafe.Pointer) string {
var val interface{}
switch opcode {
case FUSE_LOOKUP:
......@@ -27,95 +27,153 @@ func replyString(opcode uint32, ptr unsafe.Pointer) string {
return ""
}
func operationName(opcode uint32) string {
switch opcode {
case FUSE_LOOKUP:
return "FUSE_LOOKUP"
case FUSE_FORGET:
return "FUSE_FORGET"
case FUSE_GETATTR:
return "FUSE_GETATTR"
case FUSE_SETATTR:
return "FUSE_SETATTR"
case FUSE_READLINK:
return "FUSE_READLINK"
case FUSE_SYMLINK:
return "FUSE_SYMLINK"
case FUSE_MKNOD:
return "FUSE_MKNOD"
case FUSE_MKDIR:
return "FUSE_MKDIR"
case FUSE_UNLINK:
return "FUSE_UNLINK"
case FUSE_RMDIR:
return "FUSE_RMDIR"
case FUSE_RENAME:
return "FUSE_RENAME"
case FUSE_LINK:
return "FUSE_LINK"
case FUSE_OPEN:
return "FUSE_OPEN"
case FUSE_READ:
return "FUSE_READ"
case FUSE_WRITE:
return "FUSE_WRITE"
case FUSE_STATFS:
return "FUSE_STATFS"
case FUSE_RELEASE:
return "FUSE_RELEASE"
case FUSE_FSYNC:
return "FUSE_FSYNC"
case FUSE_SETXATTR:
return "FUSE_SETXATTR"
case FUSE_GETXATTR:
return "FUSE_GETXATTR"
case FUSE_LISTXATTR:
return "FUSE_LISTXATTR"
case FUSE_REMOVEXATTR:
return "FUSE_REMOVEXATTR"
case FUSE_FLUSH:
return "FUSE_FLUSH"
case FUSE_INIT:
return "FUSE_INIT"
case FUSE_OPENDIR:
return "FUSE_OPENDIR"
case FUSE_READDIR:
return "FUSE_READDIR"
case FUSE_RELEASEDIR:
return "FUSE_RELEASEDIR"
case FUSE_FSYNCDIR:
return "FUSE_FSYNCDIR"
case FUSE_GETLK:
return "FUSE_GETLK"
case FUSE_SETLK:
return "FUSE_SETLK"
case FUSE_SETLKW:
return "FUSE_SETLKW"
case FUSE_ACCESS:
return "FUSE_ACCESS"
case FUSE_CREATE:
return "FUSE_CREATE"
case FUSE_INTERRUPT:
return "FUSE_INTERRUPT"
case FUSE_BMAP:
return "FUSE_BMAP"
case FUSE_DESTROY:
return "FUSE_DESTROY"
case FUSE_IOCTL:
return "FUSE_IOCTL"
case FUSE_POLL:
return "FUSE_POLL"
}
return "UNKNOWN"
}
var inputSizeMap map[int]int
var outputSizeMap map[int]int
////////////////////////////////////////////////////////////////
func doOpen(state *MountState, req *request) {
flags, handle, status := state.fileSystem.Open(req.inHeader, (*OpenIn)(req.inData))
req.status = status
if status != OK {
return
}
out := &OpenOut{
Fh: handle,
OpenFlags: flags,
}
req.data = unsafe.Pointer(out)
}
func doCreate(state *MountState, req *request) {
flags, handle, entry, status := state.fileSystem.Create(req.inHeader, (*CreateIn)(req.inData), req.filename())
req.status = status
if status == OK {
req.data = unsafe.Pointer(&CreateOut{
EntryOut: *entry,
OpenOut: OpenOut{
Fh: handle,
OpenFlags: flags,
},
})
}
}
func doReadDir(state *MountState, req *request) {
entries, code := state.fileSystem.ReadDir(req.inHeader, (*ReadIn)(req.inData))
if entries != nil {
req.flatData = entries.Bytes()
}
req.status = code
}
func doOpenDir(state *MountState, req *request) {
flags, handle, status := state.fileSystem.OpenDir(req.inHeader, (*OpenIn)(req.inData))
req.status = status
if status == OK {
req.data = unsafe.Pointer(&OpenOut{
Fh: handle,
OpenFlags: flags,
})
}
}
func doSetattr(state *MountState, req *request) {
// TODO - if Fh != 0, we should do a FSetAttr instead.
o, s := state.fileSystem.SetAttr(req.inHeader, (*SetAttrIn)(req.inData))
req.data = unsafe.Pointer(o)
req.status = s
}
func doWrite(state *MountState, req *request) {
n, status := state.fileSystem.Write((*WriteIn)(req.inData), req.arg)
o := &WriteOut{
Size: n,
}
req.data = unsafe.Pointer(o)
req.status = status
}
func doGetXAttr(state *MountState, req *request) {
input := (*GetXAttrIn)(req.inData)
var data []byte
if req.inHeader.Opcode == FUSE_GETXATTR {
data, req.status = state.fileSystem.GetXAttr(req.inHeader, req.filename())
} else {
data, req.status = state.fileSystem.ListXAttr(req.inHeader)
}
if req.status != OK {
return
}
size := uint32(len(data))
if input.Size == 0 {
out := &GetXAttrOut{
Size: size,
}
req.data = unsafe.Pointer(out)
}
if size > input.Size {
req.status = ERANGE
}
req.flatData = data
}
////////////////////////////////////////////////////////////////
var operationNames []string
var inputSizeMap []int
var outputSizeMap []int
type operation func(*MountState, *request)
var operationFuncs []operation
func operationName(opcode Opcode) string {
if opcode > OPCODE_COUNT {
return "unknown"
}
return operationNames[opcode]
}
func inputSize(o Opcode) (int, bool) {
return lookupSize(o, inputSizeMap)
}
func outputSize(o Opcode) (int, bool) {
return lookupSize(o, outputSizeMap)
}
func lookupSize(o Opcode, sMap []int) (int, bool) {
if o >= OPCODE_COUNT {
return -1, false
}
return sMap[int(o)], true
}
func lookupOperation(o Opcode) operation {
return operationFuncs[o]
}
func makeSizeMap(dict map[int]int) []int {
out := make([]int, OPCODE_COUNT)
for i, _ := range out {
out[i] = -1
}
for code, val := range dict {
out[code] = val
}
return out
}
func init() {
inputSizeMap = map[int]int{
inputSizeMap = makeSizeMap(map[int]int{
FUSE_LOOKUP: 0,
FUSE_FORGET: unsafe.Sizeof(ForgetIn{}),
FUSE_GETATTR: unsafe.Sizeof(GetAttrIn{}),
......@@ -154,9 +212,9 @@ func init() {
FUSE_DESTROY: 0,
FUSE_IOCTL: unsafe.Sizeof(IoctlIn{}),
FUSE_POLL: unsafe.Sizeof(PollIn{}),
}
})
outputSizeMap = map[int]int{
outputSizeMap = makeSizeMap(map[int]int{
FUSE_LOOKUP: unsafe.Sizeof(EntryOut{}),
FUSE_FORGET: 0,
FUSE_GETATTR: unsafe.Sizeof(AttrOut{}),
......@@ -196,5 +254,62 @@ func init() {
FUSE_DESTROY: 0,
FUSE_IOCTL: unsafe.Sizeof(IoctlOut{}),
FUSE_POLL: unsafe.Sizeof(PollOut{}),
})
operationNames = make([]string, OPCODE_COUNT)
for k, v := range map[int]string{
FUSE_LOOKUP:"FUSE_LOOKUP",
FUSE_FORGET:"FUSE_FORGET",
FUSE_GETATTR:"FUSE_GETATTR",
FUSE_SETATTR:"FUSE_SETATTR",
FUSE_READLINK:"FUSE_READLINK",
FUSE_SYMLINK:"FUSE_SYMLINK",
FUSE_MKNOD:"FUSE_MKNOD",
FUSE_MKDIR:"FUSE_MKDIR",
FUSE_UNLINK:"FUSE_UNLINK",
FUSE_RMDIR:"FUSE_RMDIR",
FUSE_RENAME:"FUSE_RENAME",
FUSE_LINK:"FUSE_LINK",
FUSE_OPEN:"FUSE_OPEN",
FUSE_READ:"FUSE_READ",
FUSE_WRITE:"FUSE_WRITE",
FUSE_STATFS:"FUSE_STATFS",
FUSE_RELEASE:"FUSE_RELEASE",
FUSE_FSYNC:"FUSE_FSYNC",
FUSE_SETXATTR:"FUSE_SETXATTR",
FUSE_GETXATTR:"FUSE_GETXATTR",
FUSE_LISTXATTR:"FUSE_LISTXATTR",
FUSE_REMOVEXATTR:"FUSE_REMOVEXATTR",
FUSE_FLUSH:"FUSE_FLUSH",
FUSE_INIT:"FUSE_INIT",
FUSE_OPENDIR:"FUSE_OPENDIR",
FUSE_READDIR:"FUSE_READDIR",
FUSE_RELEASEDIR:"FUSE_RELEASEDIR",
FUSE_FSYNCDIR:"FUSE_FSYNCDIR",
FUSE_GETLK:"FUSE_GETLK",
FUSE_SETLK:"FUSE_SETLK",
FUSE_SETLKW:"FUSE_SETLKW",
FUSE_ACCESS:"FUSE_ACCESS",
FUSE_CREATE:"FUSE_CREATE",
FUSE_INTERRUPT:"FUSE_INTERRUPT",
FUSE_BMAP:"FUSE_BMAP",
FUSE_DESTROY:"FUSE_DESTROY",
FUSE_IOCTL:"FUSE_IOCTL",
FUSE_POLL:"FUSE_POLL"} {
operationNames[k] = v
}
operationFuncs = make([]operation, OPCODE_COUNT)
for k, v := range map[Opcode]operation{
FUSE_OPEN: doOpen,
FUSE_READDIR: doReadDir,
FUSE_WRITE: doWrite,
FUSE_OPENDIR: doOpenDir,
FUSE_CREATE: doCreate,
FUSE_SETATTR: doSetattr,
FUSE_GETXATTR: doGetXAttr,
FUSE_LISTXATTR: doGetXAttr,
} {
operationFuncs[k] = v
}
}
......@@ -143,6 +143,8 @@ const (
FUSE_IOCTL = 39
FUSE_POLL = 40
OPCODE_COUNT = 41
CUSE_INIT = 4096
)
......@@ -287,8 +289,8 @@ type OpenOut struct {
}
type CreateOut struct {
Entry EntryOut
Open OpenOut
EntryOut
OpenOut
}
type ReleaseIn struct {
......@@ -456,7 +458,7 @@ type NotifyPollWakeupOut struct {
type InHeader struct {
Length uint32
Opcode uint32
Opcode
Unique uint64
NodeId uint64
Identity
......
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