package fuse import ( "bytes" "fmt" "log" "unsafe" ) var _ = log.Printf var _ = fmt.Printf type opcode int const ( _OP_LOOKUP = opcode(1) _OP_FORGET = opcode(2) _OP_GETATTR = opcode(3) _OP_SETATTR = opcode(4) _OP_READLINK = opcode(5) _OP_SYMLINK = opcode(6) _OP_MKNOD = opcode(8) _OP_MKDIR = opcode(9) _OP_UNLINK = opcode(10) _OP_RMDIR = opcode(11) _OP_RENAME = opcode(12) _OP_LINK = opcode(13) _OP_OPEN = opcode(14) _OP_READ = opcode(15) _OP_WRITE = opcode(16) _OP_STATFS = opcode(17) _OP_RELEASE = opcode(18) _OP_FSYNC = opcode(20) _OP_SETXATTR = opcode(21) _OP_GETXATTR = opcode(22) _OP_LISTXATTR = opcode(23) _OP_REMOVEXATTR = opcode(24) _OP_FLUSH = opcode(25) _OP_INIT = opcode(26) _OP_OPENDIR = opcode(27) _OP_READDIR = opcode(28) _OP_RELEASEDIR = opcode(29) _OP_FSYNCDIR = opcode(30) _OP_GETLK = opcode(31) _OP_SETLK = opcode(32) _OP_SETLKW = opcode(33) _OP_ACCESS = opcode(34) _OP_CREATE = opcode(35) _OP_INTERRUPT = opcode(36) _OP_BMAP = opcode(37) _OP_DESTROY = opcode(38) _OP_IOCTL = opcode(39) _OP_POLL = opcode(40) _OPCODE_COUNT = opcode(41) ) //////////////////////////////////////////////////////////////// func doInit(state *MountState, req *request) { const ( FUSE_KERNEL_VERSION = 7 FUSE_KERNEL_MINOR_VERSION = 13 ) input := (*InitIn)(req.inData) if input.Major != FUSE_KERNEL_VERSION { log.Printf("Major versions does not match. Given %d, want %d\n", input.Major, FUSE_KERNEL_VERSION) req.status = EIO return } if input.Minor < FUSE_KERNEL_MINOR_VERSION { log.Printf("Minor version is less than we support. Given %d, want at least %d\n", input.Minor, FUSE_KERNEL_MINOR_VERSION) req.status = EIO return } state.kernelSettings = *input state.kernelSettings.Flags = input.Flags & (CAP_ASYNC_READ | CAP_BIG_WRITES | CAP_FILE_OPS) out := &InitOut{ Major: FUSE_KERNEL_VERSION, Minor: FUSE_KERNEL_MINOR_VERSION, MaxReadAhead: input.MaxReadAhead, Flags: state.kernelSettings.Flags, MaxWrite: maxRead, CongestionThreshold: _BACKGROUND_TASKS * 3 / 4, MaxBackground: _BACKGROUND_TASKS, } req.outData = unsafe.Pointer(out) req.status = OK } 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.outData = unsafe.Pointer(out) } func doCreate(state *MountState, req *request) { flags, handle, entry, status := state.fileSystem.Create(req.inHeader, (*CreateIn)(req.inData), req.filenames[0]) req.status = status if status == OK { req.outData = 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.outData = unsafe.Pointer(&OpenOut{ Fh: handle, OpenFlags: flags, }) } } func doSetattr(state *MountState, req *request) { o, s := state.fileSystem.SetAttr(req.inHeader, (*SetAttrIn)(req.inData)) req.outData = 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.outData = unsafe.Pointer(o) req.status = status } func doGetXAttr(state *MountState, req *request) { input := (*GetXAttrIn)(req.inData) var data []byte if req.inHeader.opcode == _OP_GETXATTR { data, req.status = state.fileSystem.GetXAttr(req.inHeader, req.filenames[0]) } 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.outData = unsafe.Pointer(out) } if size > input.Size { req.status = ERANGE } req.flatData = data } func doGetAttr(state *MountState, req *request) { // TODO - if req.inData.Fh is set, do file.GetAttr attrOut, s := state.fileSystem.GetAttr(req.inHeader, (*GetAttrIn)(req.inData)) req.status = s req.outData = unsafe.Pointer(attrOut) } func doForget(state *MountState, req *request) { state.fileSystem.Forget(req.inHeader, (*ForgetIn)(req.inData)) } func doReadlink(state *MountState, req *request) { req.flatData, req.status = state.fileSystem.Readlink(req.inHeader) } func doDestroy(state *MountState, req *request) { state.fileSystem.Destroy(req.inHeader, (*InitIn)(req.inData)) } func doLookup(state *MountState, req *request) { lookupOut, s := state.fileSystem.Lookup(req.inHeader, req.filenames[0]) req.status = s req.outData = unsafe.Pointer(lookupOut) } func doMknod(state *MountState, req *request) { entryOut, s := state.fileSystem.Mknod(req.inHeader, (*MknodIn)(req.inData), req.filenames[0]) req.status = s req.outData = unsafe.Pointer(entryOut) } func doMkdir(state *MountState, req *request) { entryOut, s := state.fileSystem.Mkdir(req.inHeader, (*MkdirIn)(req.inData), req.filenames[0]) req.status = s req.outData = unsafe.Pointer(entryOut) } func doUnlink(state *MountState, req *request) { req.status = state.fileSystem.Unlink(req.inHeader, req.filenames[0]) } func doRmdir(state *MountState, req *request) { req.status = state.fileSystem.Rmdir(req.inHeader, req.filenames[0]) } func doLink(state *MountState, req *request) { entryOut, s := state.fileSystem.Link(req.inHeader, (*LinkIn)(req.inData), req.filenames[0]) req.status = s req.outData = unsafe.Pointer(entryOut) } func doRead(state *MountState, req *request) { req.flatData, req.status = state.fileSystem.Read((*ReadIn)(req.inData), state.buffers) } func doFlush(state *MountState, req *request) { req.status = state.fileSystem.Flush((*FlushIn)(req.inData)) } func doRelease(state *MountState, req *request) { state.fileSystem.Release(req.inHeader, (*ReleaseIn)(req.inData)) } func doFsync(state *MountState, req *request) { req.status = state.fileSystem.Fsync((*FsyncIn)(req.inData)) } func doReleaseDir(state *MountState, req *request) { state.fileSystem.ReleaseDir(req.inHeader, (*ReleaseIn)(req.inData)) } func doFsyncDir(state *MountState, req *request) { req.status = state.fileSystem.FsyncDir(req.inHeader, (*FsyncIn)(req.inData)) } func doSetXAttr(state *MountState, req *request) { splits := bytes.Split(req.arg, []byte{0}, 2) req.status = state.fileSystem.SetXAttr(req.inHeader, (*SetXAttrIn)(req.inData), string(splits[0]), splits[1]) } func doRemoveXAttr(state *MountState, req *request) { req.status = state.fileSystem.RemoveXAttr(req.inHeader, req.filenames[0]) } func doAccess(state *MountState, req *request) { req.status = state.fileSystem.Access(req.inHeader, (*AccessIn)(req.inData)) } func doSymlink(state *MountState, req *request) { entryOut, s := state.fileSystem.Symlink(req.inHeader, req.filenames[1], req.filenames[0]) req.status = s req.outData = unsafe.Pointer(entryOut) } func doRename(state *MountState, req *request) { req.status = state.fileSystem.Rename(req.inHeader, (*RenameIn)(req.inData), req.filenames[0], req.filenames[1]) } //////////////////////////////////////////////////////////////// type operationFunc func(*MountState, *request) type castPointerFunc func(unsafe.Pointer) interface{} type operationHandler struct { Name string Func operationFunc InputSize int OutputSize int DecodeIn castPointerFunc DecodeOut castPointerFunc FileNames int } var operationHandlers []*operationHandler func operationName(op opcode) string { h := getHandler(op) if h == nil { return "unknown" } return h.Name } func (op opcode) String() string { return operationName(op) } func getHandler(o opcode) *operationHandler { if o >= _OPCODE_COUNT { return nil } return operationHandlers[o] } func init() { operationHandlers = make([]*operationHandler, _OPCODE_COUNT) for i, _ := range operationHandlers { operationHandlers[i] = &operationHandler{Name: "UNKNOWN"} } for op, sz := range map[opcode]int{ _OP_FORGET: unsafe.Sizeof(ForgetIn{}), _OP_GETATTR: unsafe.Sizeof(GetAttrIn{}), _OP_SETATTR: unsafe.Sizeof(SetAttrIn{}), _OP_MKNOD: unsafe.Sizeof(MknodIn{}), _OP_MKDIR: unsafe.Sizeof(MkdirIn{}), _OP_RENAME: unsafe.Sizeof(RenameIn{}), _OP_LINK: unsafe.Sizeof(LinkIn{}), _OP_OPEN: unsafe.Sizeof(OpenIn{}), _OP_READ: unsafe.Sizeof(ReadIn{}), _OP_WRITE: unsafe.Sizeof(WriteIn{}), _OP_RELEASE: unsafe.Sizeof(ReleaseIn{}), _OP_FSYNC: unsafe.Sizeof(FsyncIn{}), _OP_SETXATTR: unsafe.Sizeof(SetXAttrIn{}), _OP_GETXATTR: unsafe.Sizeof(GetXAttrIn{}), _OP_LISTXATTR: unsafe.Sizeof(GetXAttrIn{}), _OP_FLUSH: unsafe.Sizeof(FlushIn{}), _OP_INIT: unsafe.Sizeof(InitIn{}), _OP_OPENDIR: unsafe.Sizeof(OpenIn{}), _OP_READDIR: unsafe.Sizeof(ReadIn{}), _OP_RELEASEDIR: unsafe.Sizeof(ReleaseIn{}), _OP_FSYNCDIR: unsafe.Sizeof(FsyncIn{}), _OP_ACCESS: unsafe.Sizeof(AccessIn{}), _OP_CREATE: unsafe.Sizeof(CreateIn{}), _OP_INTERRUPT: unsafe.Sizeof(InterruptIn{}), _OP_BMAP: unsafe.Sizeof(BmapIn{}), _OP_IOCTL: unsafe.Sizeof(IoctlIn{}), _OP_POLL: unsafe.Sizeof(PollIn{}), } { operationHandlers[op].InputSize = sz } for op, sz := range map[opcode]int{ _OP_LOOKUP: unsafe.Sizeof(EntryOut{}), _OP_GETATTR: unsafe.Sizeof(AttrOut{}), _OP_SETATTR: unsafe.Sizeof(AttrOut{}), _OP_SYMLINK: unsafe.Sizeof(EntryOut{}), _OP_MKNOD: unsafe.Sizeof(EntryOut{}), _OP_MKDIR: unsafe.Sizeof(EntryOut{}), _OP_LINK: unsafe.Sizeof(EntryOut{}), _OP_OPEN: unsafe.Sizeof(OpenOut{}), _OP_WRITE: unsafe.Sizeof(WriteOut{}), _OP_STATFS: unsafe.Sizeof(StatfsOut{}), _OP_GETXATTR: unsafe.Sizeof(GetXAttrOut{}), _OP_LISTXATTR: unsafe.Sizeof(GetXAttrOut{}), _OP_INIT: unsafe.Sizeof(InitOut{}), _OP_OPENDIR: unsafe.Sizeof(OpenOut{}), _OP_CREATE: unsafe.Sizeof(CreateOut{}), _OP_BMAP: unsafe.Sizeof(BmapOut{}), _OP_IOCTL: unsafe.Sizeof(IoctlOut{}), _OP_POLL: unsafe.Sizeof(PollOut{}), } { operationHandlers[op].OutputSize = sz } for op, v := range map[opcode]string{ _OP_LOOKUP: "LOOKUP", _OP_FORGET: "FORGET", _OP_GETATTR: "GETATTR", _OP_SETATTR: "SETATTR", _OP_READLINK: "READLINK", _OP_SYMLINK: "SYMLINK", _OP_MKNOD: "MKNOD", _OP_MKDIR: "MKDIR", _OP_UNLINK: "UNLINK", _OP_RMDIR: "RMDIR", _OP_RENAME: "RENAME", _OP_LINK: "LINK", _OP_OPEN: "OPEN", _OP_READ: "READ", _OP_WRITE: "WRITE", _OP_STATFS: "STATFS", _OP_RELEASE: "RELEASE", _OP_FSYNC: "FSYNC", _OP_SETXATTR: "SETXATTR", _OP_GETXATTR: "GETXATTR", _OP_LISTXATTR: "LISTXATTR", _OP_REMOVEXATTR: "REMOVEXATTR", _OP_FLUSH: "FLUSH", _OP_INIT: "INIT", _OP_OPENDIR: "OPENDIR", _OP_READDIR: "READDIR", _OP_RELEASEDIR: "RELEASEDIR", _OP_FSYNCDIR: "FSYNCDIR", _OP_GETLK: "GETLK", _OP_SETLK: "SETLK", _OP_SETLKW: "SETLKW", _OP_ACCESS: "ACCESS", _OP_CREATE: "CREATE", _OP_INTERRUPT: "INTERRUPT", _OP_BMAP: "BMAP", _OP_DESTROY: "DESTROY", _OP_IOCTL: "IOCTL", _OP_POLL: "POLL"} { operationHandlers[op].Name = v } for op, v := range map[opcode]operationFunc{ _OP_OPEN: doOpen, _OP_READDIR: doReadDir, _OP_WRITE: doWrite, _OP_OPENDIR: doOpenDir, _OP_CREATE: doCreate, _OP_SETATTR: doSetattr, _OP_GETXATTR: doGetXAttr, _OP_LISTXATTR: doGetXAttr, _OP_GETATTR: doGetAttr, _OP_FORGET: doForget, _OP_READLINK: doReadlink, _OP_INIT: doInit, _OP_DESTROY: doDestroy, _OP_LOOKUP: doLookup, _OP_MKNOD: doMknod, _OP_MKDIR: doMkdir, _OP_UNLINK: doUnlink, _OP_RMDIR: doRmdir, _OP_LINK: doLink, _OP_READ: doRead, _OP_FLUSH: doFlush, _OP_RELEASE: doRelease, _OP_FSYNC: doFsync, _OP_RELEASEDIR: doReleaseDir, _OP_FSYNCDIR: doFsyncDir, _OP_SETXATTR: doSetXAttr, _OP_REMOVEXATTR: doRemoveXAttr, _OP_ACCESS: doAccess, _OP_SYMLINK: doSymlink, _OP_RENAME: doRename, } { operationHandlers[op].Func = v } for op, f := range map[opcode]castPointerFunc{ _OP_LOOKUP: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) }, _OP_OPEN: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) }, _OP_GETATTR: func(ptr unsafe.Pointer) interface{} { return (*AttrOut)(ptr) }, _OP_CREATE: func(ptr unsafe.Pointer) interface{} { return (*CreateOut)(ptr) }, } { operationHandlers[op].DecodeOut = f } for op, f := range map[opcode]castPointerFunc{ _OP_GETATTR: func(ptr unsafe.Pointer) interface{} { return (*GetAttrIn)(ptr) }, _OP_SETATTR: func(ptr unsafe.Pointer) interface{} { return (*SetAttrIn)(ptr) }, _OP_INIT: func(ptr unsafe.Pointer) interface{} { return (*InitIn)(ptr) }, } { operationHandlers[op].DecodeIn = f } for op, count := range map[opcode]int{ _OP_CREATE: 1, _OP_GETXATTR: 1, _OP_LINK: 1, _OP_LOOKUP: 1, _OP_MKDIR: 1, _OP_MKNOD: 1, _OP_REMOVEXATTR: 1, _OP_RENAME: 2, _OP_RMDIR: 1, _OP_SYMLINK: 2, _OP_UNLINK: 1, } { operationHandlers[op].FileNames = count } }