github.com/hanwen/go-fuse@v1.0.0/fuse/opcode.go (about)

     1  // Copyright 2016 the Go-FUSE Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package fuse
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"log"
    11  	"reflect"
    12  	"runtime"
    13  	"unsafe"
    14  )
    15  
    16  const (
    17  	_OP_LOOKUP       = int32(1)
    18  	_OP_FORGET       = int32(2)
    19  	_OP_GETATTR      = int32(3)
    20  	_OP_SETATTR      = int32(4)
    21  	_OP_READLINK     = int32(5)
    22  	_OP_SYMLINK      = int32(6)
    23  	_OP_MKNOD        = int32(8)
    24  	_OP_MKDIR        = int32(9)
    25  	_OP_UNLINK       = int32(10)
    26  	_OP_RMDIR        = int32(11)
    27  	_OP_RENAME       = int32(12)
    28  	_OP_LINK         = int32(13)
    29  	_OP_OPEN         = int32(14)
    30  	_OP_READ         = int32(15)
    31  	_OP_WRITE        = int32(16)
    32  	_OP_STATFS       = int32(17)
    33  	_OP_RELEASE      = int32(18)
    34  	_OP_FSYNC        = int32(20)
    35  	_OP_SETXATTR     = int32(21)
    36  	_OP_GETXATTR     = int32(22)
    37  	_OP_LISTXATTR    = int32(23)
    38  	_OP_REMOVEXATTR  = int32(24)
    39  	_OP_FLUSH        = int32(25)
    40  	_OP_INIT         = int32(26)
    41  	_OP_OPENDIR      = int32(27)
    42  	_OP_READDIR      = int32(28)
    43  	_OP_RELEASEDIR   = int32(29)
    44  	_OP_FSYNCDIR     = int32(30)
    45  	_OP_GETLK        = int32(31)
    46  	_OP_SETLK        = int32(32)
    47  	_OP_SETLKW       = int32(33)
    48  	_OP_ACCESS       = int32(34)
    49  	_OP_CREATE       = int32(35)
    50  	_OP_INTERRUPT    = int32(36)
    51  	_OP_BMAP         = int32(37)
    52  	_OP_DESTROY      = int32(38)
    53  	_OP_IOCTL        = int32(39)
    54  	_OP_POLL         = int32(40)
    55  	_OP_NOTIFY_REPLY = int32(41)
    56  	_OP_BATCH_FORGET = int32(42)
    57  	_OP_FALLOCATE    = int32(43) // protocol version 19.
    58  	_OP_READDIRPLUS  = int32(44) // protocol version 21.
    59  	_OP_RENAME2      = int32(45) // protocol version 23.
    60  
    61  	// The following entries don't have to be compatible across Go-FUSE versions.
    62  	_OP_NOTIFY_INVAL_ENTRY    = int32(100)
    63  	_OP_NOTIFY_INVAL_INODE    = int32(101)
    64  	_OP_NOTIFY_STORE_CACHE    = int32(102)
    65  	_OP_NOTIFY_RETRIEVE_CACHE = int32(103)
    66  	_OP_NOTIFY_DELETE         = int32(104) // protocol version 18
    67  
    68  	_OPCODE_COUNT = int32(105)
    69  )
    70  
    71  ////////////////////////////////////////////////////////////////
    72  
    73  func doInit(server *Server, req *request) {
    74  	input := (*InitIn)(req.inData)
    75  	if input.Major != _FUSE_KERNEL_VERSION {
    76  		log.Printf("Major versions does not match. Given %d, want %d\n", input.Major, _FUSE_KERNEL_VERSION)
    77  		req.status = EIO
    78  		return
    79  	}
    80  	if input.Minor < _MINIMUM_MINOR_VERSION {
    81  		log.Printf("Minor version is less than we support. Given %d, want at least %d\n", input.Minor, _MINIMUM_MINOR_VERSION)
    82  		req.status = EIO
    83  		return
    84  	}
    85  
    86  	server.reqMu.Lock()
    87  	server.kernelSettings = *input
    88  	server.kernelSettings.Flags = input.Flags & (CAP_ASYNC_READ | CAP_BIG_WRITES | CAP_FILE_OPS |
    89  		CAP_AUTO_INVAL_DATA | CAP_READDIRPLUS | CAP_NO_OPEN_SUPPORT | CAP_PARALLEL_DIROPS)
    90  
    91  	if server.opts.EnableLocks {
    92  		server.kernelSettings.Flags |= CAP_FLOCK_LOCKS | CAP_POSIX_LOCKS
    93  	}
    94  
    95  	if input.Minor >= 13 {
    96  		server.setSplice()
    97  	}
    98  	server.reqMu.Unlock()
    99  
   100  	out := (*InitOut)(req.outData())
   101  	*out = InitOut{
   102  		Major:               _FUSE_KERNEL_VERSION,
   103  		Minor:               _OUR_MINOR_VERSION,
   104  		MaxReadAhead:        input.MaxReadAhead,
   105  		Flags:               server.kernelSettings.Flags,
   106  		MaxWrite:            uint32(server.opts.MaxWrite),
   107  		CongestionThreshold: uint16(server.opts.MaxBackground * 3 / 4),
   108  		MaxBackground:       uint16(server.opts.MaxBackground),
   109  	}
   110  
   111  	if server.opts.MaxReadAhead != 0 && uint32(server.opts.MaxReadAhead) < out.MaxReadAhead {
   112  		out.MaxReadAhead = uint32(server.opts.MaxReadAhead)
   113  	}
   114  	if out.Minor > input.Minor {
   115  		out.Minor = input.Minor
   116  	}
   117  
   118  	if out.Minor <= 22 {
   119  		tweaked := *req.handler
   120  
   121  		// v8-v22 don't have TimeGran and further fields.
   122  		tweaked.OutputSize = 24
   123  		req.handler = &tweaked
   124  	}
   125  
   126  	req.status = OK
   127  }
   128  
   129  func doOpen(server *Server, req *request) {
   130  	out := (*OpenOut)(req.outData())
   131  	status := server.fileSystem.Open((*OpenIn)(req.inData), out)
   132  	req.status = status
   133  	if status != OK {
   134  		return
   135  	}
   136  }
   137  
   138  func doCreate(server *Server, req *request) {
   139  	out := (*CreateOut)(req.outData())
   140  	status := server.fileSystem.Create((*CreateIn)(req.inData), req.filenames[0], out)
   141  	req.status = status
   142  }
   143  
   144  func doReadDir(server *Server, req *request) {
   145  	in := (*ReadIn)(req.inData)
   146  	buf := server.allocOut(req, in.Size)
   147  	out := NewDirEntryList(buf, uint64(in.Offset))
   148  
   149  	code := server.fileSystem.ReadDir(in, out)
   150  	req.flatData = out.bytes()
   151  	req.status = code
   152  }
   153  
   154  func doReadDirPlus(server *Server, req *request) {
   155  	in := (*ReadIn)(req.inData)
   156  	buf := server.allocOut(req, in.Size)
   157  	out := NewDirEntryList(buf, uint64(in.Offset))
   158  
   159  	code := server.fileSystem.ReadDirPlus(in, out)
   160  	req.flatData = out.bytes()
   161  	req.status = code
   162  }
   163  
   164  func doOpenDir(server *Server, req *request) {
   165  	out := (*OpenOut)(req.outData())
   166  	status := server.fileSystem.OpenDir((*OpenIn)(req.inData), out)
   167  	req.status = status
   168  }
   169  
   170  func doSetattr(server *Server, req *request) {
   171  	out := (*AttrOut)(req.outData())
   172  	req.status = server.fileSystem.SetAttr((*SetAttrIn)(req.inData), out)
   173  }
   174  
   175  func doWrite(server *Server, req *request) {
   176  	n, status := server.fileSystem.Write((*WriteIn)(req.inData), req.arg)
   177  	o := (*WriteOut)(req.outData())
   178  	o.Size = n
   179  	req.status = status
   180  }
   181  
   182  func doNotifyReply(server *Server, req *request) {
   183  	reply := (*NotifyRetrieveIn)(req.inData)
   184  	server.retrieveMu.Lock()
   185  	reading := server.retrieveTab[reply.Unique]
   186  	delete(server.retrieveTab, reply.Unique)
   187  	server.retrieveMu.Unlock()
   188  
   189  	badf := func(format string, argv ...interface{}) {
   190  		log.Printf("notify reply: "+format, argv...)
   191  	}
   192  
   193  	if reading == nil {
   194  		badf("unexpected unique - ignoring")
   195  		return
   196  	}
   197  
   198  	reading.n = 0
   199  	reading.st = EIO
   200  	defer close(reading.ready)
   201  
   202  	if reading.nodeid != reply.NodeId {
   203  		badf("inode mismatch: expected %s, got %s", reading.nodeid, reply.NodeId)
   204  		return
   205  	}
   206  
   207  	if reading.offset != reply.Offset {
   208  		badf("offset mismatch: expected @%d, got @%d", reading.offset, reply.Offset)
   209  		return
   210  	}
   211  
   212  	if len(reading.dest) < len(req.arg) {
   213  		badf("too much data: requested %db, got %db (will use only %db)", len(reading.dest), len(req.arg), len(reading.dest))
   214  	}
   215  
   216  	reading.n = copy(reading.dest, req.arg)
   217  	reading.st = OK
   218  }
   219  
   220  const _SECURITY_CAPABILITY = "security.capability"
   221  const _SECURITY_ACL = "system.posix_acl_access"
   222  const _SECURITY_ACL_DEFAULT = "system.posix_acl_default"
   223  
   224  func doGetXAttr(server *Server, req *request) {
   225  	if server.opts.DisableXAttrs {
   226  		req.status = ENOSYS
   227  		return
   228  	}
   229  
   230  	if server.opts.IgnoreSecurityLabels && req.inHeader.Opcode == _OP_GETXATTR {
   231  		fn := req.filenames[0]
   232  		if fn == _SECURITY_CAPABILITY || fn == _SECURITY_ACL_DEFAULT ||
   233  			fn == _SECURITY_ACL {
   234  			req.status = ENOATTR
   235  			return
   236  		}
   237  	}
   238  
   239  	input := (*GetXAttrIn)(req.inData)
   240  
   241  	if input.Size == 0 {
   242  		out := (*GetXAttrOut)(req.outData())
   243  		switch req.inHeader.Opcode {
   244  		case _OP_GETXATTR:
   245  			// TODO(hanwen): double check this. For getxattr, input.Size
   246  			// field refers to the size of the attribute, so it usually
   247  			// is not 0.
   248  			sz, code := server.fileSystem.GetXAttrSize(req.inHeader, req.filenames[0])
   249  			if code.Ok() {
   250  				out.Size = uint32(sz)
   251  			}
   252  			req.status = code
   253  			return
   254  		case _OP_LISTXATTR:
   255  			data, code := server.fileSystem.ListXAttr(req.inHeader)
   256  			if code.Ok() {
   257  				out.Size = uint32(len(data))
   258  			}
   259  			req.status = code
   260  			return
   261  		}
   262  	}
   263  	var data []byte
   264  	switch req.inHeader.Opcode {
   265  	case _OP_GETXATTR:
   266  		data, req.status = server.fileSystem.GetXAttrData(req.inHeader, req.filenames[0])
   267  	case _OP_LISTXATTR:
   268  		data, req.status = server.fileSystem.ListXAttr(req.inHeader)
   269  	default:
   270  		log.Panicf("xattr opcode %v", req.inHeader.Opcode)
   271  		req.status = ENOSYS
   272  	}
   273  
   274  	if len(data) > int(input.Size) {
   275  		req.status = ERANGE
   276  	}
   277  
   278  	if !req.status.Ok() {
   279  		return
   280  	}
   281  
   282  	req.flatData = data
   283  }
   284  
   285  func doGetAttr(server *Server, req *request) {
   286  	out := (*AttrOut)(req.outData())
   287  	s := server.fileSystem.GetAttr((*GetAttrIn)(req.inData), out)
   288  	req.status = s
   289  }
   290  
   291  // doForget - forget one NodeId
   292  func doForget(server *Server, req *request) {
   293  	if !server.opts.RememberInodes {
   294  		server.fileSystem.Forget(req.inHeader.NodeId, (*ForgetIn)(req.inData).Nlookup)
   295  	}
   296  }
   297  
   298  // doBatchForget - forget a list of NodeIds
   299  func doBatchForget(server *Server, req *request) {
   300  	in := (*_BatchForgetIn)(req.inData)
   301  	wantBytes := uintptr(in.Count) * unsafe.Sizeof(_ForgetOne{})
   302  	if uintptr(len(req.arg)) < wantBytes {
   303  		// We have no return value to complain, so log an error.
   304  		log.Printf("Too few bytes for batch forget. Got %d bytes, want %d (%d entries)",
   305  			len(req.arg), wantBytes, in.Count)
   306  	}
   307  
   308  	h := &reflect.SliceHeader{
   309  		Data: uintptr(unsafe.Pointer(&req.arg[0])),
   310  		Len:  int(in.Count),
   311  		Cap:  int(in.Count),
   312  	}
   313  
   314  	forgets := *(*[]_ForgetOne)(unsafe.Pointer(h))
   315  	for i, f := range forgets {
   316  		if server.opts.Debug {
   317  			log.Printf("doBatchForget: rx %d %d/%d: FORGET i%d {Nlookup=%d}",
   318  				req.inHeader.Unique, i+1, len(forgets), f.NodeId, f.Nlookup)
   319  		}
   320  		if f.NodeId == pollHackInode {
   321  			continue
   322  		}
   323  		server.fileSystem.Forget(f.NodeId, f.Nlookup)
   324  	}
   325  }
   326  
   327  func doReadlink(server *Server, req *request) {
   328  	req.flatData, req.status = server.fileSystem.Readlink(req.inHeader)
   329  }
   330  
   331  func doLookup(server *Server, req *request) {
   332  	out := (*EntryOut)(req.outData())
   333  	s := server.fileSystem.Lookup(req.inHeader, req.filenames[0], out)
   334  	req.status = s
   335  }
   336  
   337  func doMknod(server *Server, req *request) {
   338  	out := (*EntryOut)(req.outData())
   339  
   340  	req.status = server.fileSystem.Mknod((*MknodIn)(req.inData), req.filenames[0], out)
   341  }
   342  
   343  func doMkdir(server *Server, req *request) {
   344  	out := (*EntryOut)(req.outData())
   345  	req.status = server.fileSystem.Mkdir((*MkdirIn)(req.inData), req.filenames[0], out)
   346  }
   347  
   348  func doUnlink(server *Server, req *request) {
   349  	req.status = server.fileSystem.Unlink(req.inHeader, req.filenames[0])
   350  }
   351  
   352  func doRmdir(server *Server, req *request) {
   353  	req.status = server.fileSystem.Rmdir(req.inHeader, req.filenames[0])
   354  }
   355  
   356  func doLink(server *Server, req *request) {
   357  	out := (*EntryOut)(req.outData())
   358  	req.status = server.fileSystem.Link((*LinkIn)(req.inData), req.filenames[0], out)
   359  }
   360  
   361  func doRead(server *Server, req *request) {
   362  	in := (*ReadIn)(req.inData)
   363  	buf := server.allocOut(req, in.Size)
   364  
   365  	req.readResult, req.status = server.fileSystem.Read(in, buf)
   366  	if fd, ok := req.readResult.(*readResultFd); ok {
   367  		req.fdData = fd
   368  		req.flatData = nil
   369  	} else if req.readResult != nil && req.status.Ok() {
   370  		req.flatData, req.status = req.readResult.Bytes(buf)
   371  	}
   372  }
   373  
   374  func doFlush(server *Server, req *request) {
   375  	req.status = server.fileSystem.Flush((*FlushIn)(req.inData))
   376  }
   377  
   378  func doRelease(server *Server, req *request) {
   379  	server.fileSystem.Release((*ReleaseIn)(req.inData))
   380  }
   381  
   382  func doFsync(server *Server, req *request) {
   383  	req.status = server.fileSystem.Fsync((*FsyncIn)(req.inData))
   384  }
   385  
   386  func doReleaseDir(server *Server, req *request) {
   387  	server.fileSystem.ReleaseDir((*ReleaseIn)(req.inData))
   388  }
   389  
   390  func doFsyncDir(server *Server, req *request) {
   391  	req.status = server.fileSystem.FsyncDir((*FsyncIn)(req.inData))
   392  }
   393  
   394  func doSetXAttr(server *Server, req *request) {
   395  	splits := bytes.SplitN(req.arg, []byte{0}, 2)
   396  	req.status = server.fileSystem.SetXAttr((*SetXAttrIn)(req.inData), string(splits[0]), splits[1])
   397  }
   398  
   399  func doRemoveXAttr(server *Server, req *request) {
   400  	req.status = server.fileSystem.RemoveXAttr(req.inHeader, req.filenames[0])
   401  }
   402  
   403  func doAccess(server *Server, req *request) {
   404  	req.status = server.fileSystem.Access((*AccessIn)(req.inData))
   405  }
   406  
   407  func doSymlink(server *Server, req *request) {
   408  	out := (*EntryOut)(req.outData())
   409  	req.status = server.fileSystem.Symlink(req.inHeader, req.filenames[1], req.filenames[0], out)
   410  }
   411  
   412  func doRename(server *Server, req *request) {
   413  	in1 := (*Rename1In)(req.inData)
   414  	in := RenameIn{
   415  		InHeader: in1.InHeader,
   416  		Newdir:   in1.Newdir,
   417  	}
   418  	req.status = server.fileSystem.Rename(&in, req.filenames[0], req.filenames[1])
   419  }
   420  
   421  func doRename2(server *Server, req *request) {
   422  	req.status = server.fileSystem.Rename((*RenameIn)(req.inData), req.filenames[0], req.filenames[1])
   423  }
   424  
   425  func doStatFs(server *Server, req *request) {
   426  	out := (*StatfsOut)(req.outData())
   427  	req.status = server.fileSystem.StatFs(req.inHeader, out)
   428  	if req.status == ENOSYS && runtime.GOOS == "darwin" {
   429  		// OSX FUSE requires Statfs to be implemented for the
   430  		// mount to succeed.
   431  		*out = StatfsOut{}
   432  		req.status = OK
   433  	}
   434  }
   435  
   436  func doIoctl(server *Server, req *request) {
   437  	req.status = ENOSYS
   438  }
   439  
   440  func doDestroy(server *Server, req *request) {
   441  	req.status = OK
   442  }
   443  
   444  func doFallocate(server *Server, req *request) {
   445  	req.status = server.fileSystem.Fallocate((*FallocateIn)(req.inData))
   446  }
   447  
   448  func doGetLk(server *Server, req *request) {
   449  	req.status = server.fileSystem.GetLk((*LkIn)(req.inData), (*LkOut)(req.outData()))
   450  }
   451  
   452  func doSetLk(server *Server, req *request) {
   453  	req.status = server.fileSystem.SetLk((*LkIn)(req.inData))
   454  }
   455  
   456  func doSetLkw(server *Server, req *request) {
   457  	req.status = server.fileSystem.SetLkw((*LkIn)(req.inData))
   458  }
   459  
   460  ////////////////////////////////////////////////////////////////
   461  
   462  type operationFunc func(*Server, *request)
   463  type castPointerFunc func(unsafe.Pointer) interface{}
   464  
   465  type operationHandler struct {
   466  	Name        string
   467  	Func        operationFunc
   468  	InputSize   uintptr
   469  	OutputSize  uintptr
   470  	DecodeIn    castPointerFunc
   471  	DecodeOut   castPointerFunc
   472  	FileNames   int
   473  	FileNameOut bool
   474  }
   475  
   476  var operationHandlers []*operationHandler
   477  
   478  func operationName(op int32) string {
   479  	h := getHandler(op)
   480  	if h == nil {
   481  		return "unknown"
   482  	}
   483  	return h.Name
   484  }
   485  
   486  func getHandler(o int32) *operationHandler {
   487  	if o >= _OPCODE_COUNT {
   488  		return nil
   489  	}
   490  	return operationHandlers[o]
   491  }
   492  
   493  func init() {
   494  	operationHandlers = make([]*operationHandler, _OPCODE_COUNT)
   495  	for i := range operationHandlers {
   496  		operationHandlers[i] = &operationHandler{Name: fmt.Sprintf("OPCODE-%d", i)}
   497  	}
   498  
   499  	fileOps := []int32{_OP_READLINK, _OP_NOTIFY_INVAL_ENTRY, _OP_NOTIFY_DELETE}
   500  	for _, op := range fileOps {
   501  		operationHandlers[op].FileNameOut = true
   502  	}
   503  
   504  	for op, sz := range map[int32]uintptr{
   505  		_OP_FORGET:       unsafe.Sizeof(ForgetIn{}),
   506  		_OP_BATCH_FORGET: unsafe.Sizeof(_BatchForgetIn{}),
   507  		_OP_GETATTR:      unsafe.Sizeof(GetAttrIn{}),
   508  		_OP_SETATTR:      unsafe.Sizeof(SetAttrIn{}),
   509  		_OP_MKNOD:        unsafe.Sizeof(MknodIn{}),
   510  		_OP_MKDIR:        unsafe.Sizeof(MkdirIn{}),
   511  		_OP_RENAME:       unsafe.Sizeof(Rename1In{}),
   512  		_OP_LINK:         unsafe.Sizeof(LinkIn{}),
   513  		_OP_OPEN:         unsafe.Sizeof(OpenIn{}),
   514  		_OP_READ:         unsafe.Sizeof(ReadIn{}),
   515  		_OP_WRITE:        unsafe.Sizeof(WriteIn{}),
   516  		_OP_RELEASE:      unsafe.Sizeof(ReleaseIn{}),
   517  		_OP_FSYNC:        unsafe.Sizeof(FsyncIn{}),
   518  		_OP_SETXATTR:     unsafe.Sizeof(SetXAttrIn{}),
   519  		_OP_GETXATTR:     unsafe.Sizeof(GetXAttrIn{}),
   520  		_OP_LISTXATTR:    unsafe.Sizeof(GetXAttrIn{}),
   521  		_OP_FLUSH:        unsafe.Sizeof(FlushIn{}),
   522  		_OP_INIT:         unsafe.Sizeof(InitIn{}),
   523  		_OP_OPENDIR:      unsafe.Sizeof(OpenIn{}),
   524  		_OP_READDIR:      unsafe.Sizeof(ReadIn{}),
   525  		_OP_RELEASEDIR:   unsafe.Sizeof(ReleaseIn{}),
   526  		_OP_FSYNCDIR:     unsafe.Sizeof(FsyncIn{}),
   527  		_OP_GETLK:        unsafe.Sizeof(LkIn{}),
   528  		_OP_SETLK:        unsafe.Sizeof(LkIn{}),
   529  		_OP_SETLKW:       unsafe.Sizeof(LkIn{}),
   530  		_OP_ACCESS:       unsafe.Sizeof(AccessIn{}),
   531  		_OP_CREATE:       unsafe.Sizeof(CreateIn{}),
   532  		_OP_INTERRUPT:    unsafe.Sizeof(InterruptIn{}),
   533  		_OP_BMAP:         unsafe.Sizeof(_BmapIn{}),
   534  		_OP_IOCTL:        unsafe.Sizeof(_IoctlIn{}),
   535  		_OP_POLL:         unsafe.Sizeof(_PollIn{}),
   536  		_OP_NOTIFY_REPLY: unsafe.Sizeof(NotifyRetrieveIn{}),
   537  		_OP_FALLOCATE:    unsafe.Sizeof(FallocateIn{}),
   538  		_OP_READDIRPLUS:  unsafe.Sizeof(ReadIn{}),
   539  		_OP_RENAME2:      unsafe.Sizeof(RenameIn{}),
   540  	} {
   541  		operationHandlers[op].InputSize = sz
   542  	}
   543  
   544  	for op, sz := range map[int32]uintptr{
   545  		_OP_LOOKUP:                unsafe.Sizeof(EntryOut{}),
   546  		_OP_GETATTR:               unsafe.Sizeof(AttrOut{}),
   547  		_OP_SETATTR:               unsafe.Sizeof(AttrOut{}),
   548  		_OP_SYMLINK:               unsafe.Sizeof(EntryOut{}),
   549  		_OP_MKNOD:                 unsafe.Sizeof(EntryOut{}),
   550  		_OP_MKDIR:                 unsafe.Sizeof(EntryOut{}),
   551  		_OP_LINK:                  unsafe.Sizeof(EntryOut{}),
   552  		_OP_OPEN:                  unsafe.Sizeof(OpenOut{}),
   553  		_OP_WRITE:                 unsafe.Sizeof(WriteOut{}),
   554  		_OP_STATFS:                unsafe.Sizeof(StatfsOut{}),
   555  		_OP_GETXATTR:              unsafe.Sizeof(GetXAttrOut{}),
   556  		_OP_LISTXATTR:             unsafe.Sizeof(GetXAttrOut{}),
   557  		_OP_INIT:                  unsafe.Sizeof(InitOut{}),
   558  		_OP_OPENDIR:               unsafe.Sizeof(OpenOut{}),
   559  		_OP_GETLK:                 unsafe.Sizeof(LkOut{}),
   560  		_OP_CREATE:                unsafe.Sizeof(CreateOut{}),
   561  		_OP_BMAP:                  unsafe.Sizeof(_BmapOut{}),
   562  		_OP_IOCTL:                 unsafe.Sizeof(_IoctlOut{}),
   563  		_OP_POLL:                  unsafe.Sizeof(_PollOut{}),
   564  		_OP_NOTIFY_INVAL_ENTRY:    unsafe.Sizeof(NotifyInvalEntryOut{}),
   565  		_OP_NOTIFY_INVAL_INODE:    unsafe.Sizeof(NotifyInvalInodeOut{}),
   566  		_OP_NOTIFY_STORE_CACHE:    unsafe.Sizeof(NotifyStoreOut{}),
   567  		_OP_NOTIFY_RETRIEVE_CACHE: unsafe.Sizeof(NotifyRetrieveOut{}),
   568  		_OP_NOTIFY_DELETE:         unsafe.Sizeof(NotifyInvalDeleteOut{}),
   569  	} {
   570  		operationHandlers[op].OutputSize = sz
   571  	}
   572  
   573  	for op, v := range map[int32]string{
   574  		_OP_LOOKUP:                "LOOKUP",
   575  		_OP_FORGET:                "FORGET",
   576  		_OP_BATCH_FORGET:          "BATCH_FORGET",
   577  		_OP_GETATTR:               "GETATTR",
   578  		_OP_SETATTR:               "SETATTR",
   579  		_OP_READLINK:              "READLINK",
   580  		_OP_SYMLINK:               "SYMLINK",
   581  		_OP_MKNOD:                 "MKNOD",
   582  		_OP_MKDIR:                 "MKDIR",
   583  		_OP_UNLINK:                "UNLINK",
   584  		_OP_RMDIR:                 "RMDIR",
   585  		_OP_RENAME:                "RENAME",
   586  		_OP_LINK:                  "LINK",
   587  		_OP_OPEN:                  "OPEN",
   588  		_OP_READ:                  "READ",
   589  		_OP_WRITE:                 "WRITE",
   590  		_OP_STATFS:                "STATFS",
   591  		_OP_RELEASE:               "RELEASE",
   592  		_OP_FSYNC:                 "FSYNC",
   593  		_OP_SETXATTR:              "SETXATTR",
   594  		_OP_GETXATTR:              "GETXATTR",
   595  		_OP_LISTXATTR:             "LISTXATTR",
   596  		_OP_REMOVEXATTR:           "REMOVEXATTR",
   597  		_OP_FLUSH:                 "FLUSH",
   598  		_OP_INIT:                  "INIT",
   599  		_OP_OPENDIR:               "OPENDIR",
   600  		_OP_READDIR:               "READDIR",
   601  		_OP_RELEASEDIR:            "RELEASEDIR",
   602  		_OP_FSYNCDIR:              "FSYNCDIR",
   603  		_OP_GETLK:                 "GETLK",
   604  		_OP_SETLK:                 "SETLK",
   605  		_OP_SETLKW:                "SETLKW",
   606  		_OP_ACCESS:                "ACCESS",
   607  		_OP_CREATE:                "CREATE",
   608  		_OP_INTERRUPT:             "INTERRUPT",
   609  		_OP_BMAP:                  "BMAP",
   610  		_OP_DESTROY:               "DESTROY",
   611  		_OP_IOCTL:                 "IOCTL",
   612  		_OP_POLL:                  "POLL",
   613  		_OP_NOTIFY_REPLY:          "NOTIFY_REPLY",
   614  		_OP_NOTIFY_INVAL_ENTRY:    "NOTIFY_INVAL_ENTRY",
   615  		_OP_NOTIFY_INVAL_INODE:    "NOTIFY_INVAL_INODE",
   616  		_OP_NOTIFY_STORE_CACHE:    "NOTIFY_STORE",
   617  		_OP_NOTIFY_RETRIEVE_CACHE: "NOTIFY_RETRIEVE",
   618  		_OP_NOTIFY_DELETE:         "NOTIFY_DELETE",
   619  		_OP_FALLOCATE:             "FALLOCATE",
   620  		_OP_READDIRPLUS:           "READDIRPLUS",
   621  		_OP_RENAME2:               "RENAME2",
   622  	} {
   623  		operationHandlers[op].Name = v
   624  	}
   625  
   626  	for op, v := range map[int32]operationFunc{
   627  		_OP_OPEN:         doOpen,
   628  		_OP_READDIR:      doReadDir,
   629  		_OP_WRITE:        doWrite,
   630  		_OP_OPENDIR:      doOpenDir,
   631  		_OP_CREATE:       doCreate,
   632  		_OP_SETATTR:      doSetattr,
   633  		_OP_GETXATTR:     doGetXAttr,
   634  		_OP_LISTXATTR:    doGetXAttr,
   635  		_OP_GETATTR:      doGetAttr,
   636  		_OP_FORGET:       doForget,
   637  		_OP_BATCH_FORGET: doBatchForget,
   638  		_OP_READLINK:     doReadlink,
   639  		_OP_INIT:         doInit,
   640  		_OP_LOOKUP:       doLookup,
   641  		_OP_MKNOD:        doMknod,
   642  		_OP_MKDIR:        doMkdir,
   643  		_OP_UNLINK:       doUnlink,
   644  		_OP_RMDIR:        doRmdir,
   645  		_OP_LINK:         doLink,
   646  		_OP_READ:         doRead,
   647  		_OP_FLUSH:        doFlush,
   648  		_OP_RELEASE:      doRelease,
   649  		_OP_FSYNC:        doFsync,
   650  		_OP_RELEASEDIR:   doReleaseDir,
   651  		_OP_FSYNCDIR:     doFsyncDir,
   652  		_OP_SETXATTR:     doSetXAttr,
   653  		_OP_REMOVEXATTR:  doRemoveXAttr,
   654  		_OP_GETLK:        doGetLk,
   655  		_OP_SETLK:        doSetLk,
   656  		_OP_SETLKW:       doSetLkw,
   657  		_OP_ACCESS:       doAccess,
   658  		_OP_SYMLINK:      doSymlink,
   659  		_OP_RENAME:       doRename,
   660  		_OP_STATFS:       doStatFs,
   661  		_OP_IOCTL:        doIoctl,
   662  		_OP_DESTROY:      doDestroy,
   663  		_OP_NOTIFY_REPLY: doNotifyReply,
   664  		_OP_FALLOCATE:    doFallocate,
   665  		_OP_READDIRPLUS:  doReadDirPlus,
   666  		_OP_RENAME2:      doRename2,
   667  	} {
   668  		operationHandlers[op].Func = v
   669  	}
   670  
   671  	// Outputs.
   672  	for op, f := range map[int32]castPointerFunc{
   673  		_OP_LOOKUP:                func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
   674  		_OP_OPEN:                  func(ptr unsafe.Pointer) interface{} { return (*OpenOut)(ptr) },
   675  		_OP_OPENDIR:               func(ptr unsafe.Pointer) interface{} { return (*OpenOut)(ptr) },
   676  		_OP_GETATTR:               func(ptr unsafe.Pointer) interface{} { return (*AttrOut)(ptr) },
   677  		_OP_CREATE:                func(ptr unsafe.Pointer) interface{} { return (*CreateOut)(ptr) },
   678  		_OP_LINK:                  func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
   679  		_OP_SETATTR:               func(ptr unsafe.Pointer) interface{} { return (*AttrOut)(ptr) },
   680  		_OP_INIT:                  func(ptr unsafe.Pointer) interface{} { return (*InitOut)(ptr) },
   681  		_OP_MKDIR:                 func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
   682  		_OP_NOTIFY_INVAL_ENTRY:    func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalEntryOut)(ptr) },
   683  		_OP_NOTIFY_INVAL_INODE:    func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalInodeOut)(ptr) },
   684  		_OP_NOTIFY_STORE_CACHE:    func(ptr unsafe.Pointer) interface{} { return (*NotifyStoreOut)(ptr) },
   685  		_OP_NOTIFY_RETRIEVE_CACHE: func(ptr unsafe.Pointer) interface{} { return (*NotifyRetrieveOut)(ptr) },
   686  		_OP_NOTIFY_DELETE:         func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalDeleteOut)(ptr) },
   687  		_OP_STATFS:                func(ptr unsafe.Pointer) interface{} { return (*StatfsOut)(ptr) },
   688  		_OP_SYMLINK:               func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
   689  		_OP_GETLK:                 func(ptr unsafe.Pointer) interface{} { return (*LkOut)(ptr) },
   690  	} {
   691  		operationHandlers[op].DecodeOut = f
   692  	}
   693  
   694  	// Inputs.
   695  	for op, f := range map[int32]castPointerFunc{
   696  		_OP_FLUSH:        func(ptr unsafe.Pointer) interface{} { return (*FlushIn)(ptr) },
   697  		_OP_GETATTR:      func(ptr unsafe.Pointer) interface{} { return (*GetAttrIn)(ptr) },
   698  		_OP_SETXATTR:     func(ptr unsafe.Pointer) interface{} { return (*SetXAttrIn)(ptr) },
   699  		_OP_GETXATTR:     func(ptr unsafe.Pointer) interface{} { return (*GetXAttrIn)(ptr) },
   700  		_OP_LISTXATTR:    func(ptr unsafe.Pointer) interface{} { return (*GetXAttrIn)(ptr) },
   701  		_OP_SETATTR:      func(ptr unsafe.Pointer) interface{} { return (*SetAttrIn)(ptr) },
   702  		_OP_INIT:         func(ptr unsafe.Pointer) interface{} { return (*InitIn)(ptr) },
   703  		_OP_IOCTL:        func(ptr unsafe.Pointer) interface{} { return (*_IoctlIn)(ptr) },
   704  		_OP_OPEN:         func(ptr unsafe.Pointer) interface{} { return (*OpenIn)(ptr) },
   705  		_OP_MKNOD:        func(ptr unsafe.Pointer) interface{} { return (*MknodIn)(ptr) },
   706  		_OP_CREATE:       func(ptr unsafe.Pointer) interface{} { return (*CreateIn)(ptr) },
   707  		_OP_READ:         func(ptr unsafe.Pointer) interface{} { return (*ReadIn)(ptr) },
   708  		_OP_READDIR:      func(ptr unsafe.Pointer) interface{} { return (*ReadIn)(ptr) },
   709  		_OP_ACCESS:       func(ptr unsafe.Pointer) interface{} { return (*AccessIn)(ptr) },
   710  		_OP_FORGET:       func(ptr unsafe.Pointer) interface{} { return (*ForgetIn)(ptr) },
   711  		_OP_BATCH_FORGET: func(ptr unsafe.Pointer) interface{} { return (*_BatchForgetIn)(ptr) },
   712  		_OP_LINK:         func(ptr unsafe.Pointer) interface{} { return (*LinkIn)(ptr) },
   713  		_OP_MKDIR:        func(ptr unsafe.Pointer) interface{} { return (*MkdirIn)(ptr) },
   714  		_OP_RELEASE:      func(ptr unsafe.Pointer) interface{} { return (*ReleaseIn)(ptr) },
   715  		_OP_RELEASEDIR:   func(ptr unsafe.Pointer) interface{} { return (*ReleaseIn)(ptr) },
   716  		_OP_FALLOCATE:    func(ptr unsafe.Pointer) interface{} { return (*FallocateIn)(ptr) },
   717  		_OP_NOTIFY_REPLY: func(ptr unsafe.Pointer) interface{} { return (*NotifyRetrieveIn)(ptr) },
   718  		_OP_READDIRPLUS:  func(ptr unsafe.Pointer) interface{} { return (*ReadIn)(ptr) },
   719  		_OP_RENAME:       func(ptr unsafe.Pointer) interface{} { return (*Rename1In)(ptr) },
   720  		_OP_GETLK:        func(ptr unsafe.Pointer) interface{} { return (*LkIn)(ptr) },
   721  		_OP_SETLK:        func(ptr unsafe.Pointer) interface{} { return (*LkIn)(ptr) },
   722  		_OP_SETLKW:       func(ptr unsafe.Pointer) interface{} { return (*LkIn)(ptr) },
   723  		_OP_RENAME2:      func(ptr unsafe.Pointer) interface{} { return (*RenameIn)(ptr) },
   724  	} {
   725  		operationHandlers[op].DecodeIn = f
   726  	}
   727  
   728  	// File name args.
   729  	for op, count := range map[int32]int{
   730  		_OP_CREATE:      1,
   731  		_OP_SETXATTR:    1,
   732  		_OP_GETXATTR:    1,
   733  		_OP_LINK:        1,
   734  		_OP_LOOKUP:      1,
   735  		_OP_MKDIR:       1,
   736  		_OP_MKNOD:       1,
   737  		_OP_REMOVEXATTR: 1,
   738  		_OP_RENAME:      2,
   739  		_OP_RENAME2:     2,
   740  		_OP_RMDIR:       1,
   741  		_OP_SYMLINK:     2,
   742  		_OP_UNLINK:      1,
   743  	} {
   744  		operationHandlers[op].FileNames = count
   745  	}
   746  
   747  	var r request
   748  	sizeOfOutHeader := unsafe.Sizeof(OutHeader{})
   749  	for code, h := range operationHandlers {
   750  		if h.OutputSize+sizeOfOutHeader > unsafe.Sizeof(r.outBuf) {
   751  			log.Panicf("request output buffer too small: code %v, sz %d + %d %v", code, h.OutputSize, sizeOfOutHeader, h)
   752  		}
   753  	}
   754  }