rclone/vendor/github.com/hanwen/go-fuse/v2/fuse/opcode.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)
}
}
}