mirror of
https://github.com/rclone/rclone.git
synced 2024-11-30 04:23:48 +08:00
805 lines
28 KiB
Go
805 lines
28 KiB
Go
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package fuse
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"log"
|
|
"reflect"
|
|
"runtime"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
_OP_LOOKUP = uint32(1)
|
|
_OP_FORGET = uint32(2)
|
|
_OP_GETATTR = uint32(3)
|
|
_OP_SETATTR = uint32(4)
|
|
_OP_READLINK = uint32(5)
|
|
_OP_SYMLINK = uint32(6)
|
|
_OP_MKNOD = uint32(8)
|
|
_OP_MKDIR = uint32(9)
|
|
_OP_UNLINK = uint32(10)
|
|
_OP_RMDIR = uint32(11)
|
|
_OP_RENAME = uint32(12)
|
|
_OP_LINK = uint32(13)
|
|
_OP_OPEN = uint32(14)
|
|
_OP_READ = uint32(15)
|
|
_OP_WRITE = uint32(16)
|
|
_OP_STATFS = uint32(17)
|
|
_OP_RELEASE = uint32(18)
|
|
_OP_FSYNC = uint32(20)
|
|
_OP_SETXATTR = uint32(21)
|
|
_OP_GETXATTR = uint32(22)
|
|
_OP_LISTXATTR = uint32(23)
|
|
_OP_REMOVEXATTR = uint32(24)
|
|
_OP_FLUSH = uint32(25)
|
|
_OP_INIT = uint32(26)
|
|
_OP_OPENDIR = uint32(27)
|
|
_OP_READDIR = uint32(28)
|
|
_OP_RELEASEDIR = uint32(29)
|
|
_OP_FSYNCDIR = uint32(30)
|
|
_OP_GETLK = uint32(31)
|
|
_OP_SETLK = uint32(32)
|
|
_OP_SETLKW = uint32(33)
|
|
_OP_ACCESS = uint32(34)
|
|
_OP_CREATE = uint32(35)
|
|
_OP_INTERRUPT = uint32(36)
|
|
_OP_BMAP = uint32(37)
|
|
_OP_DESTROY = uint32(38)
|
|
_OP_IOCTL = uint32(39)
|
|
_OP_POLL = uint32(40)
|
|
_OP_NOTIFY_REPLY = uint32(41)
|
|
_OP_BATCH_FORGET = uint32(42)
|
|
_OP_FALLOCATE = uint32(43) // protocol version 19.
|
|
_OP_READDIRPLUS = uint32(44) // protocol version 21.
|
|
_OP_RENAME2 = uint32(45) // protocol version 23.
|
|
_OP_LSEEK = uint32(46) // protocol version 24
|
|
_OP_COPY_FILE_RANGE = uint32(47) // protocol version 28.
|
|
|
|
// The following entries don't have to be compatible across Go-FUSE versions.
|
|
_OP_NOTIFY_INVAL_ENTRY = uint32(100)
|
|
_OP_NOTIFY_INVAL_INODE = uint32(101)
|
|
_OP_NOTIFY_STORE_CACHE = uint32(102)
|
|
_OP_NOTIFY_RETRIEVE_CACHE = uint32(103)
|
|
_OP_NOTIFY_DELETE = uint32(104) // protocol version 18
|
|
|
|
_OPCODE_COUNT = uint32(105)
|
|
)
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
func doInit(server *Server, req *request) {
|
|
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 < _MINIMUM_MINOR_VERSION {
|
|
log.Printf("Minor version is less than we support. Given %d, want at least %d\n", input.Minor, _MINIMUM_MINOR_VERSION)
|
|
req.status = EIO
|
|
return
|
|
}
|
|
|
|
server.reqMu.Lock()
|
|
server.kernelSettings = *input
|
|
server.kernelSettings.Flags = input.Flags & (CAP_ASYNC_READ | CAP_BIG_WRITES | CAP_FILE_OPS |
|
|
CAP_READDIRPLUS | CAP_NO_OPEN_SUPPORT | CAP_PARALLEL_DIROPS)
|
|
|
|
if server.opts.EnableLocks {
|
|
server.kernelSettings.Flags |= CAP_FLOCK_LOCKS | CAP_POSIX_LOCKS
|
|
}
|
|
|
|
dataCacheMode := input.Flags & CAP_AUTO_INVAL_DATA
|
|
if server.opts.ExplicitDataCacheControl {
|
|
// we don't want CAP_AUTO_INVAL_DATA even if we cannot go into fully explicit mode
|
|
dataCacheMode = 0
|
|
|
|
explicit := input.Flags & CAP_EXPLICIT_INVAL_DATA
|
|
if explicit != 0 {
|
|
dataCacheMode = explicit
|
|
}
|
|
}
|
|
server.kernelSettings.Flags |= dataCacheMode
|
|
|
|
if input.Minor >= 13 {
|
|
server.setSplice()
|
|
}
|
|
server.reqMu.Unlock()
|
|
|
|
out := (*InitOut)(req.outData())
|
|
*out = InitOut{
|
|
Major: _FUSE_KERNEL_VERSION,
|
|
Minor: _OUR_MINOR_VERSION,
|
|
MaxReadAhead: input.MaxReadAhead,
|
|
Flags: server.kernelSettings.Flags,
|
|
MaxWrite: uint32(server.opts.MaxWrite),
|
|
CongestionThreshold: uint16(server.opts.MaxBackground * 3 / 4),
|
|
MaxBackground: uint16(server.opts.MaxBackground),
|
|
}
|
|
|
|
if server.opts.MaxReadAhead != 0 && uint32(server.opts.MaxReadAhead) < out.MaxReadAhead {
|
|
out.MaxReadAhead = uint32(server.opts.MaxReadAhead)
|
|
}
|
|
if out.Minor > input.Minor {
|
|
out.Minor = input.Minor
|
|
}
|
|
|
|
if out.Minor <= 22 {
|
|
tweaked := *req.handler
|
|
|
|
// v8-v22 don't have TimeGran and further fields.
|
|
tweaked.OutputSize = 24
|
|
req.handler = &tweaked
|
|
}
|
|
|
|
req.status = OK
|
|
}
|
|
|
|
func doOpen(server *Server, req *request) {
|
|
out := (*OpenOut)(req.outData())
|
|
status := server.fileSystem.Open(req.cancel, (*OpenIn)(req.inData), out)
|
|
req.status = status
|
|
if status != OK {
|
|
return
|
|
}
|
|
}
|
|
|
|
func doCreate(server *Server, req *request) {
|
|
out := (*CreateOut)(req.outData())
|
|
status := server.fileSystem.Create(req.cancel, (*CreateIn)(req.inData), req.filenames[0], out)
|
|
req.status = status
|
|
}
|
|
|
|
func doReadDir(server *Server, req *request) {
|
|
in := (*ReadIn)(req.inData)
|
|
buf := server.allocOut(req, in.Size)
|
|
out := NewDirEntryList(buf, uint64(in.Offset))
|
|
|
|
code := server.fileSystem.ReadDir(req.cancel, in, out)
|
|
req.flatData = out.bytes()
|
|
req.status = code
|
|
}
|
|
|
|
func doReadDirPlus(server *Server, req *request) {
|
|
in := (*ReadIn)(req.inData)
|
|
buf := server.allocOut(req, in.Size)
|
|
out := NewDirEntryList(buf, uint64(in.Offset))
|
|
|
|
code := server.fileSystem.ReadDirPlus(req.cancel, in, out)
|
|
req.flatData = out.bytes()
|
|
req.status = code
|
|
}
|
|
|
|
func doOpenDir(server *Server, req *request) {
|
|
out := (*OpenOut)(req.outData())
|
|
status := server.fileSystem.OpenDir(req.cancel, (*OpenIn)(req.inData), out)
|
|
req.status = status
|
|
}
|
|
|
|
func doSetattr(server *Server, req *request) {
|
|
out := (*AttrOut)(req.outData())
|
|
req.status = server.fileSystem.SetAttr(req.cancel, (*SetAttrIn)(req.inData), out)
|
|
}
|
|
|
|
func doWrite(server *Server, req *request) {
|
|
n, status := server.fileSystem.Write(req.cancel, (*WriteIn)(req.inData), req.arg)
|
|
o := (*WriteOut)(req.outData())
|
|
o.Size = n
|
|
req.status = status
|
|
}
|
|
|
|
func doNotifyReply(server *Server, req *request) {
|
|
reply := (*NotifyRetrieveIn)(req.inData)
|
|
server.retrieveMu.Lock()
|
|
reading := server.retrieveTab[reply.Unique]
|
|
delete(server.retrieveTab, reply.Unique)
|
|
server.retrieveMu.Unlock()
|
|
|
|
badf := func(format string, argv ...interface{}) {
|
|
log.Printf("notify reply: "+format, argv...)
|
|
}
|
|
|
|
if reading == nil {
|
|
badf("unexpected unique - ignoring")
|
|
return
|
|
}
|
|
|
|
reading.n = 0
|
|
reading.st = EIO
|
|
defer close(reading.ready)
|
|
|
|
if reading.nodeid != reply.NodeId {
|
|
badf("inode mismatch: expected %s, got %s", reading.nodeid, reply.NodeId)
|
|
return
|
|
}
|
|
|
|
if reading.offset != reply.Offset {
|
|
badf("offset mismatch: expected @%d, got @%d", reading.offset, reply.Offset)
|
|
return
|
|
}
|
|
|
|
if len(reading.dest) < len(req.arg) {
|
|
badf("too much data: requested %db, got %db (will use only %db)", len(reading.dest), len(req.arg), len(reading.dest))
|
|
}
|
|
|
|
reading.n = copy(reading.dest, req.arg)
|
|
reading.st = OK
|
|
}
|
|
|
|
const _SECURITY_CAPABILITY = "security.capability"
|
|
const _SECURITY_ACL = "system.posix_acl_access"
|
|
const _SECURITY_ACL_DEFAULT = "system.posix_acl_default"
|
|
|
|
func doGetXAttr(server *Server, req *request) {
|
|
if server.opts.DisableXAttrs {
|
|
req.status = ENOSYS
|
|
return
|
|
}
|
|
|
|
if server.opts.IgnoreSecurityLabels && req.inHeader.Opcode == _OP_GETXATTR {
|
|
fn := req.filenames[0]
|
|
if fn == _SECURITY_CAPABILITY || fn == _SECURITY_ACL_DEFAULT ||
|
|
fn == _SECURITY_ACL {
|
|
req.status = ENOATTR
|
|
return
|
|
}
|
|
}
|
|
|
|
input := (*GetXAttrIn)(req.inData)
|
|
|
|
req.flatData = server.allocOut(req, input.Size)
|
|
out := (*GetXAttrOut)(req.outData())
|
|
|
|
var n uint32
|
|
switch req.inHeader.Opcode {
|
|
case _OP_GETXATTR:
|
|
n, req.status = server.fileSystem.GetXAttr(req.cancel, req.inHeader, req.filenames[0], req.flatData)
|
|
case _OP_LISTXATTR:
|
|
n, req.status = server.fileSystem.ListXAttr(req.cancel, req.inHeader, req.flatData)
|
|
default:
|
|
req.status = ENOSYS
|
|
}
|
|
|
|
if input.Size == 0 && req.status == ERANGE {
|
|
// For input.size==0, returning ERANGE is an error.
|
|
req.status = OK
|
|
out.Size = n
|
|
} else if req.status.Ok() {
|
|
req.flatData = req.flatData[:n]
|
|
out.Size = n
|
|
} else {
|
|
req.flatData = req.flatData[:0]
|
|
}
|
|
}
|
|
|
|
func doGetAttr(server *Server, req *request) {
|
|
out := (*AttrOut)(req.outData())
|
|
s := server.fileSystem.GetAttr(req.cancel, (*GetAttrIn)(req.inData), out)
|
|
req.status = s
|
|
}
|
|
|
|
// doForget - forget one NodeId
|
|
func doForget(server *Server, req *request) {
|
|
if !server.opts.RememberInodes {
|
|
server.fileSystem.Forget(req.inHeader.NodeId, (*ForgetIn)(req.inData).Nlookup)
|
|
}
|
|
}
|
|
|
|
// doBatchForget - forget a list of NodeIds
|
|
func doBatchForget(server *Server, req *request) {
|
|
in := (*_BatchForgetIn)(req.inData)
|
|
wantBytes := uintptr(in.Count) * unsafe.Sizeof(_ForgetOne{})
|
|
if uintptr(len(req.arg)) < wantBytes {
|
|
// We have no return value to complain, so log an error.
|
|
log.Printf("Too few bytes for batch forget. Got %d bytes, want %d (%d entries)",
|
|
len(req.arg), wantBytes, in.Count)
|
|
}
|
|
|
|
h := &reflect.SliceHeader{
|
|
Data: uintptr(unsafe.Pointer(&req.arg[0])),
|
|
Len: int(in.Count),
|
|
Cap: int(in.Count),
|
|
}
|
|
|
|
forgets := *(*[]_ForgetOne)(unsafe.Pointer(h))
|
|
for i, f := range forgets {
|
|
if server.opts.Debug {
|
|
log.Printf("doBatchForget: rx %d %d/%d: FORGET i%d {Nlookup=%d}",
|
|
req.inHeader.Unique, i+1, len(forgets), f.NodeId, f.Nlookup)
|
|
}
|
|
if f.NodeId == pollHackInode {
|
|
continue
|
|
}
|
|
server.fileSystem.Forget(f.NodeId, f.Nlookup)
|
|
}
|
|
}
|
|
|
|
func doReadlink(server *Server, req *request) {
|
|
req.flatData, req.status = server.fileSystem.Readlink(req.cancel, req.inHeader)
|
|
}
|
|
|
|
func doLookup(server *Server, req *request) {
|
|
out := (*EntryOut)(req.outData())
|
|
s := server.fileSystem.Lookup(req.cancel, req.inHeader, req.filenames[0], out)
|
|
req.status = s
|
|
}
|
|
|
|
func doMknod(server *Server, req *request) {
|
|
out := (*EntryOut)(req.outData())
|
|
|
|
req.status = server.fileSystem.Mknod(req.cancel, (*MknodIn)(req.inData), req.filenames[0], out)
|
|
}
|
|
|
|
func doMkdir(server *Server, req *request) {
|
|
out := (*EntryOut)(req.outData())
|
|
req.status = server.fileSystem.Mkdir(req.cancel, (*MkdirIn)(req.inData), req.filenames[0], out)
|
|
}
|
|
|
|
func doUnlink(server *Server, req *request) {
|
|
req.status = server.fileSystem.Unlink(req.cancel, req.inHeader, req.filenames[0])
|
|
}
|
|
|
|
func doRmdir(server *Server, req *request) {
|
|
req.status = server.fileSystem.Rmdir(req.cancel, req.inHeader, req.filenames[0])
|
|
}
|
|
|
|
func doLink(server *Server, req *request) {
|
|
out := (*EntryOut)(req.outData())
|
|
req.status = server.fileSystem.Link(req.cancel, (*LinkIn)(req.inData), req.filenames[0], out)
|
|
}
|
|
|
|
func doRead(server *Server, req *request) {
|
|
in := (*ReadIn)(req.inData)
|
|
buf := server.allocOut(req, in.Size)
|
|
|
|
req.readResult, req.status = server.fileSystem.Read(req.cancel, in, buf)
|
|
if fd, ok := req.readResult.(*readResultFd); ok {
|
|
req.fdData = fd
|
|
req.flatData = nil
|
|
} else if req.readResult != nil && req.status.Ok() {
|
|
req.flatData, req.status = req.readResult.Bytes(buf)
|
|
}
|
|
}
|
|
|
|
func doFlush(server *Server, req *request) {
|
|
req.status = server.fileSystem.Flush(req.cancel, (*FlushIn)(req.inData))
|
|
}
|
|
|
|
func doRelease(server *Server, req *request) {
|
|
server.fileSystem.Release(req.cancel, (*ReleaseIn)(req.inData))
|
|
}
|
|
|
|
func doFsync(server *Server, req *request) {
|
|
req.status = server.fileSystem.Fsync(req.cancel, (*FsyncIn)(req.inData))
|
|
}
|
|
|
|
func doReleaseDir(server *Server, req *request) {
|
|
server.fileSystem.ReleaseDir((*ReleaseIn)(req.inData))
|
|
}
|
|
|
|
func doFsyncDir(server *Server, req *request) {
|
|
req.status = server.fileSystem.FsyncDir(req.cancel, (*FsyncIn)(req.inData))
|
|
}
|
|
|
|
func doSetXAttr(server *Server, req *request) {
|
|
splits := bytes.SplitN(req.arg, []byte{0}, 2)
|
|
req.status = server.fileSystem.SetXAttr(req.cancel, (*SetXAttrIn)(req.inData), string(splits[0]), splits[1])
|
|
}
|
|
|
|
func doRemoveXAttr(server *Server, req *request) {
|
|
req.status = server.fileSystem.RemoveXAttr(req.cancel, req.inHeader, req.filenames[0])
|
|
}
|
|
|
|
func doAccess(server *Server, req *request) {
|
|
req.status = server.fileSystem.Access(req.cancel, (*AccessIn)(req.inData))
|
|
}
|
|
|
|
func doSymlink(server *Server, req *request) {
|
|
out := (*EntryOut)(req.outData())
|
|
req.status = server.fileSystem.Symlink(req.cancel, req.inHeader, req.filenames[1], req.filenames[0], out)
|
|
}
|
|
|
|
func doRename(server *Server, req *request) {
|
|
in1 := (*Rename1In)(req.inData)
|
|
in := RenameIn{
|
|
InHeader: in1.InHeader,
|
|
Newdir: in1.Newdir,
|
|
}
|
|
req.status = server.fileSystem.Rename(req.cancel, &in, req.filenames[0], req.filenames[1])
|
|
}
|
|
|
|
func doRename2(server *Server, req *request) {
|
|
req.status = server.fileSystem.Rename(req.cancel, (*RenameIn)(req.inData), req.filenames[0], req.filenames[1])
|
|
}
|
|
|
|
func doStatFs(server *Server, req *request) {
|
|
out := (*StatfsOut)(req.outData())
|
|
req.status = server.fileSystem.StatFs(req.cancel, req.inHeader, out)
|
|
if req.status == ENOSYS && runtime.GOOS == "darwin" {
|
|
// OSX FUSE requires Statfs to be implemented for the
|
|
// mount to succeed.
|
|
*out = StatfsOut{}
|
|
req.status = OK
|
|
}
|
|
}
|
|
|
|
func doIoctl(server *Server, req *request) {
|
|
req.status = ENOSYS
|
|
}
|
|
|
|
func doDestroy(server *Server, req *request) {
|
|
req.status = OK
|
|
}
|
|
|
|
func doFallocate(server *Server, req *request) {
|
|
req.status = server.fileSystem.Fallocate(req.cancel, (*FallocateIn)(req.inData))
|
|
}
|
|
|
|
func doGetLk(server *Server, req *request) {
|
|
req.status = server.fileSystem.GetLk(req.cancel, (*LkIn)(req.inData), (*LkOut)(req.outData()))
|
|
}
|
|
|
|
func doSetLk(server *Server, req *request) {
|
|
req.status = server.fileSystem.SetLk(req.cancel, (*LkIn)(req.inData))
|
|
}
|
|
|
|
func doSetLkw(server *Server, req *request) {
|
|
req.status = server.fileSystem.SetLkw(req.cancel, (*LkIn)(req.inData))
|
|
}
|
|
|
|
func doLseek(server *Server, req *request) {
|
|
in := (*LseekIn)(req.inData)
|
|
out := (*LseekOut)(req.outData())
|
|
req.status = server.fileSystem.Lseek(req.cancel, in, out)
|
|
}
|
|
|
|
func doCopyFileRange(server *Server, req *request) {
|
|
in := (*CopyFileRangeIn)(req.inData)
|
|
out := (*WriteOut)(req.outData())
|
|
|
|
out.Size, req.status = server.fileSystem.CopyFileRange(req.cancel, in)
|
|
}
|
|
|
|
func doInterrupt(server *Server, req *request) {
|
|
input := (*InterruptIn)(req.inData)
|
|
server.reqMu.Lock()
|
|
defer server.reqMu.Unlock()
|
|
|
|
// This is slow, but this operation is rare.
|
|
for _, inflight := range server.reqInflight {
|
|
if input.Unique == inflight.inHeader.Unique && !inflight.interrupted {
|
|
close(inflight.cancel)
|
|
inflight.interrupted = true
|
|
req.status = OK
|
|
return
|
|
}
|
|
}
|
|
|
|
// not found; wait for a bit
|
|
time.Sleep(10 * time.Microsecond)
|
|
req.status = EAGAIN
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
type operationFunc func(*Server, *request)
|
|
type castPointerFunc func(unsafe.Pointer) interface{}
|
|
|
|
type operationHandler struct {
|
|
Name string
|
|
Func operationFunc
|
|
InputSize uintptr
|
|
OutputSize uintptr
|
|
DecodeIn castPointerFunc
|
|
DecodeOut castPointerFunc
|
|
FileNames int
|
|
FileNameOut bool
|
|
}
|
|
|
|
var operationHandlers []*operationHandler
|
|
|
|
func operationName(op uint32) string {
|
|
h := getHandler(op)
|
|
if h == nil {
|
|
return "unknown"
|
|
}
|
|
return h.Name
|
|
}
|
|
|
|
func getHandler(o uint32) *operationHandler {
|
|
if o >= _OPCODE_COUNT {
|
|
return nil
|
|
}
|
|
return operationHandlers[o]
|
|
}
|
|
|
|
var maxInputSize uintptr
|
|
|
|
func init() {
|
|
operationHandlers = make([]*operationHandler, _OPCODE_COUNT)
|
|
for i := range operationHandlers {
|
|
operationHandlers[i] = &operationHandler{Name: fmt.Sprintf("OPCODE-%d", i)}
|
|
}
|
|
|
|
fileOps := []uint32{_OP_READLINK, _OP_NOTIFY_INVAL_ENTRY, _OP_NOTIFY_DELETE}
|
|
for _, op := range fileOps {
|
|
operationHandlers[op].FileNameOut = true
|
|
}
|
|
|
|
maxInputSize = 0
|
|
for op, sz := range map[uint32]uintptr{
|
|
_OP_FORGET: unsafe.Sizeof(ForgetIn{}),
|
|
_OP_BATCH_FORGET: unsafe.Sizeof(_BatchForgetIn{}),
|
|
_OP_GETATTR: unsafe.Sizeof(GetAttrIn{}),
|
|
_OP_SETATTR: unsafe.Sizeof(SetAttrIn{}),
|
|
_OP_MKNOD: unsafe.Sizeof(MknodIn{}),
|
|
_OP_MKDIR: unsafe.Sizeof(MkdirIn{}),
|
|
_OP_RENAME: unsafe.Sizeof(Rename1In{}),
|
|
_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_GETLK: unsafe.Sizeof(LkIn{}),
|
|
_OP_SETLK: unsafe.Sizeof(LkIn{}),
|
|
_OP_SETLKW: unsafe.Sizeof(LkIn{}),
|
|
_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{}),
|
|
_OP_NOTIFY_REPLY: unsafe.Sizeof(NotifyRetrieveIn{}),
|
|
_OP_FALLOCATE: unsafe.Sizeof(FallocateIn{}),
|
|
_OP_READDIRPLUS: unsafe.Sizeof(ReadIn{}),
|
|
_OP_RENAME2: unsafe.Sizeof(RenameIn{}),
|
|
_OP_LSEEK: unsafe.Sizeof(LseekIn{}),
|
|
_OP_COPY_FILE_RANGE: unsafe.Sizeof(CopyFileRangeIn{}),
|
|
} {
|
|
operationHandlers[op].InputSize = sz
|
|
if sz > maxInputSize {
|
|
maxInputSize = sz
|
|
}
|
|
}
|
|
|
|
for op, sz := range map[uint32]uintptr{
|
|
_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_GETLK: unsafe.Sizeof(LkOut{}),
|
|
_OP_CREATE: unsafe.Sizeof(CreateOut{}),
|
|
_OP_BMAP: unsafe.Sizeof(_BmapOut{}),
|
|
_OP_IOCTL: unsafe.Sizeof(_IoctlOut{}),
|
|
_OP_POLL: unsafe.Sizeof(_PollOut{}),
|
|
_OP_NOTIFY_INVAL_ENTRY: unsafe.Sizeof(NotifyInvalEntryOut{}),
|
|
_OP_NOTIFY_INVAL_INODE: unsafe.Sizeof(NotifyInvalInodeOut{}),
|
|
_OP_NOTIFY_STORE_CACHE: unsafe.Sizeof(NotifyStoreOut{}),
|
|
_OP_NOTIFY_RETRIEVE_CACHE: unsafe.Sizeof(NotifyRetrieveOut{}),
|
|
_OP_NOTIFY_DELETE: unsafe.Sizeof(NotifyInvalDeleteOut{}),
|
|
_OP_LSEEK: unsafe.Sizeof(LseekOut{}),
|
|
_OP_COPY_FILE_RANGE: unsafe.Sizeof(WriteOut{}),
|
|
} {
|
|
operationHandlers[op].OutputSize = sz
|
|
}
|
|
|
|
for op, v := range map[uint32]string{
|
|
_OP_LOOKUP: "LOOKUP",
|
|
_OP_FORGET: "FORGET",
|
|
_OP_BATCH_FORGET: "BATCH_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",
|
|
_OP_NOTIFY_REPLY: "NOTIFY_REPLY",
|
|
_OP_NOTIFY_INVAL_ENTRY: "NOTIFY_INVAL_ENTRY",
|
|
_OP_NOTIFY_INVAL_INODE: "NOTIFY_INVAL_INODE",
|
|
_OP_NOTIFY_STORE_CACHE: "NOTIFY_STORE",
|
|
_OP_NOTIFY_RETRIEVE_CACHE: "NOTIFY_RETRIEVE",
|
|
_OP_NOTIFY_DELETE: "NOTIFY_DELETE",
|
|
_OP_FALLOCATE: "FALLOCATE",
|
|
_OP_READDIRPLUS: "READDIRPLUS",
|
|
_OP_RENAME2: "RENAME2",
|
|
_OP_LSEEK: "LSEEK",
|
|
_OP_COPY_FILE_RANGE: "COPY_FILE_RANGE",
|
|
} {
|
|
operationHandlers[op].Name = v
|
|
}
|
|
|
|
for op, v := range map[uint32]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_BATCH_FORGET: doBatchForget,
|
|
_OP_READLINK: doReadlink,
|
|
_OP_INIT: doInit,
|
|
_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_GETLK: doGetLk,
|
|
_OP_SETLK: doSetLk,
|
|
_OP_SETLKW: doSetLkw,
|
|
_OP_ACCESS: doAccess,
|
|
_OP_SYMLINK: doSymlink,
|
|
_OP_RENAME: doRename,
|
|
_OP_STATFS: doStatFs,
|
|
_OP_IOCTL: doIoctl,
|
|
_OP_DESTROY: doDestroy,
|
|
_OP_NOTIFY_REPLY: doNotifyReply,
|
|
_OP_FALLOCATE: doFallocate,
|
|
_OP_READDIRPLUS: doReadDirPlus,
|
|
_OP_RENAME2: doRename2,
|
|
_OP_INTERRUPT: doInterrupt,
|
|
_OP_COPY_FILE_RANGE: doCopyFileRange,
|
|
_OP_LSEEK: doLseek,
|
|
} {
|
|
operationHandlers[op].Func = v
|
|
}
|
|
|
|
// Outputs.
|
|
for op, f := range map[uint32]castPointerFunc{
|
|
_OP_LOOKUP: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
|
|
_OP_OPEN: func(ptr unsafe.Pointer) interface{} { return (*OpenOut)(ptr) },
|
|
_OP_OPENDIR: func(ptr unsafe.Pointer) interface{} { return (*OpenOut)(ptr) },
|
|
_OP_GETATTR: func(ptr unsafe.Pointer) interface{} { return (*AttrOut)(ptr) },
|
|
_OP_CREATE: func(ptr unsafe.Pointer) interface{} { return (*CreateOut)(ptr) },
|
|
_OP_LINK: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
|
|
_OP_SETATTR: func(ptr unsafe.Pointer) interface{} { return (*AttrOut)(ptr) },
|
|
_OP_INIT: func(ptr unsafe.Pointer) interface{} { return (*InitOut)(ptr) },
|
|
_OP_MKDIR: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
|
|
_OP_NOTIFY_INVAL_ENTRY: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalEntryOut)(ptr) },
|
|
_OP_NOTIFY_INVAL_INODE: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalInodeOut)(ptr) },
|
|
_OP_NOTIFY_STORE_CACHE: func(ptr unsafe.Pointer) interface{} { return (*NotifyStoreOut)(ptr) },
|
|
_OP_NOTIFY_RETRIEVE_CACHE: func(ptr unsafe.Pointer) interface{} { return (*NotifyRetrieveOut)(ptr) },
|
|
_OP_NOTIFY_DELETE: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalDeleteOut)(ptr) },
|
|
_OP_STATFS: func(ptr unsafe.Pointer) interface{} { return (*StatfsOut)(ptr) },
|
|
_OP_SYMLINK: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
|
|
_OP_GETLK: func(ptr unsafe.Pointer) interface{} { return (*LkOut)(ptr) },
|
|
_OP_LSEEK: func(ptr unsafe.Pointer) interface{} { return (*LseekOut)(ptr) },
|
|
_OP_COPY_FILE_RANGE: func(ptr unsafe.Pointer) interface{} { return (*WriteOut)(ptr) },
|
|
} {
|
|
operationHandlers[op].DecodeOut = f
|
|
}
|
|
|
|
// Inputs.
|
|
for op, f := range map[uint32]castPointerFunc{
|
|
_OP_FLUSH: func(ptr unsafe.Pointer) interface{} { return (*FlushIn)(ptr) },
|
|
_OP_GETATTR: func(ptr unsafe.Pointer) interface{} { return (*GetAttrIn)(ptr) },
|
|
_OP_SETXATTR: func(ptr unsafe.Pointer) interface{} { return (*SetXAttrIn)(ptr) },
|
|
_OP_GETXATTR: func(ptr unsafe.Pointer) interface{} { return (*GetXAttrIn)(ptr) },
|
|
_OP_LISTXATTR: func(ptr unsafe.Pointer) interface{} { return (*GetXAttrIn)(ptr) },
|
|
_OP_SETATTR: func(ptr unsafe.Pointer) interface{} { return (*SetAttrIn)(ptr) },
|
|
_OP_INIT: func(ptr unsafe.Pointer) interface{} { return (*InitIn)(ptr) },
|
|
_OP_IOCTL: func(ptr unsafe.Pointer) interface{} { return (*_IoctlIn)(ptr) },
|
|
_OP_OPEN: func(ptr unsafe.Pointer) interface{} { return (*OpenIn)(ptr) },
|
|
_OP_MKNOD: func(ptr unsafe.Pointer) interface{} { return (*MknodIn)(ptr) },
|
|
_OP_CREATE: func(ptr unsafe.Pointer) interface{} { return (*CreateIn)(ptr) },
|
|
_OP_READ: func(ptr unsafe.Pointer) interface{} { return (*ReadIn)(ptr) },
|
|
_OP_WRITE: func(ptr unsafe.Pointer) interface{} { return (*WriteIn)(ptr) },
|
|
_OP_READDIR: func(ptr unsafe.Pointer) interface{} { return (*ReadIn)(ptr) },
|
|
_OP_ACCESS: func(ptr unsafe.Pointer) interface{} { return (*AccessIn)(ptr) },
|
|
_OP_FORGET: func(ptr unsafe.Pointer) interface{} { return (*ForgetIn)(ptr) },
|
|
_OP_BATCH_FORGET: func(ptr unsafe.Pointer) interface{} { return (*_BatchForgetIn)(ptr) },
|
|
_OP_LINK: func(ptr unsafe.Pointer) interface{} { return (*LinkIn)(ptr) },
|
|
_OP_MKDIR: func(ptr unsafe.Pointer) interface{} { return (*MkdirIn)(ptr) },
|
|
_OP_RELEASE: func(ptr unsafe.Pointer) interface{} { return (*ReleaseIn)(ptr) },
|
|
_OP_RELEASEDIR: func(ptr unsafe.Pointer) interface{} { return (*ReleaseIn)(ptr) },
|
|
_OP_FALLOCATE: func(ptr unsafe.Pointer) interface{} { return (*FallocateIn)(ptr) },
|
|
_OP_NOTIFY_REPLY: func(ptr unsafe.Pointer) interface{} { return (*NotifyRetrieveIn)(ptr) },
|
|
_OP_READDIRPLUS: func(ptr unsafe.Pointer) interface{} { return (*ReadIn)(ptr) },
|
|
_OP_RENAME: func(ptr unsafe.Pointer) interface{} { return (*Rename1In)(ptr) },
|
|
_OP_GETLK: func(ptr unsafe.Pointer) interface{} { return (*LkIn)(ptr) },
|
|
_OP_SETLK: func(ptr unsafe.Pointer) interface{} { return (*LkIn)(ptr) },
|
|
_OP_SETLKW: func(ptr unsafe.Pointer) interface{} { return (*LkIn)(ptr) },
|
|
_OP_RENAME2: func(ptr unsafe.Pointer) interface{} { return (*RenameIn)(ptr) },
|
|
_OP_INTERRUPT: func(ptr unsafe.Pointer) interface{} { return (*InterruptIn)(ptr) },
|
|
_OP_LSEEK: func(ptr unsafe.Pointer) interface{} { return (*LseekIn)(ptr) },
|
|
_OP_COPY_FILE_RANGE: func(ptr unsafe.Pointer) interface{} { return (*CopyFileRangeIn)(ptr) },
|
|
} {
|
|
operationHandlers[op].DecodeIn = f
|
|
}
|
|
|
|
// File name args.
|
|
for op, count := range map[uint32]int{
|
|
_OP_CREATE: 1,
|
|
_OP_SETXATTR: 1,
|
|
_OP_GETXATTR: 1,
|
|
_OP_LINK: 1,
|
|
_OP_LOOKUP: 1,
|
|
_OP_MKDIR: 1,
|
|
_OP_MKNOD: 1,
|
|
_OP_REMOVEXATTR: 1,
|
|
_OP_RENAME: 2,
|
|
_OP_RENAME2: 2,
|
|
_OP_RMDIR: 1,
|
|
_OP_SYMLINK: 2,
|
|
_OP_UNLINK: 1,
|
|
} {
|
|
operationHandlers[op].FileNames = count
|
|
}
|
|
|
|
var r request
|
|
sizeOfOutHeader := unsafe.Sizeof(OutHeader{})
|
|
for code, h := range operationHandlers {
|
|
if h.OutputSize+sizeOfOutHeader > unsafe.Sizeof(r.outBuf) {
|
|
log.Panicf("request output buffer too small: code %v, sz %d + %d %v", code, h.OutputSize, sizeOfOutHeader, h)
|
|
}
|
|
}
|
|
}
|