Killed off fuseops.Op.
commit
e7a97bf905
|
@ -26,7 +26,6 @@ import (
|
|||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/jacobsa/fuse/fuseops"
|
||||
"github.com/jacobsa/fuse/internal/buffer"
|
||||
"github.com/jacobsa/fuse/internal/fusekernel"
|
||||
)
|
||||
|
@ -90,7 +89,7 @@ type Connection struct {
|
|||
// context that the user uses to reply to the op.
|
||||
type opState struct {
|
||||
inMsg *buffer.InMessage
|
||||
op fuseops.Op
|
||||
op interface{}
|
||||
opID uint32 // For logging
|
||||
}
|
||||
|
||||
|
@ -396,7 +395,7 @@ func (c *Connection) writeMessage(msg []byte) (err error) {
|
|||
// /dev/fuse. It must not be called multiple times concurrently.
|
||||
//
|
||||
// LOCKS_EXCLUDED(c.mu)
|
||||
func (c *Connection) ReadOp() (ctx context.Context, op fuseops.Op, err error) {
|
||||
func (c *Connection) ReadOp() (ctx context.Context, op interface{}, err error) {
|
||||
// Keep going until we find a request we know how to convert.
|
||||
for {
|
||||
// Read the next message from the kernel.
|
||||
|
|
|
@ -27,13 +27,13 @@ import (
|
|||
"github.com/jacobsa/fuse/internal/fusekernel"
|
||||
)
|
||||
|
||||
// Convert a kernel message to an appropriate implementation of fuseops.Op. If
|
||||
// the op is unknown, a special unexported type will be used.
|
||||
// Convert a kernel message to an appropriate op. If the op is unknown, a
|
||||
// special unexported type will be used.
|
||||
//
|
||||
// The caller is responsible for arranging for the message to be destroyed.
|
||||
func convertInMessage(
|
||||
m *buffer.InMessage,
|
||||
protocol fusekernel.Protocol) (o fuseops.Op, err error) {
|
||||
protocol fusekernel.Protocol) (o interface{}, err error) {
|
||||
switch m.Header().Opcode {
|
||||
case fusekernel.OpLookup:
|
||||
buf := m.ConsumeBytes(m.Len())
|
||||
|
|
|
@ -18,7 +18,7 @@ import "syscall"
|
|||
|
||||
const (
|
||||
// Errors corresponding to kernel error numbers. These may be treated
|
||||
// specially by fuseops.Op.Respond methods.
|
||||
// specially by Connection.Reply.
|
||||
EEXIST = syscall.EEXIST
|
||||
EINVAL = syscall.EINVAL
|
||||
EIO = syscall.EIO
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package fuseops
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/jacobsa/fuse/internal/buffer"
|
||||
"github.com/jacobsa/reqtrace"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// An interface that all ops inside which commonOp is embedded must
|
||||
// implement.
|
||||
type internalOp interface {
|
||||
Op
|
||||
|
||||
// Create a response message for the kernel, leaving the leading
|
||||
// fusekernel.OutHeader untouched.
|
||||
//
|
||||
// Special case: a zero return value means that the kernel is not expecting a
|
||||
// response.
|
||||
kernelResponse() (b buffer.OutMessage)
|
||||
}
|
||||
|
||||
// A function that sends a reply message back to the kernel for the request
|
||||
// with the given fuse unique ID. The error argument is for informational
|
||||
// purposes only; the error to hand to the kernel is encoded in the message.
|
||||
type replyFunc func(Op, uint64, []byte, error) error
|
||||
|
||||
// A helper for embedding common behavior.
|
||||
type commonOp struct {
|
||||
// The context exposed to the user.
|
||||
ctx context.Context
|
||||
|
||||
// The op in which this struct is embedded.
|
||||
op internalOp
|
||||
|
||||
// The fuse unique ID of this request, as assigned by the kernel.
|
||||
fuseID uint64
|
||||
|
||||
// A function that can be used to send a reply to the kernel.
|
||||
sendReply replyFunc
|
||||
|
||||
// A function that can be used to log debug information about the op. The
|
||||
// first argument is a call depth.
|
||||
//
|
||||
// May be nil.
|
||||
debugLog func(int, string, ...interface{})
|
||||
|
||||
// A logger to be used for logging exceptional errors.
|
||||
//
|
||||
// May be nil.
|
||||
errorLogger *log.Logger
|
||||
}
|
||||
|
||||
func (o *commonOp) ShortDesc() (desc string) {
|
||||
v := reflect.ValueOf(o.op)
|
||||
opName := v.Type().String()
|
||||
|
||||
// Attempt to better handle the usual case: a string that looks like
|
||||
// "*fuseops.GetInodeAttributesOp".
|
||||
const prefix = "*fuseops."
|
||||
const suffix = "Op"
|
||||
if strings.HasPrefix(opName, prefix) && strings.HasSuffix(opName, suffix) {
|
||||
opName = opName[len(prefix) : len(opName)-len(suffix)]
|
||||
}
|
||||
|
||||
desc = opName
|
||||
|
||||
// Include the inode number to which the op applies, if possible.
|
||||
if f := v.Elem().FieldByName("Inode"); f.IsValid() {
|
||||
desc = fmt.Sprintf("%s(inode=%v)", desc, f.Interface())
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (o *commonOp) DebugString() string {
|
||||
// By default, defer to ShortDesc.
|
||||
return o.op.ShortDesc()
|
||||
}
|
||||
|
||||
func (o *commonOp) init(
|
||||
ctx context.Context,
|
||||
op internalOp,
|
||||
fuseID uint64,
|
||||
sendReply replyFunc,
|
||||
debugLog func(int, string, ...interface{}),
|
||||
errorLogger *log.Logger) {
|
||||
// Initialize basic fields.
|
||||
o.ctx = ctx
|
||||
o.op = op
|
||||
o.fuseID = fuseID
|
||||
o.sendReply = sendReply
|
||||
o.debugLog = debugLog
|
||||
o.errorLogger = errorLogger
|
||||
|
||||
// Set up a trace span for this op.
|
||||
var reportForTrace reqtrace.ReportFunc
|
||||
o.ctx, reportForTrace = reqtrace.StartSpan(o.ctx, o.op.ShortDesc())
|
||||
|
||||
// When the op is finished, report to both reqtrace and the connection.
|
||||
prevSendReply := o.sendReply
|
||||
o.sendReply = func(op Op, fuseID uint64, msg []byte, opErr error) (err error) {
|
||||
reportForTrace(opErr)
|
||||
err = prevSendReply(op, fuseID, msg, opErr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (o *commonOp) Context() context.Context {
|
||||
return o.ctx
|
||||
}
|
||||
|
||||
func (o *commonOp) Logf(format string, v ...interface{}) {
|
||||
if o.debugLog == nil {
|
||||
return
|
||||
}
|
||||
|
||||
const calldepth = 2
|
||||
o.debugLog(calldepth, format, v...)
|
||||
}
|
||||
|
||||
func (o *commonOp) Respond(err error) {
|
||||
// If successful, we ask the op for an appopriate response to the kernel, and
|
||||
// it is responsible for leaving room for the fusekernel.OutHeader struct.
|
||||
// Otherwise, create our own.
|
||||
var b buffer.OutMessage
|
||||
if err == nil {
|
||||
b = o.op.kernelResponse()
|
||||
} else {
|
||||
b = buffer.NewOutMessage(0)
|
||||
}
|
||||
|
||||
// Fill in the header if a reply is needed.
|
||||
msg := b.Bytes()
|
||||
if msg != nil {
|
||||
h := b.OutHeader()
|
||||
h.Unique = o.fuseID
|
||||
h.Len = uint32(len(msg))
|
||||
if err != nil {
|
||||
// If the user gave us a syscall.Errno, use that value in the reply.
|
||||
// Otherwise use the generic EIO.
|
||||
if errno, ok := err.(syscall.Errno); ok {
|
||||
h.Error = -int32(errno)
|
||||
} else {
|
||||
h.Error = -int32(syscall.EIO)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reply.
|
||||
replyErr := o.sendReply(o.op, o.fuseID, msg, err)
|
||||
if replyErr != nil && o.errorLogger != nil {
|
||||
o.errorLogger.Printf("Error from sendReply: %v", replyErr)
|
||||
}
|
||||
}
|
|
@ -12,7 +12,6 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package fuseops contains implementations of the fuse.Op interface that may
|
||||
// be returned by fuse.Connection.ReadOp. See documentation in that package for
|
||||
// more.
|
||||
// Package fuseops contains ops that may be returned by fuse.Connection.ReadOp.
|
||||
// See documentation in that package for more.
|
||||
package fuseops
|
||||
|
|
106
fuseops/ops.go
106
fuseops/ops.go
|
@ -15,24 +15,10 @@
|
|||
package fuseops
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/jacobsa/fuse/internal/fusekernel"
|
||||
)
|
||||
|
||||
// A common interface implemented by all ops in this package. Use a type switch
|
||||
// to find particular concrete types, responding with fuse.ENOSYS if a type is
|
||||
// not supported.
|
||||
type Op interface {
|
||||
// A short description of the op, to be used in logging.
|
||||
ShortDesc() string
|
||||
|
||||
// A long description of the op, to be used in debug logging.
|
||||
DebugString() string
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Inodes
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
@ -40,9 +26,6 @@ type Op interface {
|
|||
// Look up a child by name within a parent directory. The kernel sends this
|
||||
// when resolving user paths to dentry structs, which are then cached.
|
||||
type LookUpInodeOp struct {
|
||||
commonOp
|
||||
protocol fusekernel.Protocol
|
||||
|
||||
// The ID of the directory inode to which the child belongs.
|
||||
Parent InodeID
|
||||
|
||||
|
@ -64,19 +47,11 @@ type LookUpInodeOp struct {
|
|||
Entry ChildInodeEntry
|
||||
}
|
||||
|
||||
func (o *LookUpInodeOp) ShortDesc() (desc string) {
|
||||
desc = fmt.Sprintf("LookUpInode(parent=%v, name=%q)", o.Parent, o.Name)
|
||||
return
|
||||
}
|
||||
|
||||
// Refresh the attributes for an inode whose ID was previously returned in a
|
||||
// LookUpInodeOp. The kernel sends this when the FUSE VFS layer's cache of
|
||||
// inode attributes is stale. This is controlled by the AttributesExpiration
|
||||
// field of ChildInodeEntry, etc.
|
||||
type GetInodeAttributesOp struct {
|
||||
commonOp
|
||||
protocol fusekernel.Protocol
|
||||
|
||||
// The inode of interest.
|
||||
Inode InodeID
|
||||
|
||||
|
@ -87,22 +62,11 @@ type GetInodeAttributesOp struct {
|
|||
AttributesExpiration time.Time
|
||||
}
|
||||
|
||||
func (o *GetInodeAttributesOp) DebugString() string {
|
||||
return fmt.Sprintf(
|
||||
"Inode: %d, Exp: %v, Attr: %s",
|
||||
o.Inode,
|
||||
o.AttributesExpiration,
|
||||
o.Attributes.DebugString())
|
||||
}
|
||||
|
||||
// Change attributes for an inode.
|
||||
//
|
||||
// The kernel sends this for obvious cases like chmod(2), and for less obvious
|
||||
// cases like ftrunctate(2).
|
||||
type SetInodeAttributesOp struct {
|
||||
commonOp
|
||||
protocol fusekernel.Protocol
|
||||
|
||||
// The inode of interest.
|
||||
Inode InodeID
|
||||
|
||||
|
@ -159,8 +123,6 @@ type SetInodeAttributesOp struct {
|
|||
// Rather they should take fuse.Connection.ReadOp returning io.EOF as
|
||||
// implicitly decrementing all lookup counts to zero.
|
||||
type ForgetInodeOp struct {
|
||||
commonOp
|
||||
|
||||
// The inode whose reference count should be decremented.
|
||||
Inode InodeID
|
||||
|
||||
|
@ -184,9 +146,6 @@ type ForgetInodeOp struct {
|
|||
//
|
||||
// Therefore the file system should return EEXIST if the name already exists.
|
||||
type MkDirOp struct {
|
||||
commonOp
|
||||
protocol fusekernel.Protocol
|
||||
|
||||
// The ID of parent directory inode within which to create the child.
|
||||
Parent InodeID
|
||||
|
||||
|
@ -201,11 +160,6 @@ type MkDirOp struct {
|
|||
Entry ChildInodeEntry
|
||||
}
|
||||
|
||||
func (o *MkDirOp) ShortDesc() (desc string) {
|
||||
desc = fmt.Sprintf("MkDir(parent=%v, name=%q)", o.Parent, o.Name)
|
||||
return
|
||||
}
|
||||
|
||||
// Create a file inode and open it.
|
||||
//
|
||||
// The kernel sends this when the user asks to open a file with the O_CREAT
|
||||
|
@ -217,9 +171,6 @@ func (o *MkDirOp) ShortDesc() (desc string) {
|
|||
//
|
||||
// Therefore the file system should return EEXIST if the name already exists.
|
||||
type CreateFileOp struct {
|
||||
commonOp
|
||||
protocol fusekernel.Protocol
|
||||
|
||||
// The ID of parent directory inode within which to create the child file.
|
||||
Parent InodeID
|
||||
|
||||
|
@ -244,17 +195,9 @@ type CreateFileOp struct {
|
|||
Handle HandleID
|
||||
}
|
||||
|
||||
func (o *CreateFileOp) ShortDesc() (desc string) {
|
||||
desc = fmt.Sprintf("CreateFile(parent=%v, name=%q)", o.Parent, o.Name)
|
||||
return
|
||||
}
|
||||
|
||||
// Create a symlink inode. If the name already exists, the file system should
|
||||
// return EEXIST (cf. the notes on CreateFileOp and MkDirOp).
|
||||
type CreateSymlinkOp struct {
|
||||
commonOp
|
||||
protocol fusekernel.Protocol
|
||||
|
||||
// The ID of parent directory inode within which to create the child symlink.
|
||||
Parent InodeID
|
||||
|
||||
|
@ -272,16 +215,6 @@ type CreateSymlinkOp struct {
|
|||
Entry ChildInodeEntry
|
||||
}
|
||||
|
||||
func (o *CreateSymlinkOp) ShortDesc() (desc string) {
|
||||
desc = fmt.Sprintf(
|
||||
"CreateSymlink(parent=%v, name=%q, target=%q)",
|
||||
o.Parent,
|
||||
o.Name,
|
||||
o.Target)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Unlinking
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
@ -321,8 +254,6 @@ func (o *CreateSymlinkOp) ShortDesc() (desc string) {
|
|||
// about this.
|
||||
//
|
||||
type RenameOp struct {
|
||||
commonOp
|
||||
|
||||
// The old parent directory, and the name of the entry within it to be
|
||||
// relocated.
|
||||
OldParent InodeID
|
||||
|
@ -342,8 +273,6 @@ type RenameOp struct {
|
|||
//
|
||||
// Sample implementation in ext2: ext2_rmdir (http://goo.gl/B9QmFf)
|
||||
type RmDirOp struct {
|
||||
commonOp
|
||||
|
||||
// The ID of parent directory inode, and the name of the directory being
|
||||
// removed within it.
|
||||
Parent InodeID
|
||||
|
@ -357,8 +286,6 @@ type RmDirOp struct {
|
|||
//
|
||||
// Sample implementation in ext2: ext2_unlink (http://goo.gl/hY6r6C)
|
||||
type UnlinkOp struct {
|
||||
commonOp
|
||||
|
||||
// The ID of parent directory inode, and the name of the entry being removed
|
||||
// within it.
|
||||
Parent InodeID
|
||||
|
@ -376,8 +303,6 @@ type UnlinkOp struct {
|
|||
// user-space process. On OS X it may not be sent for every open(2) (cf.
|
||||
// https://github.com/osxfuse/osxfuse/issues/199).
|
||||
type OpenDirOp struct {
|
||||
commonOp
|
||||
|
||||
// The ID of the inode to be opened.
|
||||
Inode InodeID
|
||||
|
||||
|
@ -394,8 +319,6 @@ type OpenDirOp struct {
|
|||
|
||||
// Read entries from a directory previously opened with OpenDir.
|
||||
type ReadDirOp struct {
|
||||
commonOp
|
||||
|
||||
// The directory inode that we are reading, and the handle previously
|
||||
// returned by OpenDir when opening that inode.
|
||||
Inode InodeID
|
||||
|
@ -491,8 +414,6 @@ type ReadDirOp struct {
|
|||
//
|
||||
// Errors from this op are ignored by the kernel (cf. http://goo.gl/RL38Do).
|
||||
type ReleaseDirHandleOp struct {
|
||||
commonOp
|
||||
|
||||
// The handle ID to be released. The kernel guarantees that this ID will not
|
||||
// be used in further calls to the file system (unless it is reissued by the
|
||||
// file system).
|
||||
|
@ -510,8 +431,6 @@ type ReleaseDirHandleOp struct {
|
|||
// process. On OS X it may not be sent for every open(2)
|
||||
// (cf.https://github.com/osxfuse/osxfuse/issues/199).
|
||||
type OpenFileOp struct {
|
||||
commonOp
|
||||
|
||||
// The ID of the inode to be opened.
|
||||
Inode InodeID
|
||||
|
||||
|
@ -531,8 +450,6 @@ type OpenFileOp struct {
|
|||
// some reads may be served by the page cache. See notes on WriteFileOp for
|
||||
// more.
|
||||
type ReadFileOp struct {
|
||||
commonOp
|
||||
|
||||
// The file inode that we are reading, and the handle previously returned by
|
||||
// CreateFile or OpenFile when opening that inode.
|
||||
Inode InodeID
|
||||
|
@ -586,8 +503,6 @@ type ReadFileOp struct {
|
|||
// (See also http://goo.gl/ocdTdM, fuse-devel thread "Fuse guarantees on
|
||||
// concurrent requests".)
|
||||
type WriteFileOp struct {
|
||||
commonOp
|
||||
|
||||
// The file inode that we are modifying, and the handle previously returned
|
||||
// by CreateFile or OpenFile when opening that inode.
|
||||
Inode InodeID
|
||||
|
@ -641,8 +556,6 @@ type WriteFileOp struct {
|
|||
// See also: FlushFileOp, which may perform a similar function when closing a
|
||||
// file (but which is not used in "real" file systems).
|
||||
type SyncFileOp struct {
|
||||
commonOp
|
||||
|
||||
// The file and handle being sync'd.
|
||||
Inode InodeID
|
||||
Handle HandleID
|
||||
|
@ -696,8 +609,6 @@ type SyncFileOp struct {
|
|||
// to at least schedule a real flush, and maybe do it immediately in order to
|
||||
// return any errors that occur.
|
||||
type FlushFileOp struct {
|
||||
commonOp
|
||||
|
||||
// The file and handle being flushed.
|
||||
Inode InodeID
|
||||
Handle HandleID
|
||||
|
@ -712,35 +623,18 @@ type FlushFileOp struct {
|
|||
//
|
||||
// Errors from this op are ignored by the kernel (cf. http://goo.gl/RL38Do).
|
||||
type ReleaseFileHandleOp struct {
|
||||
commonOp
|
||||
|
||||
// The handle ID to be released. The kernel guarantees that this ID will not
|
||||
// be used in further calls to the file system (unless it is reissued by the
|
||||
// file system).
|
||||
Handle HandleID
|
||||
}
|
||||
|
||||
// A sentinel used for unknown ops. The user is expected to respond with a
|
||||
// non-nil error.
|
||||
type unknownOp struct {
|
||||
commonOp
|
||||
opCode uint32
|
||||
inode InodeID
|
||||
}
|
||||
|
||||
func (o *unknownOp) ShortDesc() (desc string) {
|
||||
desc = fmt.Sprintf("<opcode %d>(inode=%v)", o.opCode, o.inode)
|
||||
return
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Reading symlinks
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Read the target of a symlink inode.
|
||||
type ReadSymlinkOp struct {
|
||||
commonOp
|
||||
|
||||
// The symlink inode that we are reading.
|
||||
Inode InodeID
|
||||
|
||||
|
|
|
@ -29,8 +29,8 @@ import (
|
|||
// loop" that switches on op types, instead receiving typed method calls
|
||||
// directly.
|
||||
//
|
||||
// The FileSystem implementation should not call Op.Respond, instead returning
|
||||
// the error with which the caller should respond.
|
||||
// The FileSystem implementation should not call Connection.Reply, instead
|
||||
// returning the error with which the caller should respond.
|
||||
//
|
||||
// See NotImplementedFileSystem for a convenient way to embed default
|
||||
// implementations for methods you don't care about.
|
||||
|
@ -110,7 +110,7 @@ func (s *fileSystemServer) ServeOps(c *fuse.Connection) {
|
|||
func (s *fileSystemServer) handleOp(
|
||||
c *fuse.Connection,
|
||||
ctx context.Context,
|
||||
op fuseops.Op) {
|
||||
op interface{}) {
|
||||
defer s.opsInFlight.Done()
|
||||
|
||||
// Dispatch to the appropriate method.
|
||||
|
|
4
ops.go
4
ops.go
|
@ -28,7 +28,7 @@ import (
|
|||
// response, return a nil response.
|
||||
func kernelResponse(
|
||||
fuseID uint64,
|
||||
op fuseops.Op,
|
||||
op interface{},
|
||||
opErr error,
|
||||
protocol fusekernel.Protocol) (msg []byte) {
|
||||
// If the user replied with an error, create room enough just for the result
|
||||
|
@ -59,7 +59,7 @@ func kernelResponse(
|
|||
// Like kernelResponse, but assumes the user replied with a nil error to the
|
||||
// op.
|
||||
func kernelResponseForOp(
|
||||
op fuseops.Op,
|
||||
op interface{},
|
||||
protocol fusekernel.Protocol) (b buffer.OutMessage) {
|
||||
// Create the appropriate output message
|
||||
switch o := op.(type) {
|
||||
|
|
Loading…
Reference in New Issue