Beispiel #1
0
// Fill in the response that should be sent to the kernel, or set noResponse if
// the op requires no response.
func (c *Connection) kernelResponse(
	m *buffer.OutMessage,
	fuseID uint64,
	op interface{},
	opErr error) (noResponse bool) {
	h := m.OutHeader()
	h.Unique = fuseID

	// Special case: handle the ops for which the kernel expects no response.
	// interruptOp .
	switch op.(type) {
	case *fuseops.ForgetInodeOp:
		noResponse = true
		return

	case *interruptOp:
		noResponse = true
		return
	}

	// If the user returned the error, fill in the error field of the outgoing
	// message header.
	if opErr != nil {
		m.OutHeader().Error = -int32(syscall.EIO)
		if errno, ok := opErr.(syscall.Errno); ok {
			m.OutHeader().Error = -int32(errno)
		}

		// Special case: for some types, convertInMessage grew the message in order
		// to obtain a destination buffer. Make sure that we shrink back to just
		// the header, because on OS X the kernel otherwise returns EINVAL when we
		// attempt to write an error response with a length that extends beyond the
		// header.
		m.ShrinkTo(buffer.OutMessageInitialSize)
	}

	// Otherwise, fill in the rest of the response.
	if opErr == nil {
		c.kernelResponseForOp(m, op)
	}

	h.Len = uint32(m.Len())
	return
}
Beispiel #2
0
// Like kernelResponse, but assumes the user replied with a nil error to the
// op.
func (c *Connection) kernelResponseForOp(
	m *buffer.OutMessage,
	op interface{}) {
	// Create the appropriate output message
	switch o := op.(type) {
	case *fuseops.LookUpInodeOp:
		size := fusekernel.EntryOutSize(c.protocol)
		out := (*fusekernel.EntryOut)(m.Grow(size))
		convertChildInodeEntry(&o.Entry, out)

	case *fuseops.GetInodeAttributesOp:
		size := fusekernel.AttrOutSize(c.protocol)
		out := (*fusekernel.AttrOut)(m.Grow(size))
		out.AttrValid, out.AttrValidNsec = convertExpirationTime(
			o.AttributesExpiration)
		convertAttributes(o.Inode, &o.Attributes, &out.Attr)

	case *fuseops.SetInodeAttributesOp:
		size := fusekernel.AttrOutSize(c.protocol)
		out := (*fusekernel.AttrOut)(m.Grow(size))
		out.AttrValid, out.AttrValidNsec = convertExpirationTime(
			o.AttributesExpiration)
		convertAttributes(o.Inode, &o.Attributes, &out.Attr)

	case *fuseops.MkDirOp:
		size := fusekernel.EntryOutSize(c.protocol)
		out := (*fusekernel.EntryOut)(m.Grow(size))
		convertChildInodeEntry(&o.Entry, out)

	case *fuseops.CreateFileOp:
		eSize := fusekernel.EntryOutSize(c.protocol)

		e := (*fusekernel.EntryOut)(m.Grow(eSize))
		convertChildInodeEntry(&o.Entry, e)

		oo := (*fusekernel.OpenOut)(m.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
		oo.Fh = uint64(o.Handle)

	case *fuseops.CreateSymlinkOp:
		size := fusekernel.EntryOutSize(c.protocol)
		out := (*fusekernel.EntryOut)(m.Grow(size))
		convertChildInodeEntry(&o.Entry, out)

	case *fuseops.RenameOp:
		// Empty response

	case *fuseops.RmDirOp:
		// Empty response

	case *fuseops.UnlinkOp:
		// Empty response

	case *fuseops.OpenDirOp:
		out := (*fusekernel.OpenOut)(m.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
		out.Fh = uint64(o.Handle)

	case *fuseops.ReadDirOp:
		// convertInMessage already set up the destination buffer to be at the end
		// of the out message. We need only shrink to the right size based on how
		// much the user read.
		m.ShrinkTo(buffer.OutMessageInitialSize + uintptr(o.BytesRead))

	case *fuseops.ReleaseDirHandleOp:
		// Empty response

	case *fuseops.OpenFileOp:
		out := (*fusekernel.OpenOut)(m.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
		out.Fh = uint64(o.Handle)

		if o.KeepPageCache {
			out.OpenFlags |= uint32(fusekernel.OpenKeepCache)
		}

	case *fuseops.ReadFileOp:
		// convertInMessage already set up the destination buffer to be at the end
		// of the out message. We need only shrink to the right size based on how
		// much the user read.
		m.ShrinkTo(buffer.OutMessageInitialSize + uintptr(o.BytesRead))

	case *fuseops.WriteFileOp:
		out := (*fusekernel.WriteOut)(m.Grow(unsafe.Sizeof(fusekernel.WriteOut{})))
		out.Size = uint32(len(o.Data))

	case *fuseops.SyncFileOp:
		// Empty response

	case *fuseops.FlushFileOp:
		// Empty response

	case *fuseops.ReleaseFileHandleOp:
		// Empty response

	case *fuseops.ReadSymlinkOp:
		m.AppendString(o.Target)

	case *statFSOp:
		m.Grow(unsafe.Sizeof(fusekernel.StatfsOut{}))

	case *initOp:
		out := (*fusekernel.InitOut)(m.Grow(unsafe.Sizeof(fusekernel.InitOut{})))

		out.Major = o.Library.Major
		out.Minor = o.Library.Minor
		out.MaxReadahead = o.MaxReadahead
		out.Flags = uint32(o.Flags)
		out.MaxWrite = o.MaxWrite

	default:
		panic(fmt.Sprintf("Unexpected op: %#v", op))
	}

	return
}
Beispiel #3
0
// Like kernelResponse, but assumes the user replied with a nil error to the
// op.
func (c *Connection) kernelResponseForOp(
	m *buffer.OutMessage,
	op interface{}) {
	// Create the appropriate output message
	switch o := op.(type) {
	case *fuseops.LookUpInodeOp:
		size := fusekernel.EntryOutSize(c.protocol)
		out := (*fusekernel.EntryOut)(m.Grow(size))
		convertChildInodeEntry(&o.Entry, out)

	case *fuseops.GetInodeAttributesOp:
		size := fusekernel.AttrOutSize(c.protocol)
		out := (*fusekernel.AttrOut)(m.Grow(size))
		out.AttrValid, out.AttrValidNsec = convertExpirationTime(
			o.AttributesExpiration)
		convertAttributes(o.Inode, &o.Attributes, &out.Attr)

	case *fuseops.SetInodeAttributesOp:
		size := fusekernel.AttrOutSize(c.protocol)
		out := (*fusekernel.AttrOut)(m.Grow(size))
		out.AttrValid, out.AttrValidNsec = convertExpirationTime(
			o.AttributesExpiration)
		convertAttributes(o.Inode, &o.Attributes, &out.Attr)

	case *fuseops.MkDirOp:
		size := fusekernel.EntryOutSize(c.protocol)
		out := (*fusekernel.EntryOut)(m.Grow(size))
		convertChildInodeEntry(&o.Entry, out)

	case *fuseops.CreateFileOp:
		eSize := fusekernel.EntryOutSize(c.protocol)

		e := (*fusekernel.EntryOut)(m.Grow(eSize))
		convertChildInodeEntry(&o.Entry, e)

		oo := (*fusekernel.OpenOut)(m.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
		oo.Fh = uint64(o.Handle)

	case *fuseops.CreateSymlinkOp:
		size := fusekernel.EntryOutSize(c.protocol)
		out := (*fusekernel.EntryOut)(m.Grow(size))
		convertChildInodeEntry(&o.Entry, out)

	case *fuseops.RenameOp:
		// Empty response

	case *fuseops.RmDirOp:
		// Empty response

	case *fuseops.UnlinkOp:
		// Empty response

	case *fuseops.OpenDirOp:
		out := (*fusekernel.OpenOut)(m.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
		out.Fh = uint64(o.Handle)

	case *fuseops.ReadDirOp:
		// convertInMessage already set up the destination buffer to be at the end
		// of the out message. We need only shrink to the right size based on how
		// much the user read.
		m.ShrinkTo(buffer.OutMessageInitialSize + uintptr(o.BytesRead))

	case *fuseops.ReleaseDirHandleOp:
		// Empty response

	case *fuseops.OpenFileOp:
		out := (*fusekernel.OpenOut)(m.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
		out.Fh = uint64(o.Handle)

		if o.KeepPageCache {
			out.OpenFlags |= uint32(fusekernel.OpenKeepCache)
		}

	case *fuseops.ReadFileOp:
		// convertInMessage already set up the destination buffer to be at the end
		// of the out message. We need only shrink to the right size based on how
		// much the user read.
		m.ShrinkTo(buffer.OutMessageInitialSize + uintptr(o.BytesRead))

	case *fuseops.WriteFileOp:
		out := (*fusekernel.WriteOut)(m.Grow(unsafe.Sizeof(fusekernel.WriteOut{})))
		out.Size = uint32(len(o.Data))

	case *fuseops.SyncFileOp:
		// Empty response

	case *fuseops.FlushFileOp:
		// Empty response

	case *fuseops.ReleaseFileHandleOp:
		// Empty response

	case *fuseops.ReadSymlinkOp:
		m.AppendString(o.Target)

	case *fuseops.StatFSOp:
		out := (*fusekernel.StatfsOut)(m.Grow(unsafe.Sizeof(fusekernel.StatfsOut{})))
		out.St.Blocks = o.Blocks
		out.St.Bfree = o.BlocksFree
		out.St.Bavail = o.BlocksAvailable
		out.St.Files = o.Inodes
		out.St.Ffree = o.InodesFree

		// The posix spec for sys/statvfs.h (http://goo.gl/LktgrF) defines the
		// following fields of statvfs, among others:
		//
		//     f_bsize    File system block size.
		//     f_frsize   Fundamental file system block size.
		//     f_blocks   Total number of blocks on file system in units of f_frsize.
		//
		// It appears as though f_bsize was the only thing supported by most unixes
		// originally, but then f_frsize was added when new sorts of file systems
		// came about. Quoth The Linux Programming Interface by Michael Kerrisk
		// (https://goo.gl/5LZMxQ):
		//
		//     For most Linux file systems, the values of f_bsize and f_frsize are
		//     the same. However, some file systems support the notion of block
		//     fragments, which can be used to allocate a smaller unit of storage
		//     at the end of the file if if a full block is not required. This
		//     avoids the waste of space that would otherwise occur if a full block
		//     was allocated. On such file systems, f_frsize is the size of a
		//     fragment, and f_bsize is the size of a whole block. (The notion of
		//     fragments in UNIX file systems first appeared in the early 1980s
		//     with the 4.2BSD Fast File System.)
		//
		// Confusingly, it appears as though osxfuse surfaces fuse_kstatfs::bsize
		// as statfs::f_iosize (of advisory use only), and fuse_kstatfs::frsize as
		// statfs::f_bsize (which affects free space display in the Finder).
		out.St.Bsize = o.IoSize
		out.St.Frsize = o.BlockSize

	case *initOp:
		out := (*fusekernel.InitOut)(m.Grow(unsafe.Sizeof(fusekernel.InitOut{})))

		out.Major = o.Library.Major
		out.Minor = o.Library.Minor
		out.MaxReadahead = o.MaxReadahead
		out.Flags = uint32(o.Flags)
		out.MaxWrite = o.MaxWrite

	default:
		panic(fmt.Sprintf("Unexpected op: %#v", op))
	}

	return
}