Refactored the way the user obtains contexts and replies to ops.

The new interface gives much much cleaner documentation for package
fuseops, which is what we mostly look at. In particular, there isn't a
ton of stuttering in the method listings.

The change also simplifies the implementation of package fuseops, which
was becoming super unwieldy.
geesefs-0-30-9
Aaron Jacobs 2015-07-27 15:56:27 +10:00
commit 83630d4268
15 changed files with 525 additions and 786 deletions

View File

@ -26,11 +26,14 @@ import (
"golang.org/x/net/context"
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/internal/buffer"
"github.com/jacobsa/fuse/internal/fusekernel"
)
type contextKeyType uint64
const contextKey contextKeyType = 0
// Ask the Linux kernel for larger read requests.
//
// As of 2015-03-26, the behavior in the kernel is:
@ -82,6 +85,14 @@ type Connection struct {
cancelFuncs map[uint64]func()
}
// State that is maintained for each in-flight op. This is stuffed into the
// context that the user uses to reply to the op.
type opState struct {
inMsg *buffer.InMessage
op interface{}
opID uint32 // For logging
}
// Create a connection wrapping the supplied file descriptor connected to the
// kernel. You must eventually call c.close().
//
@ -113,15 +124,16 @@ func newConnection(
// Do the work necessary to cause the mount process to complete.
func (c *Connection) Init() (err error) {
// Read the init op.
op, err := c.ReadOp()
ctx, op, err := c.ReadOp()
if err != nil {
err = fmt.Errorf("Reading init op: %v", err)
return
}
initOp, ok := op.(*fuseops.InternalInitOp)
initOp, ok := op.(*initOp)
if !ok {
err = fmt.Errorf("Expected *fuseops.InternalInitOp, got %T", op)
c.Reply(ctx, syscall.EPROTO)
err = fmt.Errorf("Expected *initOp, got %T", op)
return
}
@ -132,7 +144,7 @@ func (c *Connection) Init() (err error) {
}
if initOp.Kernel.LT(min) {
initOp.Respond(syscall.EPROTO)
c.Reply(ctx, syscall.EPROTO)
err = fmt.Errorf("Version too old: %v", initOp.Kernel)
return
}
@ -152,8 +164,8 @@ func (c *Connection) Init() (err error) {
initOp.MaxReadahead = maxReadahead
initOp.MaxWrite = buffer.MaxWriteSize
initOp.Flags = fusekernel.InitBigWrites
initOp.Respond(nil)
c.Reply(ctx, nil)
return
}
@ -372,14 +384,18 @@ func (c *Connection) writeMessage(msg []byte) (err error) {
return
}
// Read the next op from the kernel process. Return io.EOF if the kernel has
// closed the connection.
// Read the next op from the kernel process, returning the op and a context
// that should be used for work related to the op. Return io.EOF if the kernel
// has closed the connection.
//
// If err != nil, the user is responsible for later calling c.Reply with the
// returned context.
//
// This function delivers ops in exactly the order they are received from
// /dev/fuse. It must not be called multiple times concurrently.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) ReadOp() (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.
@ -389,90 +405,91 @@ func (c *Connection) ReadOp() (op fuseops.Op, err error) {
return
}
// Choose an ID for this operation for the purposes of logging.
// Convert the message to an op.
op, err = convertInMessage(m, c.protocol)
if err != nil {
err = fmt.Errorf("convertInMessage: %v", err)
return
}
// Choose an ID for this operation for the purposes of logging, and log it.
opID := c.nextOpID
c.nextOpID++
// Set up op dependencies.
opCtx := c.beginOp(m.Header().Opcode, m.Header().Unique)
c.debugLog(opID, 1, "<- %#v", op)
var debugLogForOp func(int, string, ...interface{})
if c.debugLogger != nil {
debugLogForOp = func(calldepth int, format string, v ...interface{}) {
c.debugLog(opID, calldepth+1, format, v...)
}
}
sendReply := func(
op fuseops.Op,
fuseID uint64,
replyMsg []byte,
opErr error) (err error) {
// Make sure we destroy the message, as required by readMessage.
defer c.destroyInMessage(m)
// Clean up state for this op.
c.finishOp(m.Header().Opcode, m.Header().Unique)
// Debug logging
if c.debugLogger != nil {
if opErr == nil {
op.Logf("-> OK: %s", op.DebugString())
} else {
op.Logf("-> error: %v", opErr)
}
}
// Error logging
if opErr != nil && c.errorLogger != nil {
c.errorLogger.Printf("(%s) error: %v", op.ShortDesc(), opErr)
}
// Send the reply to the kernel.
err = c.writeMessage(replyMsg)
if err != nil {
err = fmt.Errorf("writeMessage: %v", err)
return
}
return
}
// Convert the message to an Op.
op, err = fuseops.Convert(
opCtx,
m,
c.protocol,
debugLogForOp,
c.errorLogger,
sendReply)
if err != nil {
err = fmt.Errorf("fuseops.Convert: %v", err)
return
}
// Log the receipt of the operation.
c.debugLog(opID, 1, "<- %v", op.ShortDesc())
// Special case: responding to statfs is required to make mounting work on
// OS X. We don't currently expose the capability for the file system to
// intercept this.
if _, ok := op.(*fuseops.InternalStatFSOp); ok {
op.Respond(nil)
continue
}
// Special case: handle interrupt requests.
if interruptOp, ok := op.(*fuseops.InternalInterruptOp); ok {
// Special case: handle interrupt requests inline.
if interruptOp, ok := op.(*interruptOp); ok {
c.handleInterrupt(interruptOp.FuseID)
continue
}
// Set up a context that remembers information about this op.
ctx = c.beginOp(m.Header().Opcode, m.Header().Unique)
ctx = context.WithValue(ctx, contextKey, opState{m, op, opID})
// Special case: responding to statfs is required to make mounting work on
// OS X. We don't currently expose the capability for the file system to
// intercept this.
if _, ok := op.(*statFSOp); ok {
c.Reply(ctx, nil)
continue
}
// Return the op to the user.
return
}
}
// Reply to an op previously read using ReadOp, with the supplied error (or nil
// if successful). The context must be the context returned by ReadOp.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) Reply(ctx context.Context, opErr error) {
// Extract the state we stuffed in earlier.
state, ok := ctx.Value(contextKey).(opState)
if !ok {
panic(fmt.Sprintf("Reply called with invalid context: %#v", ctx))
}
op := state.op
m := state.inMsg
opID := state.opID
// Make sure we destroy the message when we're done.
defer c.destroyInMessage(m)
// Clean up state for this op.
c.finishOp(m.Header().Opcode, m.Header().Unique)
// Debug logging
if c.debugLogger != nil {
if opErr == nil {
c.debugLog(opID, 1, "-> OK: %#v", op)
} else {
c.debugLog(opID, 1, "-> error: %v", opErr)
}
}
// Error logging
if opErr != nil && c.errorLogger != nil {
c.errorLogger.Printf("(%#v) error: %v", op, opErr)
}
// Send the reply to the kernel.
replyMsg := kernelResponse(m.Header().Unique, op, opErr, c.protocol)
if replyMsg != nil {
if err := c.writeMessage(replyMsg); err != nil {
if c.errorLogger != nil {
c.errorLogger.Printf("writeMessage: %v", err)
}
return
}
}
}
// Close the connection. Must not be called until operations that were read
// from the connection have been responded to.
func (c *Connection) close() (err error) {

View File

@ -12,41 +12,33 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package fuseops
package fuse
import (
"bytes"
"errors"
"log"
"fmt"
"os"
"syscall"
"time"
"unsafe"
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/internal/buffer"
"github.com/jacobsa/fuse/internal/fusekernel"
"golang.org/x/net/context"
)
// This function is an implementation detail of the fuse package, and must not
// be called by anyone else.
//
// Convert the supplied fuse kernel message to an Op. sendReply will be used to
// send the reply back to the kernel once the user calls o.Respond. If the op
// is unknown, a special unexported type will be used.
//
// The debug logging function and error logger may be nil. The caller is
// responsible for arranging for the message to be destroyed.
func Convert(
opCtx context.Context,
m *buffer.InMessage,
protocol fusekernel.Protocol,
debugLogForOp func(int, string, ...interface{}),
errorLogger *log.Logger,
sendReply replyFunc) (o Op, err error) {
var co *commonOp
////////////////////////////////////////////////////////////////////////
// Incoming messages
////////////////////////////////////////////////////////////////////////
var io internalOp
// 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 interface{}, err error) {
switch m.Header().Opcode {
case fusekernel.OpLookup:
buf := m.ConsumeBytes(m.Len())
@ -56,21 +48,15 @@ func Convert(
return
}
to := &LookUpInodeOp{
protocol: protocol,
Parent: InodeID(m.Header().Nodeid),
Name: string(buf[:n-1]),
o = &fuseops.LookUpInodeOp{
Parent: fuseops.InodeID(m.Header().Nodeid),
Name: string(buf[:n-1]),
}
io = to
co = &to.commonOp
case fusekernel.OpGetattr:
to := &GetInodeAttributesOp{
protocol: protocol,
Inode: InodeID(m.Header().Nodeid),
o = &fuseops.GetInodeAttributesOp{
Inode: fuseops.InodeID(m.Header().Nodeid),
}
io = to
co = &to.commonOp
case fusekernel.OpSetattr:
type input fusekernel.SetattrIn
@ -80,10 +66,10 @@ func Convert(
return
}
to := &SetInodeAttributesOp{
protocol: protocol,
Inode: InodeID(m.Header().Nodeid),
to := &fuseops.SetInodeAttributesOp{
Inode: fuseops.InodeID(m.Header().Nodeid),
}
o = to
valid := fusekernel.SetattrValid(in.Valid)
if valid&fusekernel.SetattrSize != 0 {
@ -105,9 +91,6 @@ func Convert(
to.Mtime = &t
}
io = to
co = &to.commonOp
case fusekernel.OpForget:
type input fusekernel.ForgetIn
in := (*input)(m.Consume(unsafe.Sizeof(input{})))
@ -116,12 +99,10 @@ func Convert(
return
}
to := &ForgetInodeOp{
Inode: InodeID(m.Header().Nodeid),
o = &fuseops.ForgetInodeOp{
Inode: fuseops.InodeID(m.Header().Nodeid),
N: in.Nlookup,
}
io = to
co = &to.commonOp
case fusekernel.OpMkdir:
in := (*fusekernel.MkdirIn)(m.Consume(fusekernel.MkdirInSize(protocol)))
@ -138,10 +119,9 @@ func Convert(
}
name = name[:i]
to := &MkDirOp{
protocol: protocol,
Parent: InodeID(m.Header().Nodeid),
Name: string(name),
o = &fuseops.MkDirOp{
Parent: fuseops.InodeID(m.Header().Nodeid),
Name: string(name),
// On Linux, vfs_mkdir calls through to the inode with at most
// permissions and sticky bits set (cf. https://goo.gl/WxgQXk), and fuse
@ -152,9 +132,6 @@ func Convert(
Mode: convertFileMode(in.Mode) | os.ModeDir,
}
io = to
co = &to.commonOp
case fusekernel.OpCreate:
in := (*fusekernel.CreateIn)(m.Consume(fusekernel.CreateInSize(protocol)))
if in == nil {
@ -170,14 +147,11 @@ func Convert(
}
name = name[:i]
to := &CreateFileOp{
protocol: protocol,
Parent: InodeID(m.Header().Nodeid),
Name: string(name),
Mode: convertFileMode(in.Mode),
o = &fuseops.CreateFileOp{
Parent: fuseops.InodeID(m.Header().Nodeid),
Name: string(name),
Mode: convertFileMode(in.Mode),
}
io = to
co = &to.commonOp
case fusekernel.OpSymlink:
// The message is "newName\0target\0".
@ -193,14 +167,11 @@ func Convert(
}
newName, target := names[0:i], names[i+1:len(names)-1]
to := &CreateSymlinkOp{
protocol: protocol,
Parent: InodeID(m.Header().Nodeid),
Name: string(newName),
Target: string(target),
o = &fuseops.CreateSymlinkOp{
Parent: fuseops.InodeID(m.Header().Nodeid),
Name: string(newName),
Target: string(target),
}
io = to
co = &to.commonOp
case fusekernel.OpRename:
type input fusekernel.RenameIn
@ -227,14 +198,12 @@ func Convert(
}
oldName, newName := names[:i], names[i+1:len(names)-1]
to := &RenameOp{
OldParent: InodeID(m.Header().Nodeid),
o = &fuseops.RenameOp{
OldParent: fuseops.InodeID(m.Header().Nodeid),
OldName: string(oldName),
NewParent: InodeID(in.Newdir),
NewParent: fuseops.InodeID(in.Newdir),
NewName: string(newName),
}
io = to
co = &to.commonOp
case fusekernel.OpUnlink:
buf := m.ConsumeBytes(m.Len())
@ -244,12 +213,10 @@ func Convert(
return
}
to := &UnlinkOp{
Parent: InodeID(m.Header().Nodeid),
o = &fuseops.UnlinkOp{
Parent: fuseops.InodeID(m.Header().Nodeid),
Name: string(buf[:n-1]),
}
io = to
co = &to.commonOp
case fusekernel.OpRmdir:
buf := m.ConsumeBytes(m.Len())
@ -259,26 +226,20 @@ func Convert(
return
}
to := &RmDirOp{
Parent: InodeID(m.Header().Nodeid),
o = &fuseops.RmDirOp{
Parent: fuseops.InodeID(m.Header().Nodeid),
Name: string(buf[:n-1]),
}
io = to
co = &to.commonOp
case fusekernel.OpOpen:
to := &OpenFileOp{
Inode: InodeID(m.Header().Nodeid),
o = &fuseops.OpenFileOp{
Inode: fuseops.InodeID(m.Header().Nodeid),
}
io = to
co = &to.commonOp
case fusekernel.OpOpendir:
to := &OpenDirOp{
Inode: InodeID(m.Header().Nodeid),
o = &fuseops.OpenDirOp{
Inode: fuseops.InodeID(m.Header().Nodeid),
}
io = to
co = &to.commonOp
case fusekernel.OpRead:
in := (*fusekernel.ReadIn)(m.Consume(fusekernel.ReadInSize(protocol)))
@ -287,14 +248,12 @@ func Convert(
return
}
to := &ReadFileOp{
Inode: InodeID(m.Header().Nodeid),
Handle: HandleID(in.Fh),
o = &fuseops.ReadFileOp{
Inode: fuseops.InodeID(m.Header().Nodeid),
Handle: fuseops.HandleID(in.Fh),
Offset: int64(in.Offset),
Size: int(in.Size),
}
io = to
co = &to.commonOp
case fusekernel.OpReaddir:
in := (*fusekernel.ReadIn)(m.Consume(fusekernel.ReadInSize(protocol)))
@ -303,14 +262,12 @@ func Convert(
return
}
to := &ReadDirOp{
Inode: InodeID(m.Header().Nodeid),
Handle: HandleID(in.Fh),
Offset: DirOffset(in.Offset),
o = &fuseops.ReadDirOp{
Inode: fuseops.InodeID(m.Header().Nodeid),
Handle: fuseops.HandleID(in.Fh),
Offset: fuseops.DirOffset(in.Offset),
Size: int(in.Size),
}
io = to
co = &to.commonOp
case fusekernel.OpRelease:
type input fusekernel.ReleaseIn
@ -320,11 +277,9 @@ func Convert(
return
}
to := &ReleaseFileHandleOp{
Handle: HandleID(in.Fh),
o = &fuseops.ReleaseFileHandleOp{
Handle: fuseops.HandleID(in.Fh),
}
io = to
co = &to.commonOp
case fusekernel.OpReleasedir:
type input fusekernel.ReleaseIn
@ -334,11 +289,9 @@ func Convert(
return
}
to := &ReleaseDirHandleOp{
Handle: HandleID(in.Fh),
o = &fuseops.ReleaseDirHandleOp{
Handle: fuseops.HandleID(in.Fh),
}
io = to
co = &to.commonOp
case fusekernel.OpWrite:
in := (*fusekernel.WriteIn)(m.Consume(fusekernel.WriteInSize(protocol)))
@ -353,14 +306,12 @@ func Convert(
return
}
to := &WriteFileOp{
Inode: InodeID(m.Header().Nodeid),
Handle: HandleID(in.Fh),
o = &fuseops.WriteFileOp{
Inode: fuseops.InodeID(m.Header().Nodeid),
Handle: fuseops.HandleID(in.Fh),
Data: buf,
Offset: int64(in.Offset),
}
io = to
co = &to.commonOp
case fusekernel.OpFsync:
type input fusekernel.FsyncIn
@ -370,12 +321,10 @@ func Convert(
return
}
to := &SyncFileOp{
Inode: InodeID(m.Header().Nodeid),
Handle: HandleID(in.Fh),
o = &fuseops.SyncFileOp{
Inode: fuseops.InodeID(m.Header().Nodeid),
Handle: fuseops.HandleID(in.Fh),
}
io = to
co = &to.commonOp
case fusekernel.OpFlush:
type input fusekernel.FlushIn
@ -385,24 +334,18 @@ func Convert(
return
}
to := &FlushFileOp{
Inode: InodeID(m.Header().Nodeid),
Handle: HandleID(in.Fh),
o = &fuseops.FlushFileOp{
Inode: fuseops.InodeID(m.Header().Nodeid),
Handle: fuseops.HandleID(in.Fh),
}
io = to
co = &to.commonOp
case fusekernel.OpReadlink:
to := &ReadSymlinkOp{
Inode: InodeID(m.Header().Nodeid),
o = &fuseops.ReadSymlinkOp{
Inode: fuseops.InodeID(m.Header().Nodeid),
}
io = to
co = &to.commonOp
case fusekernel.OpStatfs:
to := &InternalStatFSOp{}
io = to
co = &to.commonOp
o = &statFSOp{}
case fusekernel.OpInterrupt:
type input fusekernel.InterruptIn
@ -412,11 +355,9 @@ func Convert(
return
}
to := &InternalInterruptOp{
o = &interruptOp{
FuseID: in.Unique,
}
io = to
co = &to.commonOp
case fusekernel.OpInit:
type input fusekernel.InitIn
@ -426,35 +367,190 @@ func Convert(
return
}
to := &InternalInitOp{
o = &initOp{
Kernel: fusekernel.Protocol{in.Major, in.Minor},
MaxReadahead: in.MaxReadahead,
Flags: fusekernel.InitFlags(in.Flags),
}
io = to
co = &to.commonOp
default:
to := &unknownOp{
o = &unknownOp{
opCode: m.Header().Opcode,
inode: InodeID(m.Header().Nodeid),
inode: fuseops.InodeID(m.Header().Nodeid),
}
io = to
co = &to.commonOp
}
co.init(
opCtx,
io,
m.Header().Unique,
sendReply,
debugLogForOp,
errorLogger)
o = io
return
}
////////////////////////////////////////////////////////////////////////
// Outgoing messages
////////////////////////////////////////////////////////////////////////
// Return the response that should be sent to the kernel. If the op requires no
// response, return a nil response.
func kernelResponse(
fuseID uint64,
op interface{},
opErr error,
protocol fusekernel.Protocol) (msg []byte) {
// If the user replied with an error, create room enough just for the result
// header and fill it in with an error. Otherwise create an appropriate
// response.
var b buffer.OutMessage
if opErr != nil {
b = buffer.NewOutMessage(0)
if errno, ok := opErr.(syscall.Errno); ok {
b.OutHeader().Error = -int32(errno)
} else {
b.OutHeader().Error = -int32(syscall.EIO)
}
} else {
b = kernelResponseForOp(op, protocol)
}
msg = b.Bytes()
// Fill in the rest of the header, if a response is required.
if msg != nil {
h := b.OutHeader()
h.Unique = fuseID
h.Len = uint32(len(msg))
}
return
}
// Like kernelResponse, but assumes the user replied with a nil error to the
// op. Returns a nil response if no response is required.
func kernelResponseForOp(
op interface{},
protocol fusekernel.Protocol) (b buffer.OutMessage) {
// Create the appropriate output message
switch o := op.(type) {
case *fuseops.LookUpInodeOp:
size := fusekernel.EntryOutSize(protocol)
b = buffer.NewOutMessage(size)
out := (*fusekernel.EntryOut)(b.Grow(size))
convertChildInodeEntry(&o.Entry, out)
case *fuseops.GetInodeAttributesOp:
size := fusekernel.AttrOutSize(protocol)
b = buffer.NewOutMessage(size)
out := (*fusekernel.AttrOut)(b.Grow(size))
out.AttrValid, out.AttrValidNsec = convertExpirationTime(
o.AttributesExpiration)
convertAttributes(o.Inode, &o.Attributes, &out.Attr)
case *fuseops.SetInodeAttributesOp:
size := fusekernel.AttrOutSize(protocol)
b = buffer.NewOutMessage(size)
out := (*fusekernel.AttrOut)(b.Grow(size))
out.AttrValid, out.AttrValidNsec = convertExpirationTime(
o.AttributesExpiration)
convertAttributes(o.Inode, &o.Attributes, &out.Attr)
case *fuseops.ForgetInodeOp:
// No response.
case *fuseops.MkDirOp:
size := fusekernel.EntryOutSize(protocol)
b = buffer.NewOutMessage(size)
out := (*fusekernel.EntryOut)(b.Grow(size))
convertChildInodeEntry(&o.Entry, out)
case *fuseops.CreateFileOp:
eSize := fusekernel.EntryOutSize(protocol)
b = buffer.NewOutMessage(eSize + unsafe.Sizeof(fusekernel.OpenOut{}))
e := (*fusekernel.EntryOut)(b.Grow(eSize))
convertChildInodeEntry(&o.Entry, e)
oo := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
oo.Fh = uint64(o.Handle)
case *fuseops.CreateSymlinkOp:
size := fusekernel.EntryOutSize(protocol)
b = buffer.NewOutMessage(size)
out := (*fusekernel.EntryOut)(b.Grow(size))
convertChildInodeEntry(&o.Entry, out)
case *fuseops.RenameOp:
b = buffer.NewOutMessage(0)
case *fuseops.RmDirOp:
b = buffer.NewOutMessage(0)
case *fuseops.UnlinkOp:
b = buffer.NewOutMessage(0)
case *fuseops.OpenDirOp:
b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.OpenOut{}))
out := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
out.Fh = uint64(o.Handle)
case *fuseops.ReadDirOp:
b = buffer.NewOutMessage(uintptr(len(o.Data)))
b.Append(o.Data)
case *fuseops.ReleaseDirHandleOp:
b = buffer.NewOutMessage(0)
case *fuseops.OpenFileOp:
b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.OpenOut{}))
out := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
out.Fh = uint64(o.Handle)
case *fuseops.ReadFileOp:
b = buffer.NewOutMessage(uintptr(len(o.Data)))
b.Append(o.Data)
case *fuseops.WriteFileOp:
b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.WriteOut{}))
out := (*fusekernel.WriteOut)(b.Grow(unsafe.Sizeof(fusekernel.WriteOut{})))
out.Size = uint32(len(o.Data))
case *fuseops.SyncFileOp:
b = buffer.NewOutMessage(0)
case *fuseops.FlushFileOp:
b = buffer.NewOutMessage(0)
case *fuseops.ReleaseFileHandleOp:
b = buffer.NewOutMessage(0)
case *fuseops.ReadSymlinkOp:
b = buffer.NewOutMessage(uintptr(len(o.Target)))
b.AppendString(o.Target)
case *statFSOp:
b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{}))
b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{}))
case *interruptOp:
// No response.
case *initOp:
b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.InitOut{}))
out := (*fusekernel.InitOut)(b.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("Unknown op: %#v", op))
}
return
}
////////////////////////////////////////////////////////////////////////
// General conversions
////////////////////////////////////////////////////////////////////////
func convertTime(t time.Time) (secs uint64, nsec uint32) {
totalNano := t.UnixNano()
secs = uint64(totalNano / 1e9)
@ -463,8 +559,8 @@ func convertTime(t time.Time) (secs uint64, nsec uint32) {
}
func convertAttributes(
inodeID InodeID,
in *InodeAttributes,
inodeID fuseops.InodeID,
in *fuseops.InodeAttributes,
out *fusekernel.Attr) {
out.Ino = uint64(inodeID)
out.Size = in.Size
@ -515,7 +611,7 @@ func convertExpirationTime(t time.Time) (secs uint64, nsecs uint32) {
}
func convertChildInodeEntry(
in *ChildInodeEntry,
in *fuseops.ChildInodeEntry,
out *fusekernel.EntryOut) {
out.Nodeid = uint64(in.Child)
out.Generation = uint64(in.Generation)

View File

@ -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

View File

@ -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)
}
}

View File

@ -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

View File

@ -15,43 +15,10 @@
package fuseops
import (
"fmt"
"os"
"time"
"unsafe"
"github.com/jacobsa/fuse/internal/buffer"
"github.com/jacobsa/fuse/internal/fusekernel"
"golang.org/x/net/context"
)
// 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
// A context that can be used for long-running operations.
Context() context.Context
// Repond to the operation with the supplied error. If there is no error, set
// any necessary output fields and then call Respond(nil). The user must not
// call with a nil error for unrecognized ops; instead, use ENOSYS.
//
// Once this is invoked, the user must exclude any further calls to any
// method of this op.
Respond(error)
// Log information tied to this operation, with semantics equivalent to
// log.Printf, except that the format is different and logging is suppressed
// if no debug logger was set when mounting.
Logf(format string, v ...interface{})
}
////////////////////////////////////////////////////////////////////////
// Inodes
////////////////////////////////////////////////////////////////////////
@ -59,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
@ -83,28 +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
}
func (o *LookUpInodeOp) kernelResponse() (b buffer.OutMessage) {
size := fusekernel.EntryOutSize(o.protocol)
b = buffer.NewOutMessage(size)
out := (*fusekernel.EntryOut)(b.Grow(size))
convertChildInodeEntry(&o.Entry, out)
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
@ -115,32 +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())
}
func (o *GetInodeAttributesOp) kernelResponse() (b buffer.OutMessage) {
size := fusekernel.AttrOutSize(o.protocol)
b = buffer.NewOutMessage(size)
out := (*fusekernel.AttrOut)(b.Grow(size))
out.AttrValid, out.AttrValidNsec = convertExpirationTime(o.AttributesExpiration)
convertAttributes(o.Inode, &o.Attributes, &out.Attr)
return
}
// 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
@ -157,16 +83,6 @@ type SetInodeAttributesOp struct {
AttributesExpiration time.Time
}
func (o *SetInodeAttributesOp) kernelResponse() (b buffer.OutMessage) {
size := fusekernel.AttrOutSize(o.protocol)
b = buffer.NewOutMessage(size)
out := (*fusekernel.AttrOut)(b.Grow(size))
out.AttrValid, out.AttrValidNsec = convertExpirationTime(o.AttributesExpiration)
convertAttributes(o.Inode, &o.Attributes, &out.Attr)
return
}
// Decrement the reference count for an inode ID previously issued by the file
// system.
//
@ -207,8 +123,6 @@ func (o *SetInodeAttributesOp) kernelResponse() (b buffer.OutMessage) {
// 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
@ -216,11 +130,6 @@ type ForgetInodeOp struct {
N uint64
}
func (o *ForgetInodeOp) kernelResponse() (b buffer.OutMessage) {
// No response.
return
}
////////////////////////////////////////////////////////////////////////
// Inode creation
////////////////////////////////////////////////////////////////////////
@ -237,9 +146,6 @@ func (o *ForgetInodeOp) kernelResponse() (b buffer.OutMessage) {
//
// 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
@ -254,20 +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
}
func (o *MkDirOp) kernelResponse() (b buffer.OutMessage) {
size := fusekernel.EntryOutSize(o.protocol)
b = buffer.NewOutMessage(size)
out := (*fusekernel.EntryOut)(b.Grow(size))
convertChildInodeEntry(&o.Entry, out)
return
}
// Create a file inode and open it.
//
// The kernel sends this when the user asks to open a file with the O_CREAT
@ -279,9 +171,6 @@ func (o *MkDirOp) kernelResponse() (b buffer.OutMessage) {
//
// 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
@ -306,30 +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
}
func (o *CreateFileOp) kernelResponse() (b buffer.OutMessage) {
eSize := fusekernel.EntryOutSize(o.protocol)
b = buffer.NewOutMessage(eSize + unsafe.Sizeof(fusekernel.OpenOut{}))
e := (*fusekernel.EntryOut)(b.Grow(eSize))
convertChildInodeEntry(&o.Entry, e)
oo := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
oo.Fh = uint64(o.Handle)
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
@ -347,25 +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
}
func (o *CreateSymlinkOp) kernelResponse() (b buffer.OutMessage) {
size := fusekernel.EntryOutSize(o.protocol)
b = buffer.NewOutMessage(size)
out := (*fusekernel.EntryOut)(b.Grow(size))
convertChildInodeEntry(&o.Entry, out)
return
}
////////////////////////////////////////////////////////////////////////
// Unlinking
////////////////////////////////////////////////////////////////////////
@ -405,8 +254,6 @@ func (o *CreateSymlinkOp) kernelResponse() (b buffer.OutMessage) {
// about this.
//
type RenameOp struct {
commonOp
// The old parent directory, and the name of the entry within it to be
// relocated.
OldParent InodeID
@ -418,11 +265,6 @@ type RenameOp struct {
NewName string
}
func (o *RenameOp) kernelResponse() (b buffer.OutMessage) {
b = buffer.NewOutMessage(0)
return
}
// Unlink a directory from its parent. Because directories cannot have a link
// count above one, this means the directory inode should be deleted as well
// once the kernel sends ForgetInodeOp.
@ -431,19 +273,12 @@ func (o *RenameOp) kernelResponse() (b buffer.OutMessage) {
//
// 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
Name string
}
func (o *RmDirOp) kernelResponse() (b buffer.OutMessage) {
b = buffer.NewOutMessage(0)
return
}
// Unlink a file or symlink from its parent. If this brings the inode's link
// count to zero, the inode should be deleted once the kernel sends
// ForgetInodeOp. It may still be referenced before then if a user still has
@ -451,19 +286,12 @@ func (o *RmDirOp) kernelResponse() (b buffer.OutMessage) {
//
// 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
Name string
}
func (o *UnlinkOp) kernelResponse() (b buffer.OutMessage) {
b = buffer.NewOutMessage(0)
return
}
////////////////////////////////////////////////////////////////////////
// Directory handles
////////////////////////////////////////////////////////////////////////
@ -475,8 +303,6 @@ func (o *UnlinkOp) kernelResponse() (b buffer.OutMessage) {
// 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
@ -491,18 +317,8 @@ type OpenDirOp struct {
Handle HandleID
}
func (o *OpenDirOp) kernelResponse() (b buffer.OutMessage) {
b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.OpenOut{}))
out := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
out.Fh = uint64(o.Handle)
return
}
// 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
@ -589,12 +405,6 @@ type ReadDirOp struct {
Data []byte
}
func (o *ReadDirOp) kernelResponse() (b buffer.OutMessage) {
b = buffer.NewOutMessage(uintptr(len(o.Data)))
b.Append(o.Data)
return
}
// Release a previously-minted directory handle. The kernel sends this when
// there are no more references to an open directory: all file descriptors are
// closed and all memory mappings are unmapped.
@ -604,19 +414,12 @@ func (o *ReadDirOp) kernelResponse() (b buffer.OutMessage) {
//
// 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).
Handle HandleID
}
func (o *ReleaseDirHandleOp) kernelResponse() (b buffer.OutMessage) {
b = buffer.NewOutMessage(0)
return
}
////////////////////////////////////////////////////////////////////////
// File handles
////////////////////////////////////////////////////////////////////////
@ -628,8 +431,6 @@ func (o *ReleaseDirHandleOp) kernelResponse() (b buffer.OutMessage) {
// 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
@ -643,22 +444,12 @@ type OpenFileOp struct {
Handle HandleID
}
func (o *OpenFileOp) kernelResponse() (b buffer.OutMessage) {
b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.OpenOut{}))
out := (*fusekernel.OpenOut)(b.Grow(unsafe.Sizeof(fusekernel.OpenOut{})))
out.Fh = uint64(o.Handle)
return
}
// Read data from a file previously opened with CreateFile or OpenFile.
//
// Note that this op is not sent for every call to read(2) by the end user;
// 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
@ -680,12 +471,6 @@ type ReadFileOp struct {
Data []byte
}
func (o *ReadFileOp) kernelResponse() (b buffer.OutMessage) {
b = buffer.NewOutMessage(uintptr(len(o.Data)))
b.Append(o.Data)
return
}
// Write data to a file previously opened with CreateFile or OpenFile.
//
// When the user writes data using write(2), the write goes into the page
@ -718,8 +503,6 @@ func (o *ReadFileOp) kernelResponse() (b buffer.OutMessage) {
// (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
@ -756,14 +539,6 @@ type WriteFileOp struct {
Data []byte
}
func (o *WriteFileOp) kernelResponse() (b buffer.OutMessage) {
b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.WriteOut{}))
out := (*fusekernel.WriteOut)(b.Grow(unsafe.Sizeof(fusekernel.WriteOut{})))
out.Size = uint32(len(o.Data))
return
}
// Synchronize the current contents of an open file to storage.
//
// vfs.txt documents this as being called for by the fsync(2) system call
@ -781,18 +556,11 @@ func (o *WriteFileOp) kernelResponse() (b buffer.OutMessage) {
// 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
}
func (o *SyncFileOp) kernelResponse() (b buffer.OutMessage) {
b = buffer.NewOutMessage(0)
return
}
// Flush the current state of an open file to storage upon closing a file
// descriptor.
//
@ -841,18 +609,11 @@ func (o *SyncFileOp) kernelResponse() (b buffer.OutMessage) {
// 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
}
func (o *FlushFileOp) kernelResponse() (b buffer.OutMessage) {
b = buffer.NewOutMessage(0)
return
}
// Release a previously-minted file handle. The kernel calls this when there
// are no more references to an open file: all file descriptors are closed
// and all memory mappings are unmapped.
@ -862,113 +623,21 @@ func (o *FlushFileOp) kernelResponse() (b buffer.OutMessage) {
//
// 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
}
func (o *ReleaseFileHandleOp) kernelResponse() (b buffer.OutMessage) {
b = buffer.NewOutMessage(0)
return
}
// 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
}
func (o *unknownOp) kernelResponse() (b buffer.OutMessage) {
panic(fmt.Sprintf("Should never get here for unknown op: %s", o.ShortDesc()))
}
////////////////////////////////////////////////////////////////////////
// Reading symlinks
////////////////////////////////////////////////////////////////////////
// Read the target of a symlink inode.
type ReadSymlinkOp struct {
commonOp
// The symlink inode that we are reading.
Inode InodeID
// Set by the file system: the target of the symlink.
Target string
}
func (o *ReadSymlinkOp) kernelResponse() (b buffer.OutMessage) {
b = buffer.NewOutMessage(uintptr(len(o.Target)))
b.AppendString(o.Target)
return
}
////////////////////////////////////////////////////////////////////////
// Internal
////////////////////////////////////////////////////////////////////////
// TODO(jacobsa): Untangle the way ops work and move these to an internal
// package, along with Convert. I think all of the behavior wants to be on
// Connection. Ops have only String methods. Connection.ReadOp returns an
// interace{} and a context. If we must restore debug logging later, we can
// stuff an op ID in that context and add a Connection.Logf method. Connection
// has a Reply method that takes a descendent context and an error.
// Do not use this struct directly. See the TODO in fuseops/ops.go.
type InternalStatFSOp struct {
commonOp
}
func (o *InternalStatFSOp) kernelResponse() (b buffer.OutMessage) {
b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{}))
b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{}))
return
}
// Do not use this struct directly. See the TODO in fuseops/ops.go.
type InternalInterruptOp struct {
commonOp
FuseID uint64
}
func (o *InternalInterruptOp) kernelResponse() (b buffer.OutMessage) {
panic("Shouldn't get here.")
}
// Do not use this struct directly. See the TODO in fuseops/ops.go.
type InternalInitOp struct {
commonOp
// In
Kernel fusekernel.Protocol
// Out
Library fusekernel.Protocol
MaxReadahead uint32
Flags fusekernel.InitFlags
MaxWrite uint32
}
func (o *InternalInitOp) kernelResponse() (b buffer.OutMessage) {
b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.InitOut{}))
out := (*fusekernel.InitOut)(b.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
return
}

View File

@ -18,6 +18,8 @@ import (
"io"
"sync"
"golang.org/x/net/context"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseops"
)
@ -27,32 +29,32 @@ 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.
type FileSystem interface {
LookUpInode(*fuseops.LookUpInodeOp) error
GetInodeAttributes(*fuseops.GetInodeAttributesOp) error
SetInodeAttributes(*fuseops.SetInodeAttributesOp) error
ForgetInode(*fuseops.ForgetInodeOp) error
MkDir(*fuseops.MkDirOp) error
CreateFile(*fuseops.CreateFileOp) error
CreateSymlink(*fuseops.CreateSymlinkOp) error
Rename(*fuseops.RenameOp) error
RmDir(*fuseops.RmDirOp) error
Unlink(*fuseops.UnlinkOp) error
OpenDir(*fuseops.OpenDirOp) error
ReadDir(*fuseops.ReadDirOp) error
ReleaseDirHandle(*fuseops.ReleaseDirHandleOp) error
OpenFile(*fuseops.OpenFileOp) error
ReadFile(*fuseops.ReadFileOp) error
WriteFile(*fuseops.WriteFileOp) error
SyncFile(*fuseops.SyncFileOp) error
FlushFile(*fuseops.FlushFileOp) error
ReleaseFileHandle(*fuseops.ReleaseFileHandleOp) error
ReadSymlink(*fuseops.ReadSymlinkOp) error
LookUpInode(context.Context, *fuseops.LookUpInodeOp) error
GetInodeAttributes(context.Context, *fuseops.GetInodeAttributesOp) error
SetInodeAttributes(context.Context, *fuseops.SetInodeAttributesOp) error
ForgetInode(context.Context, *fuseops.ForgetInodeOp) error
MkDir(context.Context, *fuseops.MkDirOp) error
CreateFile(context.Context, *fuseops.CreateFileOp) error
CreateSymlink(context.Context, *fuseops.CreateSymlinkOp) error
Rename(context.Context, *fuseops.RenameOp) error
RmDir(context.Context, *fuseops.RmDirOp) error
Unlink(context.Context, *fuseops.UnlinkOp) error
OpenDir(context.Context, *fuseops.OpenDirOp) error
ReadDir(context.Context, *fuseops.ReadDirOp) error
ReleaseDirHandle(context.Context, *fuseops.ReleaseDirHandleOp) error
OpenFile(context.Context, *fuseops.OpenFileOp) error
ReadFile(context.Context, *fuseops.ReadFileOp) error
WriteFile(context.Context, *fuseops.WriteFileOp) error
SyncFile(context.Context, *fuseops.SyncFileOp) error
FlushFile(context.Context, *fuseops.FlushFileOp) error
ReleaseFileHandle(context.Context, *fuseops.ReleaseFileHandleOp) error
ReadSymlink(context.Context, *fuseops.ReadSymlinkOp) error
// Regard all inodes (including the root inode) as having their lookup counts
// decremented to zero, and clean up any resources associated with the file
@ -91,7 +93,7 @@ func (s *fileSystemServer) ServeOps(c *fuse.Connection) {
}()
for {
op, err := c.ReadOp()
ctx, op, err := c.ReadOp()
if err == io.EOF {
break
}
@ -101,11 +103,14 @@ func (s *fileSystemServer) ServeOps(c *fuse.Connection) {
}
s.opsInFlight.Add(1)
go s.handleOp(op)
go s.handleOp(c, ctx, op)
}
}
func (s *fileSystemServer) handleOp(op fuseops.Op) {
func (s *fileSystemServer) handleOp(
c *fuse.Connection,
ctx context.Context,
op interface{}) {
defer s.opsInFlight.Done()
// Dispatch to the appropriate method.
@ -115,65 +120,65 @@ func (s *fileSystemServer) handleOp(op fuseops.Op) {
err = fuse.ENOSYS
case *fuseops.LookUpInodeOp:
err = s.fs.LookUpInode(typed)
err = s.fs.LookUpInode(ctx, typed)
case *fuseops.GetInodeAttributesOp:
err = s.fs.GetInodeAttributes(typed)
err = s.fs.GetInodeAttributes(ctx, typed)
case *fuseops.SetInodeAttributesOp:
err = s.fs.SetInodeAttributes(typed)
err = s.fs.SetInodeAttributes(ctx, typed)
case *fuseops.ForgetInodeOp:
err = s.fs.ForgetInode(typed)
err = s.fs.ForgetInode(ctx, typed)
case *fuseops.MkDirOp:
err = s.fs.MkDir(typed)
err = s.fs.MkDir(ctx, typed)
case *fuseops.CreateFileOp:
err = s.fs.CreateFile(typed)
err = s.fs.CreateFile(ctx, typed)
case *fuseops.CreateSymlinkOp:
err = s.fs.CreateSymlink(typed)
err = s.fs.CreateSymlink(ctx, typed)
case *fuseops.RenameOp:
err = s.fs.Rename(typed)
err = s.fs.Rename(ctx, typed)
case *fuseops.RmDirOp:
err = s.fs.RmDir(typed)
err = s.fs.RmDir(ctx, typed)
case *fuseops.UnlinkOp:
err = s.fs.Unlink(typed)
err = s.fs.Unlink(ctx, typed)
case *fuseops.OpenDirOp:
err = s.fs.OpenDir(typed)
err = s.fs.OpenDir(ctx, typed)
case *fuseops.ReadDirOp:
err = s.fs.ReadDir(typed)
err = s.fs.ReadDir(ctx, typed)
case *fuseops.ReleaseDirHandleOp:
err = s.fs.ReleaseDirHandle(typed)
err = s.fs.ReleaseDirHandle(ctx, typed)
case *fuseops.OpenFileOp:
err = s.fs.OpenFile(typed)
err = s.fs.OpenFile(ctx, typed)
case *fuseops.ReadFileOp:
err = s.fs.ReadFile(typed)
err = s.fs.ReadFile(ctx, typed)
case *fuseops.WriteFileOp:
err = s.fs.WriteFile(typed)
err = s.fs.WriteFile(ctx, typed)
case *fuseops.SyncFileOp:
err = s.fs.SyncFile(typed)
err = s.fs.SyncFile(ctx, typed)
case *fuseops.FlushFileOp:
err = s.fs.FlushFile(typed)
err = s.fs.FlushFile(ctx, typed)
case *fuseops.ReleaseFileHandleOp:
err = s.fs.ReleaseFileHandle(typed)
err = s.fs.ReleaseFileHandle(ctx, typed)
case *fuseops.ReadSymlinkOp:
err = s.fs.ReadSymlink(typed)
err = s.fs.ReadSymlink(ctx, typed)
}
op.Respond(err)
c.Reply(ctx, err)
}

View File

@ -17,6 +17,7 @@ package fuseutil
import (
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseops"
"golang.org/x/net/context"
)
// A FileSystem that responds to all ops with fuse.ENOSYS. Embed this in your
@ -29,120 +30,140 @@ type NotImplementedFileSystem struct {
var _ FileSystem = &NotImplementedFileSystem{}
func (fs *NotImplementedFileSystem) LookUpInode(
ctx context.Context,
op *fuseops.LookUpInodeOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) GetInodeAttributes(
ctx context.Context,
op *fuseops.GetInodeAttributesOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) SetInodeAttributes(
ctx context.Context,
op *fuseops.SetInodeAttributesOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) ForgetInode(
ctx context.Context,
op *fuseops.ForgetInodeOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) MkDir(
ctx context.Context,
op *fuseops.MkDirOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) CreateFile(
ctx context.Context,
op *fuseops.CreateFileOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) CreateSymlink(
ctx context.Context,
op *fuseops.CreateSymlinkOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) Rename(
ctx context.Context,
op *fuseops.RenameOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) RmDir(
ctx context.Context,
op *fuseops.RmDirOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) Unlink(
ctx context.Context,
op *fuseops.UnlinkOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) OpenDir(
ctx context.Context,
op *fuseops.OpenDirOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) ReadDir(
ctx context.Context,
op *fuseops.ReadDirOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) ReleaseDirHandle(
ctx context.Context,
op *fuseops.ReleaseDirHandleOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) OpenFile(
ctx context.Context,
op *fuseops.OpenFileOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) ReadFile(
ctx context.Context,
op *fuseops.ReadFileOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) WriteFile(
ctx context.Context,
op *fuseops.WriteFileOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) SyncFile(
ctx context.Context,
op *fuseops.SyncFileOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) FlushFile(
ctx context.Context,
op *fuseops.FlushFileOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) ReleaseFileHandle(
ctx context.Context,
op *fuseops.ReleaseFileHandleOp) (err error) {
err = fuse.ENOSYS
return
}
func (fs *NotImplementedFileSystem) ReadSymlink(
ctx context.Context,
op *fuseops.ReadSymlinkOp) (err error) {
err = fuse.ENOSYS
return

48
ops.go Normal file
View File

@ -0,0 +1,48 @@
// 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 fuse
import (
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/internal/fusekernel"
)
// A sentinel used for unknown ops. The user is expected to respond with a
// non-nil error.
type unknownOp struct {
opCode uint32
inode fuseops.InodeID
}
// Required in order to mount on OS X.
type statFSOp struct {
}
// Causes us to cancel the associated context.
type interruptOp struct {
FuseID uint64
}
// Required in order to mount on Linux and OS X.
type initOp struct {
// In
Kernel fusekernel.Protocol
// Out
Library fusekernel.Protocol
MaxReadahead uint32
Flags fusekernel.InitFlags
MaxWrite uint32
}

View File

@ -19,6 +19,8 @@ import (
"os"
"time"
"golang.org/x/net/context"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/fuseutil"
@ -240,6 +242,7 @@ func (fs *cachingFS) SetMtime(mtime time.Time) {
// LOCKS_EXCLUDED(fs.mu)
func (fs *cachingFS) LookUpInode(
ctx context.Context,
op *fuseops.LookUpInodeOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -294,6 +297,7 @@ func (fs *cachingFS) LookUpInode(
// LOCKS_EXCLUDED(fs.mu)
func (fs *cachingFS) GetInodeAttributes(
ctx context.Context,
op *fuseops.GetInodeAttributesOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -323,11 +327,13 @@ func (fs *cachingFS) GetInodeAttributes(
}
func (fs *cachingFS) OpenDir(
ctx context.Context,
op *fuseops.OpenDirOp) (err error) {
return
}
func (fs *cachingFS) OpenFile(
ctx context.Context,
op *fuseops.OpenFileOp) (err error) {
return
}

View File

@ -19,6 +19,8 @@ import (
"os"
"sync"
"golang.org/x/net/context"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/fuseutil"
@ -93,6 +95,7 @@ func (fs *flushFS) barAttributes() fuseops.InodeAttributes {
////////////////////////////////////////////////////////////////////////
func (fs *flushFS) LookUpInode(
ctx context.Context,
op *fuseops.LookUpInodeOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -126,6 +129,7 @@ func (fs *flushFS) LookUpInode(
}
func (fs *flushFS) GetInodeAttributes(
ctx context.Context,
op *fuseops.GetInodeAttributesOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -150,6 +154,7 @@ func (fs *flushFS) GetInodeAttributes(
}
func (fs *flushFS) OpenFile(
ctx context.Context,
op *fuseops.OpenFileOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -164,6 +169,7 @@ func (fs *flushFS) OpenFile(
}
func (fs *flushFS) ReadFile(
ctx context.Context,
op *fuseops.ReadFileOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -181,6 +187,7 @@ func (fs *flushFS) ReadFile(
}
func (fs *flushFS) WriteFile(
ctx context.Context,
op *fuseops.WriteFileOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -204,6 +211,7 @@ func (fs *flushFS) WriteFile(
}
func (fs *flushFS) SyncFile(
ctx context.Context,
op *fuseops.SyncFileOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -213,6 +221,7 @@ func (fs *flushFS) SyncFile(
}
func (fs *flushFS) FlushFile(
ctx context.Context,
op *fuseops.FlushFileOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -222,6 +231,7 @@ func (fs *flushFS) FlushFile(
}
func (fs *flushFS) OpenDir(
ctx context.Context,
op *fuseops.OpenDirOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -240,6 +250,7 @@ func (fs *flushFS) OpenDir(
}
func (fs *flushFS) ReadDir(
ctx context.Context,
op *fuseops.ReadDirOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()

View File

@ -18,6 +18,8 @@ import (
"fmt"
"os"
"golang.org/x/net/context"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/fuseutil"
@ -223,6 +225,7 @@ func (fs *fsImpl) findInodeByID(id fuseops.InodeID) (in *inode) {
////////////////////////////////////////////////////////////////////////
func (fs *fsImpl) LookUpInode(
ctx context.Context,
op *fuseops.LookUpInodeOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -258,6 +261,7 @@ func (fs *fsImpl) LookUpInode(
}
func (fs *fsImpl) GetInodeAttributes(
ctx context.Context,
op *fuseops.GetInodeAttributesOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -272,6 +276,7 @@ func (fs *fsImpl) GetInodeAttributes(
}
func (fs *fsImpl) ForgetInode(
ctx context.Context,
op *fuseops.ForgetInodeOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -284,6 +289,7 @@ func (fs *fsImpl) ForgetInode(
}
func (fs *fsImpl) MkDir(
ctx context.Context,
op *fuseops.MkDirOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -315,6 +321,7 @@ func (fs *fsImpl) MkDir(
}
func (fs *fsImpl) CreateFile(
ctx context.Context,
op *fuseops.CreateFileOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -346,6 +353,7 @@ func (fs *fsImpl) CreateFile(
}
func (fs *fsImpl) OpenFile(
ctx context.Context,
op *fuseops.OpenFileOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -357,6 +365,7 @@ func (fs *fsImpl) OpenFile(
}
func (fs *fsImpl) OpenDir(
ctx context.Context,
op *fuseops.OpenDirOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()

View File

@ -19,6 +19,8 @@ import (
"os"
"strings"
"golang.org/x/net/context"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/fuseutil"
@ -147,7 +149,9 @@ func (fs *helloFS) patchAttributes(
attr.Crtime = now
}
func (fs *helloFS) LookUpInode(op *fuseops.LookUpInodeOp) (err error) {
func (fs *helloFS) LookUpInode(
ctx context.Context,
op *fuseops.LookUpInodeOp) (err error) {
// Find the info for the parent.
parentInfo, ok := gInodeInfo[op.Parent]
if !ok {
@ -172,6 +176,7 @@ func (fs *helloFS) LookUpInode(op *fuseops.LookUpInodeOp) (err error) {
}
func (fs *helloFS) GetInodeAttributes(
ctx context.Context,
op *fuseops.GetInodeAttributesOp) (err error) {
// Find the info for this inode.
info, ok := gInodeInfo[op.Inode]
@ -190,12 +195,14 @@ func (fs *helloFS) GetInodeAttributes(
}
func (fs *helloFS) OpenDir(
ctx context.Context,
op *fuseops.OpenDirOp) (err error) {
// Allow opening any directory.
return
}
func (fs *helloFS) ReadDir(
ctx context.Context,
op *fuseops.ReadDirOp) (err error) {
// Find the info for this inode.
info, ok := gInodeInfo[op.Inode]
@ -232,12 +239,14 @@ func (fs *helloFS) ReadDir(
}
func (fs *helloFS) OpenFile(
ctx context.Context,
op *fuseops.OpenFileOp) (err error) {
// Allow opening any file.
return
}
func (fs *helloFS) ReadFile(
ctx context.Context,
op *fuseops.ReadFileOp) (err error) {
// Let io.ReaderAt deal with the semantics.
reader := strings.NewReader("Hello, world!")

View File

@ -19,6 +19,8 @@ import (
"os"
"sync"
"golang.org/x/net/context"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/fuseutil"
@ -78,6 +80,7 @@ func (fs *InterruptFS) WaitForReadInFlight() {
////////////////////////////////////////////////////////////////////////
func (fs *InterruptFS) LookUpInode(
ctx context.Context,
op *fuseops.LookUpInodeOp) (err error) {
// We support only one parent.
if op.Parent != fuseops.RootInodeID {
@ -99,6 +102,7 @@ func (fs *InterruptFS) LookUpInode(
}
func (fs *InterruptFS) GetInodeAttributes(
ctx context.Context,
op *fuseops.GetInodeAttributesOp) (err error) {
switch op.Inode {
case fuseops.RootInodeID:
@ -116,11 +120,13 @@ func (fs *InterruptFS) GetInodeAttributes(
}
func (fs *InterruptFS) OpenFile(
ctx context.Context,
op *fuseops.OpenFileOp) (err error) {
return
}
func (fs *InterruptFS) ReadFile(
ctx context.Context,
op *fuseops.ReadFileOp) (err error) {
// Signal that a read has been received.
fs.mu.Lock()
@ -129,7 +135,7 @@ func (fs *InterruptFS) ReadFile(
fs.mu.Unlock()
// Wait for cancellation.
done := op.Context().Done()
done := ctx.Done()
if done == nil {
panic("Expected non-nil channel.")
}
@ -137,7 +143,7 @@ func (fs *InterruptFS) ReadFile(
<-done
// Return the context's error.
err = op.Context().Err()
err = ctx.Err()
return
}

View File

@ -20,6 +20,8 @@ import (
"os"
"time"
"golang.org/x/net/context"
"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/fuseutil"
@ -190,6 +192,7 @@ func (fs *memFS) deallocateInode(id fuseops.InodeID) {
////////////////////////////////////////////////////////////////////////
func (fs *memFS) LookUpInode(
ctx context.Context,
op *fuseops.LookUpInodeOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -220,6 +223,7 @@ func (fs *memFS) LookUpInode(
}
func (fs *memFS) GetInodeAttributes(
ctx context.Context,
op *fuseops.GetInodeAttributesOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -238,6 +242,7 @@ func (fs *memFS) GetInodeAttributes(
}
func (fs *memFS) SetInodeAttributes(
ctx context.Context,
op *fuseops.SetInodeAttributesOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -259,6 +264,7 @@ func (fs *memFS) SetInodeAttributes(
}
func (fs *memFS) MkDir(
ctx context.Context,
op *fuseops.MkDirOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -301,6 +307,7 @@ func (fs *memFS) MkDir(
}
func (fs *memFS) CreateFile(
ctx context.Context,
op *fuseops.CreateFileOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -350,6 +357,7 @@ func (fs *memFS) CreateFile(
}
func (fs *memFS) CreateSymlink(
ctx context.Context,
op *fuseops.CreateSymlinkOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -400,6 +408,7 @@ func (fs *memFS) CreateSymlink(
}
func (fs *memFS) Rename(
ctx context.Context,
op *fuseops.RenameOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -440,6 +449,7 @@ func (fs *memFS) Rename(
}
func (fs *memFS) RmDir(
ctx context.Context,
op *fuseops.RmDirOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -473,6 +483,7 @@ func (fs *memFS) RmDir(
}
func (fs *memFS) Unlink(
ctx context.Context,
op *fuseops.UnlinkOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -500,6 +511,7 @@ func (fs *memFS) Unlink(
}
func (fs *memFS) OpenDir(
ctx context.Context,
op *fuseops.OpenDirOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -517,6 +529,7 @@ func (fs *memFS) OpenDir(
}
func (fs *memFS) ReadDir(
ctx context.Context,
op *fuseops.ReadDirOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -531,6 +544,7 @@ func (fs *memFS) ReadDir(
}
func (fs *memFS) OpenFile(
ctx context.Context,
op *fuseops.OpenFileOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -548,6 +562,7 @@ func (fs *memFS) OpenFile(
}
func (fs *memFS) ReadFile(
ctx context.Context,
op *fuseops.ReadFileOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -569,6 +584,7 @@ func (fs *memFS) ReadFile(
}
func (fs *memFS) WriteFile(
ctx context.Context,
op *fuseops.WriteFileOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()
@ -583,6 +599,7 @@ func (fs *memFS) WriteFile(
}
func (fs *memFS) ReadSymlink(
ctx context.Context,
op *fuseops.ReadSymlinkOp) (err error) {
fs.mu.Lock()
defer fs.mu.Unlock()