Commit e7a97bf9 authored by Aaron Jacobs's avatar Aaron Jacobs

Killed off fuseops.Op.

parents ef3d11e2 a99d69ab
......@@ -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
......
// 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
......@@ -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.
......
......@@ -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) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment