containerd/vendor/github.com/hanwen/go-fuse/v2/fuse/opcode.go
Brad Davidson 890953d3c6
Enable btrfs/fuse-overlayfs/stargz snapshotter plugins
Signed-off-by: Brad Davidson <brad.davidson@rancher.com>
2025-05-06 22:38:41 +00:00

753 lines
23 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"
"runtime"
"syscall"
"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.
_OP_SETUPMAPPING = 48
_OP_REMOVEMAPPING = 49
_OP_SYNCFS = 50
_OP_TMPFILE = 51
_OP_STATX = 52
// 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)
// Constants from Linux kernel fs/fuse/fuse_i.h
// Default MaxPages value in all kernel versions
_FUSE_DEFAULT_MAX_PAGES_PER_REQ = 32
// Upper MaxPages limit in Linux v4.20+ (v4.19 and older: 32)
_FUSE_MAX_MAX_PAGES = 256
)
////////////////////////////////////////////////////////////////
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
}
kernelFlags := input.Flags64()
server.kernelSettings = *input
kernelFlags &= (CAP_ASYNC_READ | CAP_BIG_WRITES | CAP_FILE_OPS |
CAP_READDIRPLUS | CAP_NO_OPEN_SUPPORT | CAP_PARALLEL_DIROPS | CAP_MAX_PAGES | CAP_RENAME_SWAP | CAP_PASSTHROUGH)
if server.opts.EnableLocks {
kernelFlags |= CAP_FLOCK_LOCKS | CAP_POSIX_LOCKS
}
if server.opts.EnableSymlinkCaching {
kernelFlags |= CAP_CACHE_SYMLINKS
}
if server.opts.EnableAcl {
kernelFlags |= CAP_POSIX_ACL
}
if server.opts.SyncRead {
// Clear CAP_ASYNC_READ
kernelFlags &= ^uint64(CAP_ASYNC_READ)
}
if server.opts.DisableReadDirPlus {
// Clear CAP_READDIRPLUS
kernelFlags &= ^uint64(CAP_READDIRPLUS)
}
dataCacheMode := kernelFlags & 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 := kernelFlags & CAP_EXPLICIT_INVAL_DATA
if explicit != 0 {
dataCacheMode = explicit
}
}
kernelFlags |= dataCacheMode
// maxPages is the maximum request size we want the kernel to use, in units of
// memory pages (usually 4kiB). Linux v4.19 and older ignore this and always use
// 128kiB.
maxPages := (server.opts.MaxWrite-1)/syscall.Getpagesize() + 1 // Round up
out := (*InitOut)(req.outData())
*out = InitOut{
Major: _FUSE_KERNEL_VERSION,
Minor: _OUR_MINOR_VERSION,
MaxReadAhead: input.MaxReadAhead,
MaxWrite: uint32(server.opts.MaxWrite),
CongestionThreshold: uint16(server.opts.MaxBackground * 3 / 4),
MaxBackground: uint16(server.opts.MaxBackground),
MaxPages: uint16(maxPages),
MaxStackDepth: 1,
}
out.setFlags(kernelFlags)
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
}
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.filename(), out)
req.status = status
}
func doReadDir(server *Server, req *request) {
in := (*ReadIn)(req.inData())
out := NewDirEntryList(req.outPayload, uint64(in.Offset))
code := server.fileSystem.ReadDir(req.cancel, in, out)
req.outPayload = out.bytes()
req.status = code
}
func doReadDirPlus(server *Server, req *request) {
in := (*ReadIn)(req.inData())
out := NewDirEntryList(req.outPayload, uint64(in.Offset))
code := server.fileSystem.ReadDirPlus(req.cancel, in, out)
req.outPayload = 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.inPayload)
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{}) {
server.opts.Logger.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.inPayload) {
badf("too much data: requested %db, got %db (will use only %db)", len(reading.dest), len(req.inPayload), len(reading.dest))
}
reading.n = copy(reading.dest, req.inPayload)
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.filename()
if fn == _SECURITY_CAPABILITY || fn == _SECURITY_ACL_DEFAULT ||
fn == _SECURITY_ACL {
req.status = ENOATTR
return
}
}
input := (*GetXAttrIn)(req.inData())
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.filename(), req.outPayload)
case _OP_LISTXATTR:
n, req.status = server.fileSystem.ListXAttr(req.cancel, req.inHeader(), req.outPayload)
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() {
// ListXAttr called with an empty buffer returns the current size of
// the list but does not touch the buffer (see man 2 listxattr).
if len(req.outPayload) > 0 {
req.outPayload = req.outPayload[:n]
}
out.Size = n
} else {
req.outPayload = req.outPayload[: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.inPayload)) < wantBytes {
// We have no return value to complain, so log an error.
server.opts.Logger.Printf("Too few bytes for batch forget. Got %d bytes, want %d (%d entries)",
len(req.inPayload), wantBytes, in.Count)
}
forgets := unsafe.Slice((*_ForgetOne)(unsafe.Pointer(&req.inPayload[0])), in.Count)
for i, f := range forgets {
if server.opts.Debug {
server.opts.Logger.Printf("doBatchForget: rx %d %d/%d: FORGET n%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.outPayload, req.status = server.fileSystem.Readlink(req.cancel, req.inHeader())
}
func doLookup(server *Server, req *request) {
out := (*EntryOut)(req.outData())
req.status = server.fileSystem.Lookup(req.cancel, req.inHeader(), req.filename(), out)
}
func doMknod(server *Server, req *request) {
out := (*EntryOut)(req.outData())
req.status = server.fileSystem.Mknod(req.cancel, (*MknodIn)(req.inData()), req.filename(), out)
}
func doMkdir(server *Server, req *request) {
out := (*EntryOut)(req.outData())
req.status = server.fileSystem.Mkdir(req.cancel, (*MkdirIn)(req.inData()), req.filename(), out)
}
func doUnlink(server *Server, req *request) {
req.status = server.fileSystem.Unlink(req.cancel, req.inHeader(), req.filename())
}
func doRmdir(server *Server, req *request) {
req.status = server.fileSystem.Rmdir(req.cancel, req.inHeader(), req.filename())
}
func doLink(server *Server, req *request) {
out := (*EntryOut)(req.outData())
req.status = server.fileSystem.Link(req.cancel, (*LinkIn)(req.inData()), req.filename(), out)
}
func doRead(server *Server, req *request) {
in := (*ReadIn)(req.inData())
req.readResult, req.status = server.fileSystem.Read(req.cancel, in, req.outPayload)
if fd, ok := req.readResult.(*readResultFd); ok {
req.fdData = fd
} else if req.readResult != nil && req.status.Ok() {
req.outPayload, req.status = req.readResult.Bytes(req.outPayload)
}
}
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) {
i := bytes.IndexByte(req.inPayload, 0)
req.status = server.fileSystem.SetXAttr(req.cancel, (*SetXAttrIn)(req.inData()), string(req.inPayload[:i]), req.inPayload[i+1:])
}
func doRemoveXAttr(server *Server, req *request) {
req.status = server.fileSystem.RemoveXAttr(req.cancel, req.inHeader(), req.filename())
}
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())
n1, n2 := req.filenames()
req.status = server.fileSystem.Symlink(req.cancel, req.inHeader(), n2, n1, out)
}
func doRename(server *Server, req *request) {
if server.kernelSettings.supportsRenameSwap() {
doRename2(server, req)
return
}
in1 := (*Rename1In)(req.inData())
in := RenameIn{
InHeader: in1.InHeader,
Newdir: in1.Newdir,
}
n1, n2 := req.filenames()
req.status = server.fileSystem.Rename(req.cancel, &in, n1, n2)
}
func doRename2(server *Server, req *request) {
n1, n2 := req.filenames()
req.status = server.fileSystem.Rename(req.cancel, (*RenameIn)(req.inData()), n1, n2)
}
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 = Status(syscall.ENOTTY)
}
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())
req.status = server.interruptRequest(input.Unique)
}
////////////////////////////////////////////////////////////////
type operationFunc func(*Server, *request)
type castPointerFunc func(unsafe.Pointer) interface{}
type operationHandler struct {
Name string
Func operationFunc
InputSize uintptr
OutputSize uintptr
InType interface{}
OutType interface{}
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]
}
// maximum size of all input headers
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
}
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",
_OP_SETUPMAPPING: "SETUPMAPPING",
_OP_REMOVEMAPPING: "REMOVEMAPPING",
_OP_SYNCFS: "SYNCFS",
_OP_TMPFILE: "TMPFILE",
} {
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]interface{}{
_OP_BMAP: _BmapOut{},
_OP_COPY_FILE_RANGE: WriteOut{},
_OP_CREATE: CreateOut{},
_OP_GETATTR: AttrOut{},
_OP_GETLK: LkOut{},
_OP_GETXATTR: GetXAttrOut{},
_OP_INIT: InitOut{},
_OP_IOCTL: _IoctlOut{},
_OP_LINK: EntryOut{},
_OP_LISTXATTR: GetXAttrOut{},
_OP_LOOKUP: EntryOut{},
_OP_LSEEK: LseekOut{},
_OP_MKDIR: EntryOut{},
_OP_MKNOD: EntryOut{},
_OP_NOTIFY_DELETE: NotifyInvalDeleteOut{},
_OP_NOTIFY_INVAL_ENTRY: NotifyInvalEntryOut{},
_OP_NOTIFY_INVAL_INODE: NotifyInvalInodeOut{},
_OP_NOTIFY_RETRIEVE_CACHE: NotifyRetrieveOut{},
_OP_NOTIFY_STORE_CACHE: NotifyStoreOut{},
_OP_OPEN: OpenOut{},
_OP_OPENDIR: OpenOut{},
_OP_POLL: _PollOut{},
_OP_SETATTR: AttrOut{},
_OP_STATFS: StatfsOut{},
_OP_SYMLINK: EntryOut{},
_OP_WRITE: WriteOut{},
} {
operationHandlers[op].OutType = f
operationHandlers[op].OutputSize = typSize(f)
}
// Inputs.
for op, f := range map[uint32]interface{}{
_OP_ACCESS: AccessIn{},
_OP_BATCH_FORGET: _BatchForgetIn{},
_OP_BMAP: _BmapIn{},
_OP_COPY_FILE_RANGE: CopyFileRangeIn{},
_OP_CREATE: CreateIn{},
_OP_FALLOCATE: FallocateIn{},
_OP_FLUSH: FlushIn{},
_OP_FORGET: ForgetIn{},
_OP_FSYNC: FsyncIn{},
_OP_FSYNCDIR: FsyncIn{},
_OP_GETATTR: GetAttrIn{},
_OP_GETLK: LkIn{},
_OP_GETXATTR: GetXAttrIn{},
_OP_INIT: InitIn{},
_OP_INTERRUPT: InterruptIn{},
_OP_IOCTL: _IoctlIn{},
_OP_LINK: LinkIn{},
_OP_LISTXATTR: GetXAttrIn{},
_OP_LSEEK: LseekIn{},
_OP_MKDIR: MkdirIn{},
_OP_MKNOD: MknodIn{},
_OP_NOTIFY_REPLY: NotifyRetrieveIn{},
_OP_OPEN: OpenIn{},
_OP_OPENDIR: OpenIn{},
_OP_POLL: _PollIn{},
_OP_READ: ReadIn{},
_OP_READDIR: ReadIn{},
_OP_READDIRPLUS: ReadIn{},
_OP_RELEASE: ReleaseIn{},
_OP_RELEASEDIR: ReleaseIn{},
_OP_RENAME2: RenameIn{},
_OP_RENAME: Rename1In{},
_OP_SETATTR: SetAttrIn{},
_OP_SETLK: LkIn{},
_OP_SETLKW: LkIn{},
_OP_SETXATTR: SetXAttrIn{},
_OP_WRITE: WriteIn{},
} {
operationHandlers[op].InType = f
sz := typSize(f)
operationHandlers[op].InputSize = sz
if maxInputSize < sz {
maxInputSize = sz
}
}
// 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 requestAlloc
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)
}
}
}