github.com/scaleoutsean/fusego@v0.0.0-20220224074057-4a6429e46bb8/conversions.go (about)

     1  // Copyright 2015 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package fuse
    16  
    17  import (
    18  	"bytes"
    19  	"errors"
    20  	"fmt"
    21  	"os"
    22  	"reflect"
    23  	"syscall"
    24  	"time"
    25  	"unsafe"
    26  
    27  	"github.com/scaleoutsean/fusego/fuseops"
    28  	"github.com/scaleoutsean/fusego/internal/buffer"
    29  	"github.com/scaleoutsean/fusego/internal/fusekernel"
    30  )
    31  
    32  ////////////////////////////////////////////////////////////////////////
    33  // Incoming messages
    34  ////////////////////////////////////////////////////////////////////////
    35  
    36  // Convert a kernel message to an appropriate op. If the op is unknown, a
    37  // special unexported type will be used.
    38  //
    39  // The caller is responsible for arranging for the message to be destroyed.
    40  func convertInMessage(
    41  	inMsg *buffer.InMessage,
    42  	outMsg *buffer.OutMessage,
    43  	protocol fusekernel.Protocol) (o interface{}, err error) {
    44  	switch inMsg.Header().Opcode {
    45  	case fusekernel.OpLookup:
    46  		buf := inMsg.ConsumeBytes(inMsg.Len())
    47  		n := len(buf)
    48  		if n == 0 || buf[n-1] != '\x00' {
    49  			return nil, errors.New("Corrupt OpLookup")
    50  		}
    51  
    52  		o = &fuseops.LookUpInodeOp{
    53  			Parent: fuseops.InodeID(inMsg.Header().Nodeid),
    54  			Name:   string(buf[:n-1]),
    55  		}
    56  
    57  	case fusekernel.OpGetattr:
    58  		o = &fuseops.GetInodeAttributesOp{
    59  			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
    60  		}
    61  
    62  	case fusekernel.OpSetattr:
    63  		type input fusekernel.SetattrIn
    64  		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
    65  		if in == nil {
    66  			return nil, errors.New("Corrupt OpSetattr")
    67  		}
    68  
    69  		to := &fuseops.SetInodeAttributesOp{
    70  			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
    71  		}
    72  		o = to
    73  
    74  		valid := fusekernel.SetattrValid(in.Valid)
    75  		if valid&fusekernel.SetattrSize != 0 {
    76  			to.Size = &in.Size
    77  		}
    78  
    79  		if valid&fusekernel.SetattrMode != 0 {
    80  			mode := convertFileMode(in.Mode)
    81  			to.Mode = &mode
    82  		}
    83  
    84  		if valid&fusekernel.SetattrAtime != 0 {
    85  			t := time.Unix(int64(in.Atime), int64(in.AtimeNsec))
    86  			to.Atime = &t
    87  		}
    88  
    89  		if valid&fusekernel.SetattrMtime != 0 {
    90  			t := time.Unix(int64(in.Mtime), int64(in.MtimeNsec))
    91  			to.Mtime = &t
    92  		}
    93  
    94  		if valid.Handle() {
    95  			t := fuseops.HandleID(in.Fh)
    96  			to.Handle = &t
    97  		}
    98  
    99  	case fusekernel.OpForget:
   100  		type input fusekernel.ForgetIn
   101  		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
   102  		if in == nil {
   103  			return nil, errors.New("Corrupt OpForget")
   104  		}
   105  
   106  		o = &fuseops.ForgetInodeOp{
   107  			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
   108  			N:     in.Nlookup,
   109  		}
   110  
   111  	case fusekernel.OpMkdir:
   112  		in := (*fusekernel.MkdirIn)(inMsg.Consume(fusekernel.MkdirInSize(protocol)))
   113  		if in == nil {
   114  			return nil, errors.New("Corrupt OpMkdir")
   115  		}
   116  
   117  		name := inMsg.ConsumeBytes(inMsg.Len())
   118  		i := bytes.IndexByte(name, '\x00')
   119  		if i < 0 {
   120  			return nil, errors.New("Corrupt OpMkdir")
   121  		}
   122  		name = name[:i]
   123  
   124  		o = &fuseops.MkDirOp{
   125  			Parent: fuseops.InodeID(inMsg.Header().Nodeid),
   126  			Name:   string(name),
   127  
   128  			// On Linux, vfs_mkdir calls through to the inode with at most
   129  			// permissions and sticky bits set (cf. https://goo.gl/WxgQXk), and fuse
   130  			// passes that on directly (cf. https://goo.gl/f31aMo). In other words,
   131  			// the fact that this is a directory is implicit in the fact that the
   132  			// opcode is mkdir. But we want the correct mode to go through, so ensure
   133  			// that os.ModeDir is set.
   134  			Mode: convertFileMode(in.Mode) | os.ModeDir,
   135  		}
   136  
   137  	case fusekernel.OpMknod:
   138  		in := (*fusekernel.MknodIn)(inMsg.Consume(fusekernel.MknodInSize(protocol)))
   139  		if in == nil {
   140  			return nil, errors.New("Corrupt OpMknod")
   141  		}
   142  
   143  		name := inMsg.ConsumeBytes(inMsg.Len())
   144  		i := bytes.IndexByte(name, '\x00')
   145  		if i < 0 {
   146  			return nil, errors.New("Corrupt OpMknod")
   147  		}
   148  		name = name[:i]
   149  
   150  		o = &fuseops.MkNodeOp{
   151  			Parent: fuseops.InodeID(inMsg.Header().Nodeid),
   152  			Name:   string(name),
   153  			Mode:   convertFileMode(in.Mode),
   154  		}
   155  
   156  	case fusekernel.OpCreate:
   157  		in := (*fusekernel.CreateIn)(inMsg.Consume(fusekernel.CreateInSize(protocol)))
   158  		if in == nil {
   159  			return nil, errors.New("Corrupt OpCreate")
   160  		}
   161  
   162  		name := inMsg.ConsumeBytes(inMsg.Len())
   163  		i := bytes.IndexByte(name, '\x00')
   164  		if i < 0 {
   165  			return nil, errors.New("Corrupt OpCreate")
   166  		}
   167  		name = name[:i]
   168  
   169  		o = &fuseops.CreateFileOp{
   170  			Parent:   fuseops.InodeID(inMsg.Header().Nodeid),
   171  			Name:     string(name),
   172  			Mode:     convertFileMode(in.Mode),
   173  			Metadata: fuseops.OpMetadata{Pid: inMsg.Header().Pid},
   174  		}
   175  
   176  	case fusekernel.OpSymlink:
   177  		// The message is "newName\0target\0".
   178  		names := inMsg.ConsumeBytes(inMsg.Len())
   179  		if len(names) == 0 || names[len(names)-1] != 0 {
   180  			return nil, errors.New("Corrupt OpSymlink")
   181  		}
   182  		i := bytes.IndexByte(names, '\x00')
   183  		if i < 0 {
   184  			return nil, errors.New("Corrupt OpSymlink")
   185  		}
   186  		newName, target := names[0:i], names[i+1:len(names)-1]
   187  
   188  		o = &fuseops.CreateSymlinkOp{
   189  			Parent: fuseops.InodeID(inMsg.Header().Nodeid),
   190  			Name:   string(newName),
   191  			Target: string(target),
   192  		}
   193  
   194  	case fusekernel.OpRename:
   195  		type input fusekernel.RenameIn
   196  		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
   197  		if in == nil {
   198  			return nil, errors.New("Corrupt OpRename")
   199  		}
   200  
   201  		names := inMsg.ConsumeBytes(inMsg.Len())
   202  		// names should be "old\x00new\x00"
   203  		if len(names) < 4 {
   204  			return nil, errors.New("Corrupt OpRename")
   205  		}
   206  		if names[len(names)-1] != '\x00' {
   207  			return nil, errors.New("Corrupt OpRename")
   208  		}
   209  		i := bytes.IndexByte(names, '\x00')
   210  		if i < 0 {
   211  			return nil, errors.New("Corrupt OpRename")
   212  		}
   213  		oldName, newName := names[:i], names[i+1:len(names)-1]
   214  
   215  		o = &fuseops.RenameOp{
   216  			OldParent: fuseops.InodeID(inMsg.Header().Nodeid),
   217  			OldName:   string(oldName),
   218  			NewParent: fuseops.InodeID(in.Newdir),
   219  			NewName:   string(newName),
   220  		}
   221  
   222  	case fusekernel.OpUnlink:
   223  		buf := inMsg.ConsumeBytes(inMsg.Len())
   224  		n := len(buf)
   225  		if n == 0 || buf[n-1] != '\x00' {
   226  			return nil, errors.New("Corrupt OpUnlink")
   227  		}
   228  
   229  		o = &fuseops.UnlinkOp{
   230  			Parent: fuseops.InodeID(inMsg.Header().Nodeid),
   231  			Name:   string(buf[:n-1]),
   232  		}
   233  
   234  	case fusekernel.OpRmdir:
   235  		buf := inMsg.ConsumeBytes(inMsg.Len())
   236  		n := len(buf)
   237  		if n == 0 || buf[n-1] != '\x00' {
   238  			return nil, errors.New("Corrupt OpRmdir")
   239  		}
   240  
   241  		o = &fuseops.RmDirOp{
   242  			Parent: fuseops.InodeID(inMsg.Header().Nodeid),
   243  			Name:   string(buf[:n-1]),
   244  		}
   245  
   246  	case fusekernel.OpOpen:
   247  		o = &fuseops.OpenFileOp{
   248  			Inode:    fuseops.InodeID(inMsg.Header().Nodeid),
   249  			Metadata: fuseops.OpMetadata{Pid: inMsg.Header().Pid},
   250  		}
   251  
   252  	case fusekernel.OpOpendir:
   253  		o = &fuseops.OpenDirOp{
   254  			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
   255  		}
   256  
   257  	case fusekernel.OpRead:
   258  		in := (*fusekernel.ReadIn)(inMsg.Consume(fusekernel.ReadInSize(protocol)))
   259  		if in == nil {
   260  			return nil, errors.New("Corrupt OpRead")
   261  		}
   262  
   263  		to := &fuseops.ReadFileOp{
   264  			Inode:  fuseops.InodeID(inMsg.Header().Nodeid),
   265  			Handle: fuseops.HandleID(in.Fh),
   266  			Offset: int64(in.Offset),
   267  		}
   268  		o = to
   269  
   270  		readSize := int(in.Size)
   271  		p := outMsg.GrowNoZero(readSize)
   272  		if p == nil {
   273  			return nil, fmt.Errorf("Can't grow for %d-byte read", readSize)
   274  		}
   275  
   276  		sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst))
   277  		sh.Data = uintptr(p)
   278  		sh.Len = readSize
   279  		sh.Cap = readSize
   280  
   281  	case fusekernel.OpReaddir:
   282  		in := (*fusekernel.ReadIn)(inMsg.Consume(fusekernel.ReadInSize(protocol)))
   283  		if in == nil {
   284  			return nil, errors.New("Corrupt OpReaddir")
   285  		}
   286  
   287  		to := &fuseops.ReadDirOp{
   288  			Inode:  fuseops.InodeID(inMsg.Header().Nodeid),
   289  			Handle: fuseops.HandleID(in.Fh),
   290  			Offset: fuseops.DirOffset(in.Offset),
   291  		}
   292  		o = to
   293  
   294  		readSize := int(in.Size)
   295  		p := outMsg.GrowNoZero(readSize)
   296  		if p == nil {
   297  			return nil, fmt.Errorf("Can't grow for %d-byte read", readSize)
   298  		}
   299  
   300  		sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst))
   301  		sh.Data = uintptr(p)
   302  		sh.Len = readSize
   303  		sh.Cap = readSize
   304  
   305  	case fusekernel.OpRelease:
   306  		type input fusekernel.ReleaseIn
   307  		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
   308  		if in == nil {
   309  			return nil, errors.New("Corrupt OpRelease")
   310  		}
   311  
   312  		o = &fuseops.ReleaseFileHandleOp{
   313  			Handle: fuseops.HandleID(in.Fh),
   314  		}
   315  
   316  	case fusekernel.OpReleasedir:
   317  		type input fusekernel.ReleaseIn
   318  		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
   319  		if in == nil {
   320  			return nil, errors.New("Corrupt OpReleasedir")
   321  		}
   322  
   323  		o = &fuseops.ReleaseDirHandleOp{
   324  			Handle: fuseops.HandleID(in.Fh),
   325  		}
   326  
   327  	case fusekernel.OpWrite:
   328  		in := (*fusekernel.WriteIn)(inMsg.Consume(fusekernel.WriteInSize(protocol)))
   329  		if in == nil {
   330  			return nil, errors.New("Corrupt OpWrite")
   331  		}
   332  
   333  		buf := inMsg.ConsumeBytes(inMsg.Len())
   334  		if len(buf) < int(in.Size) {
   335  			return nil, errors.New("Corrupt OpWrite")
   336  		}
   337  
   338  		o = &fuseops.WriteFileOp{
   339  			Inode:  fuseops.InodeID(inMsg.Header().Nodeid),
   340  			Handle: fuseops.HandleID(in.Fh),
   341  			Data:   buf,
   342  			Offset: int64(in.Offset),
   343  		}
   344  
   345  	case fusekernel.OpFsync:
   346  		type input fusekernel.FsyncIn
   347  		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
   348  		if in == nil {
   349  			return nil, errors.New("Corrupt OpFsync")
   350  		}
   351  
   352  		o = &fuseops.SyncFileOp{
   353  			Inode:  fuseops.InodeID(inMsg.Header().Nodeid),
   354  			Handle: fuseops.HandleID(in.Fh),
   355  		}
   356  
   357  	case fusekernel.OpFlush:
   358  		type input fusekernel.FlushIn
   359  		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
   360  		if in == nil {
   361  			return nil, errors.New("Corrupt OpFlush")
   362  		}
   363  
   364  		o = &fuseops.FlushFileOp{
   365  			Inode:    fuseops.InodeID(inMsg.Header().Nodeid),
   366  			Handle:   fuseops.HandleID(in.Fh),
   367  			Metadata: fuseops.OpMetadata{Pid: inMsg.Header().Pid},
   368  		}
   369  
   370  	case fusekernel.OpReadlink:
   371  		o = &fuseops.ReadSymlinkOp{
   372  			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
   373  		}
   374  
   375  	case fusekernel.OpStatfs:
   376  		o = &fuseops.StatFSOp{}
   377  
   378  	case fusekernel.OpInterrupt:
   379  		type input fusekernel.InterruptIn
   380  		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
   381  		if in == nil {
   382  			return nil, errors.New("Corrupt OpInterrupt")
   383  		}
   384  
   385  		o = &interruptOp{
   386  			FuseID: in.Unique,
   387  		}
   388  
   389  	case fusekernel.OpInit:
   390  		type input fusekernel.InitIn
   391  		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
   392  		if in == nil {
   393  			return nil, errors.New("Corrupt OpInit")
   394  		}
   395  
   396  		o = &initOp{
   397  			Kernel:       fusekernel.Protocol{in.Major, in.Minor},
   398  			MaxReadahead: in.MaxReadahead,
   399  			Flags:        fusekernel.InitFlags(in.Flags),
   400  		}
   401  
   402  	case fusekernel.OpLink:
   403  		type input fusekernel.LinkIn
   404  		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
   405  		if in == nil {
   406  			return nil, errors.New("Corrupt OpLink")
   407  		}
   408  
   409  		name := inMsg.ConsumeBytes(inMsg.Len())
   410  		i := bytes.IndexByte(name, '\x00')
   411  		if i < 0 {
   412  			return nil, errors.New("Corrupt OpLink")
   413  		}
   414  		name = name[:i]
   415  		if len(name) == 0 {
   416  			return nil, errors.New("Corrupt OpLink (Name not read)")
   417  		}
   418  
   419  		o = &fuseops.CreateLinkOp{
   420  			Parent: fuseops.InodeID(inMsg.Header().Nodeid),
   421  			Name:   string(name),
   422  			Target: fuseops.InodeID(in.Oldnodeid),
   423  		}
   424  
   425  	case fusekernel.OpRemovexattr:
   426  		buf := inMsg.ConsumeBytes(inMsg.Len())
   427  		n := len(buf)
   428  		if n == 0 || buf[n-1] != '\x00' {
   429  			return nil, errors.New("Corrupt OpRemovexattr")
   430  		}
   431  
   432  		o = &fuseops.RemoveXattrOp{
   433  			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
   434  			Name:  string(buf[:n-1]),
   435  		}
   436  
   437  	case fusekernel.OpGetxattr:
   438  		type input fusekernel.GetxattrIn
   439  		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
   440  		if in == nil {
   441  			return nil, errors.New("Corrupt OpGetxattr")
   442  		}
   443  
   444  		name := inMsg.ConsumeBytes(inMsg.Len())
   445  		i := bytes.IndexByte(name, '\x00')
   446  		if i < 0 {
   447  			return nil, errors.New("Corrupt OpGetxattr")
   448  		}
   449  		name = name[:i]
   450  
   451  		to := &fuseops.GetXattrOp{
   452  			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
   453  			Name:  string(name),
   454  		}
   455  		o = to
   456  
   457  		readSize := int(in.Size)
   458  		p := outMsg.GrowNoZero(readSize)
   459  		if p == nil {
   460  			return nil, fmt.Errorf("Can't grow for %d-byte read", readSize)
   461  		}
   462  
   463  		sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst))
   464  		sh.Data = uintptr(p)
   465  		sh.Len = readSize
   466  		sh.Cap = readSize
   467  
   468  	case fusekernel.OpListxattr:
   469  		type input fusekernel.ListxattrIn
   470  		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
   471  		if in == nil {
   472  			return nil, errors.New("Corrupt OpListxattr")
   473  		}
   474  
   475  		to := &fuseops.ListXattrOp{
   476  			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
   477  		}
   478  		o = to
   479  
   480  		readSize := int(in.Size)
   481  		if readSize != 0 {
   482  			p := outMsg.GrowNoZero(readSize)
   483  			if p == nil {
   484  				return nil, fmt.Errorf("Can't grow for %d-byte read", readSize)
   485  			}
   486  			sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst))
   487  			sh.Data = uintptr(p)
   488  			sh.Len = readSize
   489  			sh.Cap = readSize
   490  		}
   491  	case fusekernel.OpSetxattr:
   492  		type input fusekernel.SetxattrIn
   493  		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
   494  		if in == nil {
   495  			return nil, errors.New("Corrupt OpSetxattr")
   496  		}
   497  
   498  		payload := inMsg.ConsumeBytes(inMsg.Len())
   499  		// payload should be "name\x00value"
   500  		if len(payload) < 3 {
   501  			return nil, errors.New("Corrupt OpSetxattr")
   502  		}
   503  		i := bytes.IndexByte(payload, '\x00')
   504  		if i < 0 {
   505  			return nil, errors.New("Corrupt OpSetxattr")
   506  		}
   507  
   508  		name, value := payload[:i], payload[i+1:len(payload)]
   509  
   510  		o = &fuseops.SetXattrOp{
   511  			Inode: fuseops.InodeID(inMsg.Header().Nodeid),
   512  			Name:  string(name),
   513  			Value: value,
   514  			Flags: in.Flags,
   515  		}
   516  	case fusekernel.OpFallocate:
   517  		type input fusekernel.FallocateIn
   518  		in := (*input)(inMsg.Consume(unsafe.Sizeof(input{})))
   519  		if in == nil {
   520  			return nil, errors.New("Corrupt OpFallocate")
   521  		}
   522  
   523  		o = &fuseops.FallocateOp{
   524  			Inode:  fuseops.InodeID(inMsg.Header().Nodeid),
   525  			Handle: fuseops.HandleID(in.Fh),
   526  			Offset: in.Offset,
   527  			Length: in.Length,
   528  			Mode:   in.Mode,
   529  		}
   530  
   531  	default:
   532  		o = &unknownOp{
   533  			OpCode: inMsg.Header().Opcode,
   534  			Inode:  fuseops.InodeID(inMsg.Header().Nodeid),
   535  		}
   536  	}
   537  
   538  	return o, nil
   539  }
   540  
   541  ////////////////////////////////////////////////////////////////////////
   542  // Outgoing messages
   543  ////////////////////////////////////////////////////////////////////////
   544  
   545  // Fill in the response that should be sent to the kernel, or set noResponse if
   546  // the op requires no response.
   547  func (c *Connection) kernelResponse(
   548  	m *buffer.OutMessage,
   549  	fuseID uint64,
   550  	op interface{},
   551  	opErr error) (noResponse bool) {
   552  	h := m.OutHeader()
   553  	h.Unique = fuseID
   554  
   555  	// Special case: handle the ops for which the kernel expects no response.
   556  	// interruptOp .
   557  	switch op.(type) {
   558  	case *fuseops.ForgetInodeOp:
   559  		return true
   560  
   561  	case *interruptOp:
   562  		return true
   563  	}
   564  
   565  	// If the user returned the error, fill in the error field of the outgoing
   566  	// message header.
   567  	if opErr != nil {
   568  		handled := false
   569  
   570  		if !handled {
   571  			m.OutHeader().Error = -int32(syscall.EIO)
   572  			if errno, ok := opErr.(syscall.Errno); ok {
   573  				m.OutHeader().Error = -int32(errno)
   574  			}
   575  
   576  			// Special case: for some types, convertInMessage grew the message in order
   577  			// to obtain a destination buffer. Make sure that we shrink back to just
   578  			// the header, because on OS X the kernel otherwise returns EINVAL when we
   579  			// attempt to write an error response with a length that extends beyond the
   580  			// header.
   581  			m.ShrinkTo(buffer.OutMessageHeaderSize)
   582  		}
   583  	}
   584  
   585  	// Otherwise, fill in the rest of the response.
   586  	if opErr == nil {
   587  		c.kernelResponseForOp(m, op)
   588  	}
   589  
   590  	h.Len = uint32(m.Len())
   591  	return false
   592  }
   593  
   594  // Like kernelResponse, but assumes the user replied with a nil error to the
   595  // op.
   596  func (c *Connection) kernelResponseForOp(
   597  	m *buffer.OutMessage,
   598  	op interface{}) {
   599  	// Create the appropriate output message
   600  	switch o := op.(type) {
   601  	case *fuseops.LookUpInodeOp:
   602  		size := int(fusekernel.EntryOutSize(c.protocol))
   603  		out := (*fusekernel.EntryOut)(m.Grow(size))
   604  		convertChildInodeEntry(&o.Entry, out)
   605  
   606  	case *fuseops.GetInodeAttributesOp:
   607  		size := int(fusekernel.AttrOutSize(c.protocol))
   608  		out := (*fusekernel.AttrOut)(m.Grow(size))
   609  		out.AttrValid, out.AttrValidNsec = convertExpirationTime(
   610  			o.AttributesExpiration)
   611  		convertAttributes(o.Inode, &o.Attributes, &out.Attr)
   612  
   613  	case *fuseops.SetInodeAttributesOp:
   614  		size := int(fusekernel.AttrOutSize(c.protocol))
   615  		out := (*fusekernel.AttrOut)(m.Grow(size))
   616  		out.AttrValid, out.AttrValidNsec = convertExpirationTime(
   617  			o.AttributesExpiration)
   618  		convertAttributes(o.Inode, &o.Attributes, &out.Attr)
   619  
   620  	case *fuseops.MkDirOp:
   621  		size := int(fusekernel.EntryOutSize(c.protocol))
   622  		out := (*fusekernel.EntryOut)(m.Grow(size))
   623  		convertChildInodeEntry(&o.Entry, out)
   624  
   625  	case *fuseops.MkNodeOp:
   626  		size := int(fusekernel.EntryOutSize(c.protocol))
   627  		out := (*fusekernel.EntryOut)(m.Grow(size))
   628  		convertChildInodeEntry(&o.Entry, out)
   629  
   630  	case *fuseops.CreateFileOp:
   631  		eSize := int(fusekernel.EntryOutSize(c.protocol))
   632  
   633  		e := (*fusekernel.EntryOut)(m.Grow(eSize))
   634  		convertChildInodeEntry(&o.Entry, e)
   635  
   636  		oo := (*fusekernel.OpenOut)(m.Grow(int(unsafe.Sizeof(fusekernel.OpenOut{}))))
   637  		oo.Fh = uint64(o.Handle)
   638  
   639  	case *fuseops.CreateSymlinkOp:
   640  		size := int(fusekernel.EntryOutSize(c.protocol))
   641  		out := (*fusekernel.EntryOut)(m.Grow(size))
   642  		convertChildInodeEntry(&o.Entry, out)
   643  
   644  	case *fuseops.CreateLinkOp:
   645  		size := int(fusekernel.EntryOutSize(c.protocol))
   646  		out := (*fusekernel.EntryOut)(m.Grow(size))
   647  		convertChildInodeEntry(&o.Entry, out)
   648  
   649  	case *fuseops.RenameOp:
   650  		// Empty response
   651  
   652  	case *fuseops.RmDirOp:
   653  		// Empty response
   654  
   655  	case *fuseops.UnlinkOp:
   656  		// Empty response
   657  
   658  	case *fuseops.OpenDirOp:
   659  		out := (*fusekernel.OpenOut)(m.Grow(int(unsafe.Sizeof(fusekernel.OpenOut{}))))
   660  		out.Fh = uint64(o.Handle)
   661  
   662  	case *fuseops.ReadDirOp:
   663  		// convertInMessage already set up the destination buffer to be at the end
   664  		// of the out message. We need only shrink to the right size based on how
   665  		// much the user read.
   666  		m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead)
   667  
   668  	case *fuseops.ReleaseDirHandleOp:
   669  		// Empty response
   670  
   671  	case *fuseops.OpenFileOp:
   672  		out := (*fusekernel.OpenOut)(m.Grow(int(unsafe.Sizeof(fusekernel.OpenOut{}))))
   673  		out.Fh = uint64(o.Handle)
   674  
   675  		if o.KeepPageCache {
   676  			out.OpenFlags |= uint32(fusekernel.OpenKeepCache)
   677  		}
   678  
   679  		if o.UseDirectIO {
   680  			out.OpenFlags |= uint32(fusekernel.OpenDirectIO)
   681  		}
   682  
   683  	case *fuseops.ReadFileOp:
   684  		// convertInMessage already set up the destination buffer to be at the end
   685  		// of the out message. We need only shrink to the right size based on how
   686  		// much the user read.
   687  		m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead)
   688  
   689  	case *fuseops.WriteFileOp:
   690  		out := (*fusekernel.WriteOut)(m.Grow(int(unsafe.Sizeof(fusekernel.WriteOut{}))))
   691  		out.Size = uint32(len(o.Data))
   692  
   693  	case *fuseops.SyncFileOp:
   694  		// Empty response
   695  
   696  	case *fuseops.FlushFileOp:
   697  		// Empty response
   698  
   699  	case *fuseops.ReleaseFileHandleOp:
   700  		// Empty response
   701  
   702  	case *fuseops.ReadSymlinkOp:
   703  		m.AppendString(o.Target)
   704  
   705  	case *fuseops.StatFSOp:
   706  		out := (*fusekernel.StatfsOut)(m.Grow(int(unsafe.Sizeof(fusekernel.StatfsOut{}))))
   707  		out.St.Blocks = o.Blocks
   708  		out.St.Bfree = o.BlocksFree
   709  		out.St.Bavail = o.BlocksAvailable
   710  		out.St.Files = o.Inodes
   711  		out.St.Ffree = o.InodesFree
   712  
   713  		// The posix spec for sys/statvfs.h (http://goo.gl/LktgrF) defines the
   714  		// following fields of statvfs, among others:
   715  		//
   716  		//     f_bsize    File system block size.
   717  		//     f_frsize   Fundamental file system block size.
   718  		//     f_blocks   Total number of blocks on file system in units of f_frsize.
   719  		//
   720  		// It appears as though f_bsize was the only thing supported by most unixes
   721  		// originally, but then f_frsize was added when new sorts of file systems
   722  		// came about. Quoth The Linux Programming Interface by Michael Kerrisk
   723  		// (https://goo.gl/5LZMxQ):
   724  		//
   725  		//     For most Linux file systems, the values of f_bsize and f_frsize are
   726  		//     the same. However, some file systems support the notion of block
   727  		//     fragments, which can be used to allocate a smaller unit of storage
   728  		//     at the end of the file if if a full block is not required. This
   729  		//     avoids the waste of space that would otherwise occur if a full block
   730  		//     was allocated. On such file systems, f_frsize is the size of a
   731  		//     fragment, and f_bsize is the size of a whole block. (The notion of
   732  		//     fragments in UNIX file systems first appeared in the early 1980s
   733  		//     with the 4.2BSD Fast File System.)
   734  		//
   735  		// Confusingly, it appears as though osxfuse surfaces fuse_kstatfs::bsize
   736  		// as statfs::f_iosize (of advisory use only), and fuse_kstatfs::frsize as
   737  		// statfs::f_bsize (which affects free space display in the Finder).
   738  		out.St.Bsize = o.IoSize
   739  		out.St.Frsize = o.BlockSize
   740  
   741  	case *fuseops.RemoveXattrOp:
   742  		// Empty response
   743  
   744  	case *fuseops.GetXattrOp:
   745  		// convertInMessage already set up the destination buffer to be at the end
   746  		// of the out message. We need only shrink to the right size based on how
   747  		// much the user read.
   748  		if len(o.Dst) == 0 {
   749  			writeXattrSize(m, uint32(o.BytesRead))
   750  		} else {
   751  			m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead)
   752  		}
   753  
   754  	case *fuseops.ListXattrOp:
   755  		if len(o.Dst) == 0 {
   756  			writeXattrSize(m, uint32(o.BytesRead))
   757  		} else {
   758  			m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead)
   759  		}
   760  
   761  	case *fuseops.SetXattrOp:
   762  		// Empty response
   763  
   764  	case *fuseops.FallocateOp:
   765  		// Empty response
   766  
   767  	case *initOp:
   768  		out := (*fusekernel.InitOut)(m.Grow(int(unsafe.Sizeof(fusekernel.InitOut{}))))
   769  
   770  		out.Major = o.Library.Major
   771  		out.Minor = o.Library.Minor
   772  		out.MaxReadahead = o.MaxReadahead
   773  		out.Flags = uint32(o.Flags)
   774  		// Default values
   775  		out.MaxBackground = 12
   776  		out.CongestionThreshold = 9
   777  		out.MaxWrite = o.MaxWrite
   778  		out.TimeGran = 1
   779  		out.MaxPages = o.MaxPages
   780  
   781  	default:
   782  		panic(fmt.Sprintf("Unexpected op: %#v", op))
   783  	}
   784  
   785  	return
   786  }
   787  
   788  ////////////////////////////////////////////////////////////////////////
   789  // General conversions
   790  ////////////////////////////////////////////////////////////////////////
   791  
   792  func convertTime(t time.Time) (secs uint64, nsec uint32) {
   793  	totalNano := t.UnixNano()
   794  	secs = uint64(totalNano / 1e9)
   795  	nsec = uint32(totalNano % 1e9)
   796  	return secs, nsec
   797  }
   798  
   799  func convertAttributes(
   800  	inodeID fuseops.InodeID,
   801  	in *fuseops.InodeAttributes,
   802  	out *fusekernel.Attr) {
   803  	out.Ino = uint64(inodeID)
   804  	out.Size = in.Size
   805  	out.Atime, out.AtimeNsec = convertTime(in.Atime)
   806  	out.Mtime, out.MtimeNsec = convertTime(in.Mtime)
   807  	out.Ctime, out.CtimeNsec = convertTime(in.Ctime)
   808  	out.SetCrtime(convertTime(in.Crtime))
   809  	out.Nlink = in.Nlink
   810  	out.Uid = in.Uid
   811  	out.Gid = in.Gid
   812  	// round up to the nearest 512 boundary
   813  	out.Blocks = (in.Size + 512 - 1) / 512
   814  
   815  	// Set the mode.
   816  	out.Mode = uint32(in.Mode) & 0777
   817  	switch {
   818  	default:
   819  		out.Mode |= syscall.S_IFREG
   820  	case in.Mode&os.ModeDir != 0:
   821  		out.Mode |= syscall.S_IFDIR
   822  	case in.Mode&os.ModeDevice != 0:
   823  		if in.Mode&os.ModeCharDevice != 0 {
   824  			out.Mode |= syscall.S_IFCHR
   825  		} else {
   826  			out.Mode |= syscall.S_IFBLK
   827  		}
   828  	case in.Mode&os.ModeNamedPipe != 0:
   829  		out.Mode |= syscall.S_IFIFO
   830  	case in.Mode&os.ModeSymlink != 0:
   831  		out.Mode |= syscall.S_IFLNK
   832  	case in.Mode&os.ModeSocket != 0:
   833  		out.Mode |= syscall.S_IFSOCK
   834  	}
   835  	if in.Mode&os.ModeSetuid != 0 {
   836  		out.Mode |= syscall.S_ISUID
   837  	}
   838  }
   839  
   840  // Convert an absolute cache expiration time to a relative time from now for
   841  // consumption by the fuse kernel module.
   842  func convertExpirationTime(t time.Time) (secs uint64, nsecs uint32) {
   843  	// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
   844  	// counts of nanoseconds (cf. http://goo.gl/EJupJV). So negative durations
   845  	// are right out. There is no need to cap the positive magnitude, because
   846  	// 2^64 seconds is well longer than the 2^63 ns range of time.Duration.
   847  	d := t.Sub(time.Now())
   848  	if d > 0 {
   849  		secs = uint64(d / time.Second)
   850  		nsecs = uint32((d % time.Second) / time.Nanosecond)
   851  	}
   852  
   853  	return secs, nsecs
   854  }
   855  
   856  func convertChildInodeEntry(
   857  	in *fuseops.ChildInodeEntry,
   858  	out *fusekernel.EntryOut) {
   859  	out.Nodeid = uint64(in.Child)
   860  	out.Generation = uint64(in.Generation)
   861  	out.EntryValid, out.EntryValidNsec = convertExpirationTime(in.EntryExpiration)
   862  	out.AttrValid, out.AttrValidNsec = convertExpirationTime(in.AttributesExpiration)
   863  
   864  	convertAttributes(in.Child, &in.Attributes, &out.Attr)
   865  }
   866  
   867  func convertFileMode(unixMode uint32) os.FileMode {
   868  	mode := os.FileMode(unixMode & 0777)
   869  	switch unixMode & syscall.S_IFMT {
   870  	case syscall.S_IFREG:
   871  		// nothing
   872  	case syscall.S_IFDIR:
   873  		mode |= os.ModeDir
   874  	case syscall.S_IFCHR:
   875  		mode |= os.ModeCharDevice | os.ModeDevice
   876  	case syscall.S_IFBLK:
   877  		mode |= os.ModeDevice
   878  	case syscall.S_IFIFO:
   879  		mode |= os.ModeNamedPipe
   880  	case syscall.S_IFLNK:
   881  		mode |= os.ModeSymlink
   882  	case syscall.S_IFSOCK:
   883  		mode |= os.ModeSocket
   884  	default:
   885  		// no idea
   886  		mode |= os.ModeDevice
   887  	}
   888  	if unixMode&syscall.S_ISUID != 0 {
   889  		mode |= os.ModeSetuid
   890  	}
   891  	if unixMode&syscall.S_ISGID != 0 {
   892  		mode |= os.ModeSetgid
   893  	}
   894  	return mode
   895  }
   896  
   897  func writeXattrSize(m *buffer.OutMessage, size uint32) {
   898  	out := (*fusekernel.GetxattrOut)(m.Grow(int(unsafe.Sizeof(fusekernel.GetxattrOut{}))))
   899  	out.Size = size
   900  }