diff --git a/connection.go b/connection.go index 3ac152c..bd15d7a 100644 --- a/connection.go +++ b/connection.go @@ -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) { diff --git a/fuseops/convert.go b/conversions.go similarity index 55% rename from fuseops/convert.go rename to conversions.go index 930e444..ee3b97d 100644 --- a/fuseops/convert.go +++ b/conversions.go @@ -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) diff --git a/errors.go b/errors.go index bc9f902..455c79b 100644 --- a/errors.go +++ b/errors.go @@ -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 diff --git a/fuseops/common_op.go b/fuseops/common_op.go deleted file mode 100644 index fb3a224..0000000 --- a/fuseops/common_op.go +++ /dev/null @@ -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) - } -} diff --git a/fuseops/doc.go b/fuseops/doc.go index 5e4a880..8eccde0 100644 --- a/fuseops/doc.go +++ b/fuseops/doc.go @@ -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 diff --git a/fuseops/ops.go b/fuseops/ops.go index 0eb15b8..d73c42c 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -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("(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 -} diff --git a/fuseutil/file_system.go b/fuseutil/file_system.go index 7aa1b45..3ebcdf8 100644 --- a/fuseutil/file_system.go +++ b/fuseutil/file_system.go @@ -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) } diff --git a/fuseutil/not_implemented_file_system.go b/fuseutil/not_implemented_file_system.go index a395c16..41f3a6d 100644 --- a/fuseutil/not_implemented_file_system.go +++ b/fuseutil/not_implemented_file_system.go @@ -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 diff --git a/ops.go b/ops.go new file mode 100644 index 0000000..13ede33 --- /dev/null +++ b/ops.go @@ -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 +} diff --git a/samples/cachingfs/caching_fs.go b/samples/cachingfs/caching_fs.go index dc8e4d8..2565719 100644 --- a/samples/cachingfs/caching_fs.go +++ b/samples/cachingfs/caching_fs.go @@ -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 } diff --git a/samples/flushfs/flush_fs.go b/samples/flushfs/flush_fs.go index bf8c3c0..5a642de 100644 --- a/samples/flushfs/flush_fs.go +++ b/samples/flushfs/flush_fs.go @@ -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() diff --git a/samples/forgetfs/forget_fs.go b/samples/forgetfs/forget_fs.go index d8f47ea..697ffed 100644 --- a/samples/forgetfs/forget_fs.go +++ b/samples/forgetfs/forget_fs.go @@ -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() diff --git a/samples/hellofs/hello_fs.go b/samples/hellofs/hello_fs.go index b07d1d4..59d2c1a 100644 --- a/samples/hellofs/hello_fs.go +++ b/samples/hellofs/hello_fs.go @@ -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!") diff --git a/samples/interruptfs/interrupt_fs.go b/samples/interruptfs/interrupt_fs.go index 5960a32..3208ab3 100644 --- a/samples/interruptfs/interrupt_fs.go +++ b/samples/interruptfs/interrupt_fs.go @@ -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 } diff --git a/samples/memfs/memfs.go b/samples/memfs/memfs.go index d0f34e6..e1bf030 100644 --- a/samples/memfs/memfs.go +++ b/samples/memfs/memfs.go @@ -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()