From 36f2166878228024c70eda77b84031de8862e46b Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:09:51 +1000 Subject: [PATCH 01/49] Copied over ops.go. --- ops.go | 974 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 974 insertions(+) create mode 100644 ops.go diff --git a/ops.go b/ops.go new file mode 100644 index 0000000..0eb15b8 --- /dev/null +++ b/ops.go @@ -0,0 +1,974 @@ +// 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" + "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 +//////////////////////////////////////////////////////////////////////// + +// 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 + + // The name of the child of interest, relative to the parent. For example, in + // this directory structure: + // + // foo/ + // bar/ + // baz + // + // the file system may receive a request to look up the child named "bar" for + // the parent foo/. + Name string + + // The resulting entry. Must be filled out by the file system. + // + // The lookup count for the inode is implicitly incremented. See notes on + // ForgetInodeOp for more information. + 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 + + // Set by the file system: attributes for the inode, and the time at which + // they should expire. See notes on ChildInodeEntry.AttributesExpiration for + // more. + Attributes InodeAttributes + 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 + + // The attributes to modify, or nil for attributes that don't need a change. + Size *uint64 + Mode *os.FileMode + Atime *time.Time + Mtime *time.Time + + // Set by the file system: the new attributes for the inode, and the time at + // which they should expire. See notes on + // ChildInodeEntry.AttributesExpiration for more. + Attributes InodeAttributes + 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. +// +// The comments for the ops that implicitly increment the reference count +// contain a note of this (but see also the note about the root inode below). +// For example, LookUpInodeOp and MkDirOp. The authoritative source is the +// libfuse documentation, which states that any op that returns +// fuse_reply_entry fuse_reply_create implicitly increments (cf. +// http://goo.gl/o5C7Dx). +// +// If the reference count hits zero, the file system can forget about that ID +// entirely, and even re-use it in future responses. The kernel guarantees that +// it will not otherwise use it again. +// +// The reference count corresponds to fuse_inode::nlookup +// (http://goo.gl/ut48S4). Some examples of where the kernel manipulates it: +// +// * (http://goo.gl/vPD9Oh) Any caller to fuse_iget increases the count. +// * (http://goo.gl/B6tTTC) fuse_lookup_name calls fuse_iget. +// * (http://goo.gl/IlcxWv) fuse_create_open calls fuse_iget. +// * (http://goo.gl/VQMQul) fuse_dentry_revalidate increments after +// revalidating. +// +// In contrast to all other inodes, RootInodeID begins with an implicit +// lookup count of one, without a corresponding op to increase it. (There +// could be no such op, because the root cannot be referred to by name.) Code +// walk: +// +// * (http://goo.gl/gWAheU) fuse_fill_super calls fuse_get_root_inode. +// +// * (http://goo.gl/AoLsbb) fuse_get_root_inode calls fuse_iget without +// sending any particular request. +// +// * (http://goo.gl/vPD9Oh) fuse_iget increments nlookup. +// +// File systems should tolerate but not rely on receiving forget ops for +// remaining inodes when the file system unmounts, including the root inode. +// 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 + + // The amount to decrement the reference count. + N uint64 +} + +func (o *ForgetInodeOp) kernelResponse() (b buffer.OutMessage) { + // No response. + return +} + +//////////////////////////////////////////////////////////////////////// +// Inode creation +//////////////////////////////////////////////////////////////////////// + +// Create a directory inode as a child of an existing directory inode. The +// kernel sends this in response to a mkdir(2) call. +// +// The Linux kernel appears to verify the name doesn't already exist (mkdir +// calls mkdirat calls user_path_create calls filename_create, which verifies: +// http://goo.gl/FZpLu5). Indeed, the tests in samples/memfs that call in +// parallel appear to bear this out. But osxfuse does not appear to guarantee +// this (cf. https://goo.gl/PqzZDv). And if names may be created outside of the +// kernel's control, it doesn't matter what the kernel does anyway. +// +// 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 + + // The name of the child to create, and the mode with which to create it. + Name string + Mode os.FileMode + + // Set by the file system: information about the inode that was created. + // + // The lookup count for the inode is implicitly incremented. See notes on + // ForgetInodeOp for more information. + 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 +// flag and the kernel has observed that the file doesn't exist. (See for +// example lookup_open, http://goo.gl/PlqE9d). However, osxfuse doesn't appear +// to make this check atomically (cf. https://goo.gl/PqzZDv). And if names may +// be created outside of the kernel's control, it doesn't matter what the +// kernel does anyway. +// +// 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 + + // The name of the child to create, and the mode with which to create it. + Name string + Mode os.FileMode + + // Set by the file system: information about the inode that was created. + // + // The lookup count for the inode is implicitly incremented. See notes on + // ForgetInodeOp for more information. + Entry ChildInodeEntry + + // Set by the file system: an opaque ID that will be echoed in follow-up + // calls for this file using the same struct file in the kernel. In practice + // this usually means follow-up calls using the file descriptor returned by + // open(2). + // + // The handle may be supplied in future ops like ReadFileOp that contain a + // file handle. The file system must ensure this ID remains valid until a + // later call to ReleaseFileHandle. + 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 + + // The name of the symlink to create. + Name string + + // The target of the symlink. + Target string + + // Set by the file system: information about the symlink inode that was + // created. + // + // The lookup count for the inode is implicitly incremented. See notes on + // ForgetInodeOp for more information. + 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 +//////////////////////////////////////////////////////////////////////// + +// Rename a file or directory, given the IDs of the original parent directory +// and the new one (which may be the same). +// +// In Linux, this is called by vfs_rename (https://goo.gl/eERItT), which is +// called by sys_renameat2 (https://goo.gl/fCC9qC). +// +// The kernel takes care of ensuring that the source and destination are not +// identical (in which case it does nothing), that the rename is not across +// file system boundaries, and that the destination doesn't already exist with +// the wrong type. Some subtleties that the file system must care about: +// +// * If the new name is an existing directory, the file system must ensure it +// is empty before replacing it, returning ENOTEMPTY otherwise. (This is +// per the posix spec: http://goo.gl/4XtT79) +// +// * The rename must be atomic from the point of view of an observer of the +// new name. That is, if the new name already exists, there must be no +// point at which it doesn't exist. +// +// * It is okay for the new name to be modified before the old name is +// removed; these need not be atomic. In fact, the Linux man page +// explicitly says this is likely (cf. https://goo.gl/Y1wVZc). +// +// * Linux bends over backwards (https://goo.gl/pLDn3r) to ensure that +// neither the old nor the new parent can be concurrently modified. But +// it's not clear whether OS X does this, and in any case it doesn't matter +// for file systems that may be modified remotely. Therefore a careful file +// system implementor should probably ensure if possible that the unlink +// step in the "link new name, unlink old name" process doesn't unlink a +// different inode than the one that was linked to the new name. Still, +// posix and the man pages are imprecise about the actual semantics of a +// rename if it's not atomic, so it is probably not disastrous to be loose +// about this. +// +type RenameOp struct { + commonOp + + // The old parent directory, and the name of the entry within it to be + // relocated. + OldParent InodeID + OldName string + + // The new parent directory, and the name of the entry to be created or + // overwritten within it. + NewParent InodeID + 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. +// +// The file system is responsible for checking that the directory is empty. +// +// 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 +// the file open. +// +// 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 +//////////////////////////////////////////////////////////////////////// + +// Open a directory inode. +// +// On Linux the sends this when setting up a struct file for a particular inode +// with type directory, usually in response to an open(2) call from a +// 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 + + // Set by the file system: an opaque ID that will be echoed in follow-up + // calls for this directory using the same struct file in the kernel. In + // practice this usually means follow-up calls using the file descriptor + // returned by open(2). + // + // The handle may be supplied in future ops like ReadDirOp that contain a + // directory handle. The file system must ensure this ID remains valid until + // a later call to ReleaseDirHandle. + 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 + Handle HandleID + + // The offset within the directory at which to read. + // + // Warning: this field is not necessarily a count of bytes. Its legal values + // are defined by the results returned in ReadDirResponse. See the notes + // below and the notes on that struct. + // + // In the Linux kernel this ultimately comes from file::f_pos, which starts + // at zero and is set by llseek and by the final consumed result returned by + // each call to ReadDir: + // + // * (http://goo.gl/2nWJPL) iterate_dir, which is called by getdents(2) and + // readdir(2), sets dir_context::pos to file::f_pos before calling + // f_op->iterate, and then does the opposite assignment afterward. + // + // * (http://goo.gl/rTQVSL) fuse_readdir, which implements iterate for fuse + // directories, passes dir_context::pos as the offset to fuse_read_fill, + // which passes it on to user-space. fuse_readdir later calls + // parse_dirfile with the same context. + // + // * (http://goo.gl/vU5ukv) For each returned result (except perhaps the + // last, which may be truncated by the page boundary), parse_dirfile + // updates dir_context::pos with fuse_dirent::off. + // + // It is affected by the Posix directory stream interfaces in the following + // manner: + // + // * (http://goo.gl/fQhbyn, http://goo.gl/ns1kDF) opendir initially causes + // filepos to be set to zero. + // + // * (http://goo.gl/ezNKyR, http://goo.gl/xOmDv0) readdir allows the user + // to iterate through the directory one entry at a time. As each entry is + // consumed, its d_off field is stored in __dirstream::filepos. + // + // * (http://goo.gl/WEOXG8, http://goo.gl/rjSXl3) telldir allows the user + // to obtain the d_off field from the most recently returned entry. + // + // * (http://goo.gl/WG3nDZ, http://goo.gl/Lp0U6W) seekdir allows the user + // to seek backward to an offset previously returned by telldir. It + // stores the new offset in filepos, and calls llseek to update the + // kernel's struct file. + // + // * (http://goo.gl/gONQhz, http://goo.gl/VlrQkc) rewinddir allows the user + // to go back to the beginning of the directory, obtaining a fresh view. + // It updates filepos and calls llseek to update the kernel's struct + // file. + // + // Unfortunately, FUSE offers no way to intercept seeks + // (http://goo.gl/H6gEXa), so there is no way to cause seekdir or rewinddir + // to fail. Additionally, there is no way to distinguish an explicit + // rewinddir followed by readdir from the initial readdir, or a rewinddir + // from a seekdir to the value returned by telldir just after opendir. + // + // Luckily, Posix is vague about what the user will see if they seek + // backwards, and requires the user not to seek to an old offset after a + // rewind. The only requirement on freshness is that rewinddir results in + // something that looks like a newly-opened directory. So FUSE file systems + // may e.g. cache an entire fresh listing for each ReadDir with a zero + // offset, and return array offsets into that cached listing. + Offset DirOffset + + // The maximum number of bytes to return in ReadDirResponse.Data. A smaller + // number is acceptable. + Size int + + // Set by the file system: a buffer consisting of a sequence of FUSE + // directory entries in the format generated by fuse_add_direntry + // (http://goo.gl/qCcHCV), which is consumed by parse_dirfile + // (http://goo.gl/2WUmD2). Use fuseutil.AppendDirent to generate this data. + // + // The buffer must not exceed the length specified in ReadDirRequest.Size. It + // is okay for the final entry to be truncated; parse_dirfile copes with this + // by ignoring the partial record. + // + // Each entry returned exposes a directory offset to the user that may later + // show up in ReadDirRequest.Offset. See notes on that field for more + // information. + // + // An empty buffer indicates the end of the directory has been reached. + 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. +// +// The kernel guarantees that the handle ID will not be used in further ops +// sent to the file system (unless it is reissued by the file system). +// +// 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 +//////////////////////////////////////////////////////////////////////// + +// Open a file inode. +// +// On Linux the sends this when setting up a struct file for a particular inode +// with type file, usually in response to an open(2) call from a user-space +// 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 + + // An opaque ID that will be echoed in follow-up calls for this file using + // the same struct file in the kernel. In practice this usually means + // follow-up calls using the file descriptor returned by open(2). + // + // The handle may be supplied in future ops like ReadFileOp that contain a + // file handle. The file system must ensure this ID remains valid until a + // later call to ReleaseFileHandle. + 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 + Handle HandleID + + // The range of the file to read. + // + // The FUSE documentation requires that exactly the number of bytes be + // returned, except in the case of EOF or error (http://goo.gl/ZgfBkF). This + // appears to be because it uses file mmapping machinery + // (http://goo.gl/SGxnaN) to read a page at a time. It appears to understand + // where EOF is by checking the inode size (http://goo.gl/0BkqKD), returned + // by a previous call to LookUpInode, GetInodeAttributes, etc. + Offset int64 + Size int + + // Set by the file system: the data read. If this is less than the requested + // size, it indicates EOF. An error should not be returned in this case. + 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 +// cache and the page is marked dirty. Later the kernel may write back the +// page via the FUSE VFS layer, causing this op to be sent: +// +// * The kernel calls address_space_operations::writepage when a dirty page +// needs to be written to backing store (cf. http://goo.gl/Ezbewg). Fuse +// sets this to fuse_writepage (cf. http://goo.gl/IeNvLT). +// +// * (http://goo.gl/Eestuy) fuse_writepage calls fuse_writepage_locked. +// +// * (http://goo.gl/RqYIxY) fuse_writepage_locked makes a write request to +// the userspace server. +// +// Note that the kernel *will* ensure that writes are received and acknowledged +// by the file system before sending a FlushFileOp when closing the file +// descriptor to which they were written: +// +// * (http://goo.gl/PheZjf) fuse_flush calls write_inode_now, which appears +// to start a writeback in the background (it talks about a "flusher +// thread"). +// +// * (http://goo.gl/1IiepM) fuse_flush then calls fuse_sync_writes, which +// "[waits] for all pending writepages on the inode to finish". +// +// * (http://goo.gl/zzvxWv) Only then does fuse_flush finally send the +// flush request. +// +// (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 + Handle HandleID + + // The offset at which to write the data below. + // + // The man page for pwrite(2) implies that aside from changing the file + // handle's offset, using pwrite is equivalent to using lseek(2) and then + // write(2). The man page for lseek(2) says the following: + // + // "The lseek() function allows the file offset to be set beyond the end of + // the file (but this does not change the size of the file). If data is later + // written at this point, subsequent reads of the data in the gap (a "hole") + // return null bytes (aq\0aq) until data is actually written into the gap." + // + // It is therefore reasonable to assume that the kernel is looking for + // the following semantics: + // + // * If the offset is less than or equal to the current size, extend the + // file as necessary to fit any data that goes past the end of the file. + // + // * If the offset is greater than the current size, extend the file + // with null bytes until it is not, then do the above. + // + Offset int64 + + // The data to write. + // + // The FUSE documentation requires that exactly the number of bytes supplied + // be written, except on error (http://goo.gl/KUpwwn). This appears to be + // because it uses file mmapping machinery (http://goo.gl/SGxnaN) to write a + // page at a time. + 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 +// (cf. http://goo.gl/j9X8nB). Code walk for that case: +// +// * (http://goo.gl/IQkWZa) sys_fsync calls do_fsync, calls vfs_fsync, calls +// vfs_fsync_range. +// +// * (http://goo.gl/5L2SMy) vfs_fsync_range calls f_op->fsync. +// +// Note that this is also sent by fdatasync(2) (cf. http://goo.gl/01R7rF), and +// may be sent for msync(2) with the MS_SYNC flag (see the notes on +// FlushFileOp). +// +// 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. +// +// vfs.txt documents this as being sent for each close(2) system call (cf. +// http://goo.gl/FSkbrq). Code walk for that case: +// +// * (http://goo.gl/e3lv0e) sys_close calls __close_fd, calls filp_close. +// * (http://goo.gl/nI8fxD) filp_close calls f_op->flush (fuse_flush). +// +// But note that this is also sent in other contexts where a file descriptor is +// closed, such as dup2(2) (cf. http://goo.gl/NQDvFS). In the case of close(2), +// a flush error is returned to the user. For dup2(2), it is not. +// +// One potentially significant case where this may not be sent is mmap'd files, +// where the behavior is complicated: +// +// * munmap(2) does not cause flushes (cf. http://goo.gl/j8B9g0). +// +// * On OS X, if a user modifies a mapped file via the mapping before +// closing the file with close(2), the WriteFileOps for the modifications +// may not be received before the FlushFileOp for the close(2) (cf. +// http://goo.gl/kVmNcx). +// +// * However, even on OS X you can arrange for writes via a mapping to be +// flushed by calling msync(2) followed by close(2). On OS X msync(2) +// will cause a WriteFileOps to go through and close(2) will cause a +// FlushFile as usual (cf. http://goo.gl/kVmNcx). On Linux, msync(2) does +// nothing unless you set the MS_SYNC flag, in which case it causes a +// SyncFileOp to be sent (cf. http://goo.gl/P3mErk). +// +// In summary: if you make data durable in both FlushFile and SyncFile, then +// your users can get safe behavior from mapped files on both operating systems +// by calling msync(2) with MS_SYNC, followed by munmap(2), followed by +// close(2). On Linux, the msync(2) is optional (cf. http://goo.gl/EIhAxv and +// the notes on WriteFileOp). +// +// Because of cases like dup2(2), FlushFileOps are not necessarily one to one +// with OpenFileOps. They should not be used for reference counting, and the +// handle must remain valid even after the flush op is received (use +// ReleaseFileHandleOp for disposing of it). +// +// Typical "real" file systems do not implement this, presumably relying on +// the kernel to write out the page cache to the block device eventually. +// They can get away with this because a later open(2) will see the same +// data. A file system that writes to remote storage however probably wants +// 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. +// +// The kernel guarantees that the handle ID will not be used in further calls +// to the file system (unless it is reissued by the file system). +// +// 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 +} From 6488c44fe2597ecb53bd51311b437764570ccc05 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:09:56 +1000 Subject: [PATCH 02/49] Fixed package name. --- ops.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ops.go b/ops.go index 0eb15b8..8e7fb60 100644 --- a/ops.go +++ b/ops.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package fuseops +package fuse import ( "fmt" From f0bdfc203bebc105c9d9aec4814f66d80630ad07 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:10:39 +1000 Subject: [PATCH 03/49] Killed the Op interface. --- ops.go | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/ops.go b/ops.go index 8e7fb60..824780c 100644 --- a/ops.go +++ b/ops.go @@ -22,36 +22,8 @@ import ( "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 //////////////////////////////////////////////////////////////////////// From bf3dd28ec8c814f2d94eb09136e8a7a13efa0a63 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:13:08 +1000 Subject: [PATCH 04/49] lookUpInodeOp --- ops.go | 44 +++++++++++--------------------------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/ops.go b/ops.go index 824780c..d0e2142 100644 --- a/ops.go +++ b/ops.go @@ -20,51 +20,29 @@ import ( "time" "unsafe" + "github.com/jacobsa/fuse/fuseops" "github.com/jacobsa/fuse/internal/buffer" "github.com/jacobsa/fuse/internal/fusekernel" ) +type internalOp struct { +} + //////////////////////////////////////////////////////////////////////// // Inodes //////////////////////////////////////////////////////////////////////// -// 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 - - // The name of the child of interest, relative to the parent. For example, in - // this directory structure: - // - // foo/ - // bar/ - // baz - // - // the file system may receive a request to look up the child named "bar" for - // the parent foo/. - Name string - - // The resulting entry. Must be filled out by the file system. - // - // The lookup count for the inode is implicitly incremented. See notes on - // ForgetInodeOp for more information. - Entry ChildInodeEntry +type lookUpInodeOp struct { + internalOp + wrapped fuseops.LookUpInodeOp } -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) +func (o *lookUpInodeOp) kernelResponse( + protocol fusekernel.Protocol) (b buffer.OutMessage) { + size := fusekernel.EntryOutSize(protocol) b = buffer.NewOutMessage(size) out := (*fusekernel.EntryOut)(b.Grow(size)) - convertChildInodeEntry(&o.Entry, out) + convertChildInodeEntry(&o.wrapped.Entry, out) return } From c7a53f572a2f3dc7288cce29c7d0ebf653556371 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:17:39 +1000 Subject: [PATCH 05/49] Made basic updates to several structs. --- ops.go | 617 ++++----------------------------------------------------- 1 file changed, 41 insertions(+), 576 deletions(-) diff --git a/ops.go b/ops.go index d0e2142..a1cc792 100644 --- a/ops.go +++ b/ops.go @@ -16,8 +16,6 @@ package fuse import ( "fmt" - "os" - "time" "unsafe" "github.com/jacobsa/fuse/fuseops" @@ -47,22 +45,9 @@ func (o *lookUpInodeOp) kernelResponse( 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 { +type getInodeAttributesOp struct { commonOp - protocol fusekernel.Protocol - - // The inode of interest. - Inode InodeID - - // Set by the file system: attributes for the inode, and the time at which - // they should expire. See notes on ChildInodeEntry.AttributesExpiration for - // more. - Attributes InodeAttributes - AttributesExpiration time.Time + wrapped fuseops.GetInodeAttributesOp } func (o *GetInodeAttributesOp) DebugString() string { @@ -83,28 +68,9 @@ func (o *GetInodeAttributesOp) kernelResponse() (b buffer.OutMessage) { 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 { +type setInodeAttributesOp struct { commonOp - protocol fusekernel.Protocol - - // The inode of interest. - Inode InodeID - - // The attributes to modify, or nil for attributes that don't need a change. - Size *uint64 - Mode *os.FileMode - Atime *time.Time - Mtime *time.Time - - // Set by the file system: the new attributes for the inode, and the time at - // which they should expire. See notes on - // ChildInodeEntry.AttributesExpiration for more. - Attributes InodeAttributes - AttributesExpiration time.Time + wrapped fuseops.SetInodeAttributesOp } func (o *SetInodeAttributesOp) kernelResponse() (b buffer.OutMessage) { @@ -117,53 +83,9 @@ func (o *SetInodeAttributesOp) kernelResponse() (b buffer.OutMessage) { return } -// Decrement the reference count for an inode ID previously issued by the file -// system. -// -// The comments for the ops that implicitly increment the reference count -// contain a note of this (but see also the note about the root inode below). -// For example, LookUpInodeOp and MkDirOp. The authoritative source is the -// libfuse documentation, which states that any op that returns -// fuse_reply_entry fuse_reply_create implicitly increments (cf. -// http://goo.gl/o5C7Dx). -// -// If the reference count hits zero, the file system can forget about that ID -// entirely, and even re-use it in future responses. The kernel guarantees that -// it will not otherwise use it again. -// -// The reference count corresponds to fuse_inode::nlookup -// (http://goo.gl/ut48S4). Some examples of where the kernel manipulates it: -// -// * (http://goo.gl/vPD9Oh) Any caller to fuse_iget increases the count. -// * (http://goo.gl/B6tTTC) fuse_lookup_name calls fuse_iget. -// * (http://goo.gl/IlcxWv) fuse_create_open calls fuse_iget. -// * (http://goo.gl/VQMQul) fuse_dentry_revalidate increments after -// revalidating. -// -// In contrast to all other inodes, RootInodeID begins with an implicit -// lookup count of one, without a corresponding op to increase it. (There -// could be no such op, because the root cannot be referred to by name.) Code -// walk: -// -// * (http://goo.gl/gWAheU) fuse_fill_super calls fuse_get_root_inode. -// -// * (http://goo.gl/AoLsbb) fuse_get_root_inode calls fuse_iget without -// sending any particular request. -// -// * (http://goo.gl/vPD9Oh) fuse_iget increments nlookup. -// -// File systems should tolerate but not rely on receiving forget ops for -// remaining inodes when the file system unmounts, including the root inode. -// Rather they should take fuse.Connection.ReadOp returning io.EOF as -// implicitly decrementing all lookup counts to zero. -type ForgetInodeOp struct { +type forgetInodeOp struct { commonOp - - // The inode whose reference count should be decremented. - Inode InodeID - - // The amount to decrement the reference count. - N uint64 + wrapped fuseops.ForgetInodeOp } func (o *ForgetInodeOp) kernelResponse() (b buffer.OutMessage) { @@ -175,33 +97,9 @@ func (o *ForgetInodeOp) kernelResponse() (b buffer.OutMessage) { // Inode creation //////////////////////////////////////////////////////////////////////// -// Create a directory inode as a child of an existing directory inode. The -// kernel sends this in response to a mkdir(2) call. -// -// The Linux kernel appears to verify the name doesn't already exist (mkdir -// calls mkdirat calls user_path_create calls filename_create, which verifies: -// http://goo.gl/FZpLu5). Indeed, the tests in samples/memfs that call in -// parallel appear to bear this out. But osxfuse does not appear to guarantee -// this (cf. https://goo.gl/PqzZDv). And if names may be created outside of the -// kernel's control, it doesn't matter what the kernel does anyway. -// -// Therefore the file system should return EEXIST if the name already exists. -type MkDirOp struct { +type mkDirOp struct { commonOp - protocol fusekernel.Protocol - - // The ID of parent directory inode within which to create the child. - Parent InodeID - - // The name of the child to create, and the mode with which to create it. - Name string - Mode os.FileMode - - // Set by the file system: information about the inode that was created. - // - // The lookup count for the inode is implicitly incremented. See notes on - // ForgetInodeOp for more information. - Entry ChildInodeEntry + wrapped fuseops.MkDirOp } func (o *MkDirOp) ShortDesc() (desc string) { @@ -218,42 +116,9 @@ func (o *MkDirOp) kernelResponse() (b buffer.OutMessage) { return } -// Create a file inode and open it. -// -// The kernel sends this when the user asks to open a file with the O_CREAT -// flag and the kernel has observed that the file doesn't exist. (See for -// example lookup_open, http://goo.gl/PlqE9d). However, osxfuse doesn't appear -// to make this check atomically (cf. https://goo.gl/PqzZDv). And if names may -// be created outside of the kernel's control, it doesn't matter what the -// kernel does anyway. -// -// Therefore the file system should return EEXIST if the name already exists. -type CreateFileOp struct { +type createFileOp struct { commonOp - protocol fusekernel.Protocol - - // The ID of parent directory inode within which to create the child file. - Parent InodeID - - // The name of the child to create, and the mode with which to create it. - Name string - Mode os.FileMode - - // Set by the file system: information about the inode that was created. - // - // The lookup count for the inode is implicitly incremented. See notes on - // ForgetInodeOp for more information. - Entry ChildInodeEntry - - // Set by the file system: an opaque ID that will be echoed in follow-up - // calls for this file using the same struct file in the kernel. In practice - // this usually means follow-up calls using the file descriptor returned by - // open(2). - // - // The handle may be supplied in future ops like ReadFileOp that contain a - // file handle. The file system must ensure this ID remains valid until a - // later call to ReleaseFileHandle. - Handle HandleID + wrapped fuseops.CreateFileOp } func (o *CreateFileOp) ShortDesc() (desc string) { @@ -274,27 +139,9 @@ func (o *CreateFileOp) kernelResponse() (b buffer.OutMessage) { 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 { +type createSymlinkOp struct { commonOp - protocol fusekernel.Protocol - - // The ID of parent directory inode within which to create the child symlink. - Parent InodeID - - // The name of the symlink to create. - Name string - - // The target of the symlink. - Target string - - // Set by the file system: information about the symlink inode that was - // created. - // - // The lookup count for the inode is implicitly incremented. See notes on - // ForgetInodeOp for more information. - Entry ChildInodeEntry + wrapped fuseops.CreateSymlinkOp } func (o *CreateSymlinkOp) ShortDesc() (desc string) { @@ -320,52 +167,9 @@ func (o *CreateSymlinkOp) kernelResponse() (b buffer.OutMessage) { // Unlinking //////////////////////////////////////////////////////////////////////// -// Rename a file or directory, given the IDs of the original parent directory -// and the new one (which may be the same). -// -// In Linux, this is called by vfs_rename (https://goo.gl/eERItT), which is -// called by sys_renameat2 (https://goo.gl/fCC9qC). -// -// The kernel takes care of ensuring that the source and destination are not -// identical (in which case it does nothing), that the rename is not across -// file system boundaries, and that the destination doesn't already exist with -// the wrong type. Some subtleties that the file system must care about: -// -// * If the new name is an existing directory, the file system must ensure it -// is empty before replacing it, returning ENOTEMPTY otherwise. (This is -// per the posix spec: http://goo.gl/4XtT79) -// -// * The rename must be atomic from the point of view of an observer of the -// new name. That is, if the new name already exists, there must be no -// point at which it doesn't exist. -// -// * It is okay for the new name to be modified before the old name is -// removed; these need not be atomic. In fact, the Linux man page -// explicitly says this is likely (cf. https://goo.gl/Y1wVZc). -// -// * Linux bends over backwards (https://goo.gl/pLDn3r) to ensure that -// neither the old nor the new parent can be concurrently modified. But -// it's not clear whether OS X does this, and in any case it doesn't matter -// for file systems that may be modified remotely. Therefore a careful file -// system implementor should probably ensure if possible that the unlink -// step in the "link new name, unlink old name" process doesn't unlink a -// different inode than the one that was linked to the new name. Still, -// posix and the man pages are imprecise about the actual semantics of a -// rename if it's not atomic, so it is probably not disastrous to be loose -// about this. -// -type RenameOp struct { +type renameOp struct { commonOp - - // The old parent directory, and the name of the entry within it to be - // relocated. - OldParent InodeID - OldName string - - // The new parent directory, and the name of the entry to be created or - // overwritten within it. - NewParent InodeID - NewName string + wrapped fuseops.RenameOp } func (o *RenameOp) kernelResponse() (b buffer.OutMessage) { @@ -373,20 +177,9 @@ func (o *RenameOp) kernelResponse() (b buffer.OutMessage) { 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. -// -// The file system is responsible for checking that the directory is empty. -// -// Sample implementation in ext2: ext2_rmdir (http://goo.gl/B9QmFf) -type RmDirOp struct { +type rmDirOp struct { commonOp - - // The ID of parent directory inode, and the name of the directory being - // removed within it. - Parent InodeID - Name string + wrapped fuseops.RmDirOp } func (o *RmDirOp) kernelResponse() (b buffer.OutMessage) { @@ -394,19 +187,9 @@ func (o *RmDirOp) kernelResponse() (b buffer.OutMessage) { 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 -// the file open. -// -// Sample implementation in ext2: ext2_unlink (http://goo.gl/hY6r6C) -type UnlinkOp struct { +type unlinkOp struct { commonOp - - // The ID of parent directory inode, and the name of the entry being removed - // within it. - Parent InodeID - Name string + wrapped fuseops.UnlinkOp } func (o *UnlinkOp) kernelResponse() (b buffer.OutMessage) { @@ -418,27 +201,9 @@ func (o *UnlinkOp) kernelResponse() (b buffer.OutMessage) { // Directory handles //////////////////////////////////////////////////////////////////////// -// Open a directory inode. -// -// On Linux the sends this when setting up a struct file for a particular inode -// with type directory, usually in response to an open(2) call from a -// 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 { +type openDirOp struct { commonOp - - // The ID of the inode to be opened. - Inode InodeID - - // Set by the file system: an opaque ID that will be echoed in follow-up - // calls for this directory using the same struct file in the kernel. In - // practice this usually means follow-up calls using the file descriptor - // returned by open(2). - // - // The handle may be supplied in future ops like ReadDirOp that contain a - // directory handle. The file system must ensure this ID remains valid until - // a later call to ReleaseDirHandle. - Handle HandleID + wrapped fuseops.OpenDirOp } func (o *OpenDirOp) kernelResponse() (b buffer.OutMessage) { @@ -449,94 +214,9 @@ func (o *OpenDirOp) kernelResponse() (b buffer.OutMessage) { return } -// Read entries from a directory previously opened with OpenDir. -type ReadDirOp struct { +type readDirOp struct { commonOp - - // The directory inode that we are reading, and the handle previously - // returned by OpenDir when opening that inode. - Inode InodeID - Handle HandleID - - // The offset within the directory at which to read. - // - // Warning: this field is not necessarily a count of bytes. Its legal values - // are defined by the results returned in ReadDirResponse. See the notes - // below and the notes on that struct. - // - // In the Linux kernel this ultimately comes from file::f_pos, which starts - // at zero and is set by llseek and by the final consumed result returned by - // each call to ReadDir: - // - // * (http://goo.gl/2nWJPL) iterate_dir, which is called by getdents(2) and - // readdir(2), sets dir_context::pos to file::f_pos before calling - // f_op->iterate, and then does the opposite assignment afterward. - // - // * (http://goo.gl/rTQVSL) fuse_readdir, which implements iterate for fuse - // directories, passes dir_context::pos as the offset to fuse_read_fill, - // which passes it on to user-space. fuse_readdir later calls - // parse_dirfile with the same context. - // - // * (http://goo.gl/vU5ukv) For each returned result (except perhaps the - // last, which may be truncated by the page boundary), parse_dirfile - // updates dir_context::pos with fuse_dirent::off. - // - // It is affected by the Posix directory stream interfaces in the following - // manner: - // - // * (http://goo.gl/fQhbyn, http://goo.gl/ns1kDF) opendir initially causes - // filepos to be set to zero. - // - // * (http://goo.gl/ezNKyR, http://goo.gl/xOmDv0) readdir allows the user - // to iterate through the directory one entry at a time. As each entry is - // consumed, its d_off field is stored in __dirstream::filepos. - // - // * (http://goo.gl/WEOXG8, http://goo.gl/rjSXl3) telldir allows the user - // to obtain the d_off field from the most recently returned entry. - // - // * (http://goo.gl/WG3nDZ, http://goo.gl/Lp0U6W) seekdir allows the user - // to seek backward to an offset previously returned by telldir. It - // stores the new offset in filepos, and calls llseek to update the - // kernel's struct file. - // - // * (http://goo.gl/gONQhz, http://goo.gl/VlrQkc) rewinddir allows the user - // to go back to the beginning of the directory, obtaining a fresh view. - // It updates filepos and calls llseek to update the kernel's struct - // file. - // - // Unfortunately, FUSE offers no way to intercept seeks - // (http://goo.gl/H6gEXa), so there is no way to cause seekdir or rewinddir - // to fail. Additionally, there is no way to distinguish an explicit - // rewinddir followed by readdir from the initial readdir, or a rewinddir - // from a seekdir to the value returned by telldir just after opendir. - // - // Luckily, Posix is vague about what the user will see if they seek - // backwards, and requires the user not to seek to an old offset after a - // rewind. The only requirement on freshness is that rewinddir results in - // something that looks like a newly-opened directory. So FUSE file systems - // may e.g. cache an entire fresh listing for each ReadDir with a zero - // offset, and return array offsets into that cached listing. - Offset DirOffset - - // The maximum number of bytes to return in ReadDirResponse.Data. A smaller - // number is acceptable. - Size int - - // Set by the file system: a buffer consisting of a sequence of FUSE - // directory entries in the format generated by fuse_add_direntry - // (http://goo.gl/qCcHCV), which is consumed by parse_dirfile - // (http://goo.gl/2WUmD2). Use fuseutil.AppendDirent to generate this data. - // - // The buffer must not exceed the length specified in ReadDirRequest.Size. It - // is okay for the final entry to be truncated; parse_dirfile copes with this - // by ignoring the partial record. - // - // Each entry returned exposes a directory offset to the user that may later - // show up in ReadDirRequest.Offset. See notes on that field for more - // information. - // - // An empty buffer indicates the end of the directory has been reached. - Data []byte + wrapped fuseops.ReadDirOp } func (o *ReadDirOp) kernelResponse() (b buffer.OutMessage) { @@ -545,21 +225,9 @@ func (o *ReadDirOp) kernelResponse() (b buffer.OutMessage) { 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. -// -// The kernel guarantees that the handle ID will not be used in further ops -// sent to the file system (unless it is reissued by the file system). -// -// Errors from this op are ignored by the kernel (cf. http://goo.gl/RL38Do). -type ReleaseDirHandleOp struct { +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 + wrapped fuseops.ReleaseDirHandleOp } func (o *ReleaseDirHandleOp) kernelResponse() (b buffer.OutMessage) { @@ -571,26 +239,9 @@ func (o *ReleaseDirHandleOp) kernelResponse() (b buffer.OutMessage) { // File handles //////////////////////////////////////////////////////////////////////// -// Open a file inode. -// -// On Linux the sends this when setting up a struct file for a particular inode -// with type file, usually in response to an open(2) call from a user-space -// process. On OS X it may not be sent for every open(2) -// (cf.https://github.com/osxfuse/osxfuse/issues/199). -type OpenFileOp struct { +type openFileOp struct { commonOp - - // The ID of the inode to be opened. - Inode InodeID - - // An opaque ID that will be echoed in follow-up calls for this file using - // the same struct file in the kernel. In practice this usually means - // follow-up calls using the file descriptor returned by open(2). - // - // The handle may be supplied in future ops like ReadFileOp that contain a - // file handle. The file system must ensure this ID remains valid until a - // later call to ReleaseFileHandle. - Handle HandleID + wrapped fuseops.OpenFileOp } func (o *OpenFileOp) kernelResponse() (b buffer.OutMessage) { @@ -601,33 +252,9 @@ func (o *OpenFileOp) kernelResponse() (b buffer.OutMessage) { 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 { +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 - Handle HandleID - - // The range of the file to read. - // - // The FUSE documentation requires that exactly the number of bytes be - // returned, except in the case of EOF or error (http://goo.gl/ZgfBkF). This - // appears to be because it uses file mmapping machinery - // (http://goo.gl/SGxnaN) to read a page at a time. It appears to understand - // where EOF is by checking the inode size (http://goo.gl/0BkqKD), returned - // by a previous call to LookUpInode, GetInodeAttributes, etc. - Offset int64 - Size int - - // Set by the file system: the data read. If this is less than the requested - // size, it indicates EOF. An error should not be returned in this case. - Data []byte + wrapped fuseops.ReadFileOp } func (o *ReadFileOp) kernelResponse() (b buffer.OutMessage) { @@ -636,74 +263,9 @@ func (o *ReadFileOp) kernelResponse() (b buffer.OutMessage) { 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 -// cache and the page is marked dirty. Later the kernel may write back the -// page via the FUSE VFS layer, causing this op to be sent: -// -// * The kernel calls address_space_operations::writepage when a dirty page -// needs to be written to backing store (cf. http://goo.gl/Ezbewg). Fuse -// sets this to fuse_writepage (cf. http://goo.gl/IeNvLT). -// -// * (http://goo.gl/Eestuy) fuse_writepage calls fuse_writepage_locked. -// -// * (http://goo.gl/RqYIxY) fuse_writepage_locked makes a write request to -// the userspace server. -// -// Note that the kernel *will* ensure that writes are received and acknowledged -// by the file system before sending a FlushFileOp when closing the file -// descriptor to which they were written: -// -// * (http://goo.gl/PheZjf) fuse_flush calls write_inode_now, which appears -// to start a writeback in the background (it talks about a "flusher -// thread"). -// -// * (http://goo.gl/1IiepM) fuse_flush then calls fuse_sync_writes, which -// "[waits] for all pending writepages on the inode to finish". -// -// * (http://goo.gl/zzvxWv) Only then does fuse_flush finally send the -// flush request. -// -// (See also http://goo.gl/ocdTdM, fuse-devel thread "Fuse guarantees on -// concurrent requests".) -type WriteFileOp struct { +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 - Handle HandleID - - // The offset at which to write the data below. - // - // The man page for pwrite(2) implies that aside from changing the file - // handle's offset, using pwrite is equivalent to using lseek(2) and then - // write(2). The man page for lseek(2) says the following: - // - // "The lseek() function allows the file offset to be set beyond the end of - // the file (but this does not change the size of the file). If data is later - // written at this point, subsequent reads of the data in the gap (a "hole") - // return null bytes (aq\0aq) until data is actually written into the gap." - // - // It is therefore reasonable to assume that the kernel is looking for - // the following semantics: - // - // * If the offset is less than or equal to the current size, extend the - // file as necessary to fit any data that goes past the end of the file. - // - // * If the offset is greater than the current size, extend the file - // with null bytes until it is not, then do the above. - // - Offset int64 - - // The data to write. - // - // The FUSE documentation requires that exactly the number of bytes supplied - // be written, except on error (http://goo.gl/KUpwwn). This appears to be - // because it uses file mmapping machinery (http://goo.gl/SGxnaN) to write a - // page at a time. - Data []byte + wrapped fuseops.WriteFileOp } func (o *WriteFileOp) kernelResponse() (b buffer.OutMessage) { @@ -714,28 +276,9 @@ func (o *WriteFileOp) kernelResponse() (b buffer.OutMessage) { 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 -// (cf. http://goo.gl/j9X8nB). Code walk for that case: -// -// * (http://goo.gl/IQkWZa) sys_fsync calls do_fsync, calls vfs_fsync, calls -// vfs_fsync_range. -// -// * (http://goo.gl/5L2SMy) vfs_fsync_range calls f_op->fsync. -// -// Note that this is also sent by fdatasync(2) (cf. http://goo.gl/01R7rF), and -// may be sent for msync(2) with the MS_SYNC flag (see the notes on -// FlushFileOp). -// -// 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 { +type syncFileOp struct { commonOp - - // The file and handle being sync'd. - Inode InodeID - Handle HandleID + wrapped fuseops.SyncFileOp } func (o *SyncFileOp) kernelResponse() (b buffer.OutMessage) { @@ -743,59 +286,9 @@ func (o *SyncFileOp) kernelResponse() (b buffer.OutMessage) { return } -// Flush the current state of an open file to storage upon closing a file -// descriptor. -// -// vfs.txt documents this as being sent for each close(2) system call (cf. -// http://goo.gl/FSkbrq). Code walk for that case: -// -// * (http://goo.gl/e3lv0e) sys_close calls __close_fd, calls filp_close. -// * (http://goo.gl/nI8fxD) filp_close calls f_op->flush (fuse_flush). -// -// But note that this is also sent in other contexts where a file descriptor is -// closed, such as dup2(2) (cf. http://goo.gl/NQDvFS). In the case of close(2), -// a flush error is returned to the user. For dup2(2), it is not. -// -// One potentially significant case where this may not be sent is mmap'd files, -// where the behavior is complicated: -// -// * munmap(2) does not cause flushes (cf. http://goo.gl/j8B9g0). -// -// * On OS X, if a user modifies a mapped file via the mapping before -// closing the file with close(2), the WriteFileOps for the modifications -// may not be received before the FlushFileOp for the close(2) (cf. -// http://goo.gl/kVmNcx). -// -// * However, even on OS X you can arrange for writes via a mapping to be -// flushed by calling msync(2) followed by close(2). On OS X msync(2) -// will cause a WriteFileOps to go through and close(2) will cause a -// FlushFile as usual (cf. http://goo.gl/kVmNcx). On Linux, msync(2) does -// nothing unless you set the MS_SYNC flag, in which case it causes a -// SyncFileOp to be sent (cf. http://goo.gl/P3mErk). -// -// In summary: if you make data durable in both FlushFile and SyncFile, then -// your users can get safe behavior from mapped files on both operating systems -// by calling msync(2) with MS_SYNC, followed by munmap(2), followed by -// close(2). On Linux, the msync(2) is optional (cf. http://goo.gl/EIhAxv and -// the notes on WriteFileOp). -// -// Because of cases like dup2(2), FlushFileOps are not necessarily one to one -// with OpenFileOps. They should not be used for reference counting, and the -// handle must remain valid even after the flush op is received (use -// ReleaseFileHandleOp for disposing of it). -// -// Typical "real" file systems do not implement this, presumably relying on -// the kernel to write out the page cache to the block device eventually. -// They can get away with this because a later open(2) will see the same -// data. A file system that writes to remote storage however probably wants -// to at least schedule a real flush, and maybe do it immediately in order to -// return any errors that occur. -type FlushFileOp struct { +type flushFileOp struct { commonOp - - // The file and handle being flushed. - Inode InodeID - Handle HandleID + wrapped fuseops.FlushFileOp } func (o *FlushFileOp) kernelResponse() (b buffer.OutMessage) { @@ -803,21 +296,9 @@ func (o *FlushFileOp) kernelResponse() (b buffer.OutMessage) { 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. -// -// The kernel guarantees that the handle ID will not be used in further calls -// to the file system (unless it is reissued by the file system). -// -// Errors from this op are ignored by the kernel (cf. http://goo.gl/RL38Do). -type ReleaseFileHandleOp struct { +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 + wrapped fuseops.ReleaseFileHandleOp } func (o *ReleaseFileHandleOp) kernelResponse() (b buffer.OutMessage) { @@ -846,15 +327,9 @@ func (o *unknownOp) kernelResponse() (b buffer.OutMessage) { // Reading symlinks //////////////////////////////////////////////////////////////////////// -// Read the target of a symlink inode. -type ReadSymlinkOp struct { +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 + wrapped fuseops.ReadSymlinkOp } func (o *ReadSymlinkOp) kernelResponse() (b buffer.OutMessage) { @@ -867,15 +342,7 @@ func (o *ReadSymlinkOp) kernelResponse() (b buffer.OutMessage) { // 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 { +type internalStatFSOp struct { commonOp } @@ -886,8 +353,7 @@ func (o *InternalStatFSOp) kernelResponse() (b buffer.OutMessage) { return } -// Do not use this struct directly. See the TODO in fuseops/ops.go. -type InternalInterruptOp struct { +type internalInterruptOp struct { commonOp FuseID uint64 } @@ -896,8 +362,7 @@ 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 { +type internalInitOp struct { commonOp // In From f1f41736b3c85d81c9c2d98108dcf7084c93f4e8 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:18:06 +1000 Subject: [PATCH 06/49] Updated commonOp fields. --- ops.go | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/ops.go b/ops.go index a1cc792..ff576e7 100644 --- a/ops.go +++ b/ops.go @@ -46,7 +46,7 @@ func (o *lookUpInodeOp) kernelResponse( } type getInodeAttributesOp struct { - commonOp + internalOp wrapped fuseops.GetInodeAttributesOp } @@ -69,7 +69,7 @@ func (o *GetInodeAttributesOp) kernelResponse() (b buffer.OutMessage) { } type setInodeAttributesOp struct { - commonOp + internalOp wrapped fuseops.SetInodeAttributesOp } @@ -84,7 +84,7 @@ func (o *SetInodeAttributesOp) kernelResponse() (b buffer.OutMessage) { } type forgetInodeOp struct { - commonOp + internalOp wrapped fuseops.ForgetInodeOp } @@ -98,7 +98,7 @@ func (o *ForgetInodeOp) kernelResponse() (b buffer.OutMessage) { //////////////////////////////////////////////////////////////////////// type mkDirOp struct { - commonOp + internalOp wrapped fuseops.MkDirOp } @@ -117,7 +117,7 @@ func (o *MkDirOp) kernelResponse() (b buffer.OutMessage) { } type createFileOp struct { - commonOp + internalOp wrapped fuseops.CreateFileOp } @@ -140,7 +140,7 @@ func (o *CreateFileOp) kernelResponse() (b buffer.OutMessage) { } type createSymlinkOp struct { - commonOp + internalOp wrapped fuseops.CreateSymlinkOp } @@ -168,7 +168,7 @@ func (o *CreateSymlinkOp) kernelResponse() (b buffer.OutMessage) { //////////////////////////////////////////////////////////////////////// type renameOp struct { - commonOp + internalOp wrapped fuseops.RenameOp } @@ -178,7 +178,7 @@ func (o *RenameOp) kernelResponse() (b buffer.OutMessage) { } type rmDirOp struct { - commonOp + internalOp wrapped fuseops.RmDirOp } @@ -188,7 +188,7 @@ func (o *RmDirOp) kernelResponse() (b buffer.OutMessage) { } type unlinkOp struct { - commonOp + internalOp wrapped fuseops.UnlinkOp } @@ -202,7 +202,7 @@ func (o *UnlinkOp) kernelResponse() (b buffer.OutMessage) { //////////////////////////////////////////////////////////////////////// type openDirOp struct { - commonOp + internalOp wrapped fuseops.OpenDirOp } @@ -215,7 +215,7 @@ func (o *OpenDirOp) kernelResponse() (b buffer.OutMessage) { } type readDirOp struct { - commonOp + internalOp wrapped fuseops.ReadDirOp } @@ -226,7 +226,7 @@ func (o *ReadDirOp) kernelResponse() (b buffer.OutMessage) { } type releaseDirHandleOp struct { - commonOp + internalOp wrapped fuseops.ReleaseDirHandleOp } @@ -240,7 +240,7 @@ func (o *ReleaseDirHandleOp) kernelResponse() (b buffer.OutMessage) { //////////////////////////////////////////////////////////////////////// type openFileOp struct { - commonOp + internalOp wrapped fuseops.OpenFileOp } @@ -253,7 +253,7 @@ func (o *OpenFileOp) kernelResponse() (b buffer.OutMessage) { } type readFileOp struct { - commonOp + internalOp wrapped fuseops.ReadFileOp } @@ -264,7 +264,7 @@ func (o *ReadFileOp) kernelResponse() (b buffer.OutMessage) { } type writeFileOp struct { - commonOp + internalOp wrapped fuseops.WriteFileOp } @@ -277,7 +277,7 @@ func (o *WriteFileOp) kernelResponse() (b buffer.OutMessage) { } type syncFileOp struct { - commonOp + internalOp wrapped fuseops.SyncFileOp } @@ -287,7 +287,7 @@ func (o *SyncFileOp) kernelResponse() (b buffer.OutMessage) { } type flushFileOp struct { - commonOp + internalOp wrapped fuseops.FlushFileOp } @@ -297,7 +297,7 @@ func (o *FlushFileOp) kernelResponse() (b buffer.OutMessage) { } type releaseFileHandleOp struct { - commonOp + internalOp wrapped fuseops.ReleaseFileHandleOp } @@ -309,7 +309,7 @@ func (o *ReleaseFileHandleOp) kernelResponse() (b buffer.OutMessage) { // A sentinel used for unknown ops. The user is expected to respond with a // non-nil error. type unknownOp struct { - commonOp + internalOp opCode uint32 inode InodeID } @@ -328,7 +328,7 @@ func (o *unknownOp) kernelResponse() (b buffer.OutMessage) { //////////////////////////////////////////////////////////////////////// type readSymlinkOp struct { - commonOp + internalOp wrapped fuseops.ReadSymlinkOp } @@ -343,7 +343,7 @@ func (o *ReadSymlinkOp) kernelResponse() (b buffer.OutMessage) { //////////////////////////////////////////////////////////////////////// type internalStatFSOp struct { - commonOp + internalOp } func (o *InternalStatFSOp) kernelResponse() (b buffer.OutMessage) { @@ -354,7 +354,7 @@ func (o *InternalStatFSOp) kernelResponse() (b buffer.OutMessage) { } type internalInterruptOp struct { - commonOp + internalOp FuseID uint64 } @@ -363,7 +363,7 @@ func (o *InternalInterruptOp) kernelResponse() (b buffer.OutMessage) { } type internalInitOp struct { - commonOp + internalOp // In Kernel fusekernel.Protocol From 712ae496242bc2ad080d61625dd7e2d33376c1d1 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:20:27 +1000 Subject: [PATCH 07/49] Trimmed and updated several functions. --- ops.go | 79 +++++++++++++++++----------------------------------------- 1 file changed, 23 insertions(+), 56 deletions(-) diff --git a/ops.go b/ops.go index ff576e7..f8f7f36 100644 --- a/ops.go +++ b/ops.go @@ -50,15 +50,7 @@ type getInodeAttributesOp struct { wrapped fuseops.GetInodeAttributesOp } -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) { +func (o *getInodeAttributesOp) kernelResponse() (b buffer.OutMessage) { size := fusekernel.AttrOutSize(o.protocol) b = buffer.NewOutMessage(size) out := (*fusekernel.AttrOut)(b.Grow(size)) @@ -73,7 +65,7 @@ type setInodeAttributesOp struct { wrapped fuseops.SetInodeAttributesOp } -func (o *SetInodeAttributesOp) kernelResponse() (b buffer.OutMessage) { +func (o *setInodeAttributesOp) kernelResponse() (b buffer.OutMessage) { size := fusekernel.AttrOutSize(o.protocol) b = buffer.NewOutMessage(size) out := (*fusekernel.AttrOut)(b.Grow(size)) @@ -88,7 +80,7 @@ type forgetInodeOp struct { wrapped fuseops.ForgetInodeOp } -func (o *ForgetInodeOp) kernelResponse() (b buffer.OutMessage) { +func (o *forgetInodeOp) kernelResponse() (b buffer.OutMessage) { // No response. return } @@ -102,12 +94,7 @@ type mkDirOp struct { wrapped fuseops.MkDirOp } -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) { +func (o *mkDirOp) kernelResponse() (b buffer.OutMessage) { size := fusekernel.EntryOutSize(o.protocol) b = buffer.NewOutMessage(size) out := (*fusekernel.EntryOut)(b.Grow(size)) @@ -121,12 +108,7 @@ type createFileOp struct { wrapped fuseops.CreateFileOp } -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) { +func (o *createFileOp) kernelResponse() (b buffer.OutMessage) { eSize := fusekernel.EntryOutSize(o.protocol) b = buffer.NewOutMessage(eSize + unsafe.Sizeof(fusekernel.OpenOut{})) @@ -144,17 +126,7 @@ type createSymlinkOp struct { wrapped fuseops.CreateSymlinkOp } -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) { +func (o *createSymlinkOp) kernelResponse() (b buffer.OutMessage) { size := fusekernel.EntryOutSize(o.protocol) b = buffer.NewOutMessage(size) out := (*fusekernel.EntryOut)(b.Grow(size)) @@ -172,7 +144,7 @@ type renameOp struct { wrapped fuseops.RenameOp } -func (o *RenameOp) kernelResponse() (b buffer.OutMessage) { +func (o *renameOp) kernelResponse() (b buffer.OutMessage) { b = buffer.NewOutMessage(0) return } @@ -182,7 +154,7 @@ type rmDirOp struct { wrapped fuseops.RmDirOp } -func (o *RmDirOp) kernelResponse() (b buffer.OutMessage) { +func (o *rmDirOp) kernelResponse() (b buffer.OutMessage) { b = buffer.NewOutMessage(0) return } @@ -192,7 +164,7 @@ type unlinkOp struct { wrapped fuseops.UnlinkOp } -func (o *UnlinkOp) kernelResponse() (b buffer.OutMessage) { +func (o *unlinkOp) kernelResponse() (b buffer.OutMessage) { b = buffer.NewOutMessage(0) return } @@ -206,7 +178,7 @@ type openDirOp struct { wrapped fuseops.OpenDirOp } -func (o *OpenDirOp) kernelResponse() (b buffer.OutMessage) { +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) @@ -219,7 +191,7 @@ type readDirOp struct { wrapped fuseops.ReadDirOp } -func (o *ReadDirOp) kernelResponse() (b buffer.OutMessage) { +func (o *readDirOp) kernelResponse() (b buffer.OutMessage) { b = buffer.NewOutMessage(uintptr(len(o.Data))) b.Append(o.Data) return @@ -230,7 +202,7 @@ type releaseDirHandleOp struct { wrapped fuseops.ReleaseDirHandleOp } -func (o *ReleaseDirHandleOp) kernelResponse() (b buffer.OutMessage) { +func (o *releaseDirHandleOp) kernelResponse() (b buffer.OutMessage) { b = buffer.NewOutMessage(0) return } @@ -244,7 +216,7 @@ type openFileOp struct { wrapped fuseops.OpenFileOp } -func (o *OpenFileOp) kernelResponse() (b buffer.OutMessage) { +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) @@ -257,7 +229,7 @@ type readFileOp struct { wrapped fuseops.ReadFileOp } -func (o *ReadFileOp) kernelResponse() (b buffer.OutMessage) { +func (o *readFileOp) kernelResponse() (b buffer.OutMessage) { b = buffer.NewOutMessage(uintptr(len(o.Data))) b.Append(o.Data) return @@ -268,7 +240,7 @@ type writeFileOp struct { wrapped fuseops.WriteFileOp } -func (o *WriteFileOp) kernelResponse() (b buffer.OutMessage) { +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)) @@ -281,7 +253,7 @@ type syncFileOp struct { wrapped fuseops.SyncFileOp } -func (o *SyncFileOp) kernelResponse() (b buffer.OutMessage) { +func (o *syncFileOp) kernelResponse() (b buffer.OutMessage) { b = buffer.NewOutMessage(0) return } @@ -291,7 +263,7 @@ type flushFileOp struct { wrapped fuseops.FlushFileOp } -func (o *FlushFileOp) kernelResponse() (b buffer.OutMessage) { +func (o *flushFileOp) kernelResponse() (b buffer.OutMessage) { b = buffer.NewOutMessage(0) return } @@ -301,7 +273,7 @@ type releaseFileHandleOp struct { wrapped fuseops.ReleaseFileHandleOp } -func (o *ReleaseFileHandleOp) kernelResponse() (b buffer.OutMessage) { +func (o *releaseFileHandleOp) kernelResponse() (b buffer.OutMessage) { b = buffer.NewOutMessage(0) return } @@ -311,12 +283,7 @@ func (o *ReleaseFileHandleOp) kernelResponse() (b buffer.OutMessage) { type unknownOp struct { internalOp opCode uint32 - inode InodeID -} - -func (o *unknownOp) ShortDesc() (desc string) { - desc = fmt.Sprintf("(inode=%v)", o.opCode, o.inode) - return + inode fuseops.InodeID } func (o *unknownOp) kernelResponse() (b buffer.OutMessage) { @@ -332,7 +299,7 @@ type readSymlinkOp struct { wrapped fuseops.ReadSymlinkOp } -func (o *ReadSymlinkOp) kernelResponse() (b buffer.OutMessage) { +func (o *readSymlinkOp) kernelResponse() (b buffer.OutMessage) { b = buffer.NewOutMessage(uintptr(len(o.Target))) b.AppendString(o.Target) return @@ -346,7 +313,7 @@ type internalStatFSOp struct { internalOp } -func (o *InternalStatFSOp) kernelResponse() (b buffer.OutMessage) { +func (o *internalStatFSOp) kernelResponse() (b buffer.OutMessage) { b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{})) b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{})) @@ -358,7 +325,7 @@ type internalInterruptOp struct { FuseID uint64 } -func (o *InternalInterruptOp) kernelResponse() (b buffer.OutMessage) { +func (o *internalInterruptOp) kernelResponse() (b buffer.OutMessage) { panic("Shouldn't get here.") } @@ -375,7 +342,7 @@ type internalInitOp struct { MaxWrite uint32 } -func (o *InternalInitOp) kernelResponse() (b buffer.OutMessage) { +func (o *internalInitOp) kernelResponse() (b buffer.OutMessage) { b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.InitOut{})) out := (*fusekernel.InitOut)(b.Grow(unsafe.Sizeof(fusekernel.InitOut{}))) From cf97f40c331fb1f205e5618a86ee91b83a647705 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:20:54 +1000 Subject: [PATCH 08/49] Moved over convert.go. --- fuseops/convert.go => conversions.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename fuseops/convert.go => conversions.go (100%) diff --git a/fuseops/convert.go b/conversions.go similarity index 100% rename from fuseops/convert.go rename to conversions.go From 6389e8773f9cf6707577e7bdd44cb75b3b3ee35d Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:21:36 +1000 Subject: [PATCH 09/49] Fixed package name. --- conversions.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conversions.go b/conversions.go index 930e444..5b2c41e 100644 --- a/conversions.go +++ b/conversions.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package fuseops +package fuse import ( "bytes" From dce2ceffdbdd96d6cdea874a32f3206673d4c85a Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:22:59 +1000 Subject: [PATCH 10/49] Deleted old kernelResponse methods. --- fuseops/ops.go | 146 ------------------------------------------------- 1 file changed, 146 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index 0eb15b8..0a594e6 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -88,15 +88,6 @@ func (o *LookUpInodeOp) ShortDesc() (desc string) { 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 @@ -123,16 +114,6 @@ func (o *GetInodeAttributesOp) DebugString() string { 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 @@ -157,16 +138,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. // @@ -216,11 +187,6 @@ type ForgetInodeOp struct { N uint64 } -func (o *ForgetInodeOp) kernelResponse() (b buffer.OutMessage) { - // No response. - return -} - //////////////////////////////////////////////////////////////////////// // Inode creation //////////////////////////////////////////////////////////////////////// @@ -259,15 +225,6 @@ func (o *MkDirOp) ShortDesc() (desc string) { 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 @@ -311,19 +268,6 @@ func (o *CreateFileOp) ShortDesc() (desc string) { 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 { @@ -357,15 +301,6 @@ func (o *CreateSymlinkOp) ShortDesc() (desc string) { 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 //////////////////////////////////////////////////////////////////////// @@ -418,11 +353,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. @@ -439,11 +369,6 @@ type RmDirOp struct { 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 @@ -459,11 +384,6 @@ type UnlinkOp struct { Name string } -func (o *UnlinkOp) kernelResponse() (b buffer.OutMessage) { - b = buffer.NewOutMessage(0) - return -} - //////////////////////////////////////////////////////////////////////// // Directory handles //////////////////////////////////////////////////////////////////////// @@ -491,14 +411,6 @@ 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 @@ -589,12 +501,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. @@ -612,11 +518,6 @@ type ReleaseDirHandleOp struct { Handle HandleID } -func (o *ReleaseDirHandleOp) kernelResponse() (b buffer.OutMessage) { - b = buffer.NewOutMessage(0) - return -} - //////////////////////////////////////////////////////////////////////// // File handles //////////////////////////////////////////////////////////////////////// @@ -643,14 +544,6 @@ 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; @@ -680,12 +573,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 @@ -756,14 +643,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 @@ -788,11 +667,6 @@ type SyncFileOp struct { 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. // @@ -848,11 +722,6 @@ type FlushFileOp struct { 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. @@ -870,11 +739,6 @@ type ReleaseFileHandleOp struct { 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 { @@ -888,10 +752,6 @@ func (o *unknownOp) ShortDesc() (desc string) { return } -func (o *unknownOp) kernelResponse() (b buffer.OutMessage) { - panic(fmt.Sprintf("Should never get here for unknown op: %s", o.ShortDesc())) -} - //////////////////////////////////////////////////////////////////////// // Reading symlinks //////////////////////////////////////////////////////////////////////// @@ -907,12 +767,6 @@ type ReadSymlinkOp struct { Target string } -func (o *ReadSymlinkOp) kernelResponse() (b buffer.OutMessage) { - b = buffer.NewOutMessage(uintptr(len(o.Target))) - b.AppendString(o.Target) - return -} - //////////////////////////////////////////////////////////////////////// // Internal //////////////////////////////////////////////////////////////////////// From efeaec5ec70482ed76b4c04fb0ebe4272c5243cd Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:23:09 +1000 Subject: [PATCH 11/49] Deleted internal ops. --- fuseops/ops.go | 62 -------------------------------------------------- 1 file changed, 62 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index 0a594e6..f1c3d27 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -18,9 +18,7 @@ import ( "fmt" "os" "time" - "unsafe" - "github.com/jacobsa/fuse/internal/buffer" "github.com/jacobsa/fuse/internal/fusekernel" "golang.org/x/net/context" ) @@ -766,63 +764,3 @@ type ReadSymlinkOp struct { // Set by the file system: the target of the symlink. Target string } - -//////////////////////////////////////////////////////////////////////// -// 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 -} From ff06adf43120aba8fe86195aaaab6cd609552df1 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:24:45 +1000 Subject: [PATCH 12/49] Trimmed the Op interface. --- fuseops/ops.go | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index f1c3d27..11d9271 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -20,7 +20,6 @@ import ( "time" "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 @@ -32,22 +31,6 @@ type Op interface { // 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{}) } //////////////////////////////////////////////////////////////////////// From eb5fba11cb423d39c6c020c70dd80e0a34fa6d82 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:26:03 +1000 Subject: [PATCH 13/49] Renamed internalOp to opCommon. --- ops.go | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/ops.go b/ops.go index f8f7f36..567bbee 100644 --- a/ops.go +++ b/ops.go @@ -23,7 +23,7 @@ import ( "github.com/jacobsa/fuse/internal/fusekernel" ) -type internalOp struct { +type opCommon struct { } //////////////////////////////////////////////////////////////////////// @@ -31,7 +31,7 @@ type internalOp struct { //////////////////////////////////////////////////////////////////////// type lookUpInodeOp struct { - internalOp + opCommon wrapped fuseops.LookUpInodeOp } @@ -46,7 +46,7 @@ func (o *lookUpInodeOp) kernelResponse( } type getInodeAttributesOp struct { - internalOp + opCommon wrapped fuseops.GetInodeAttributesOp } @@ -61,7 +61,7 @@ func (o *getInodeAttributesOp) kernelResponse() (b buffer.OutMessage) { } type setInodeAttributesOp struct { - internalOp + opCommon wrapped fuseops.SetInodeAttributesOp } @@ -76,7 +76,7 @@ func (o *setInodeAttributesOp) kernelResponse() (b buffer.OutMessage) { } type forgetInodeOp struct { - internalOp + opCommon wrapped fuseops.ForgetInodeOp } @@ -90,7 +90,7 @@ func (o *forgetInodeOp) kernelResponse() (b buffer.OutMessage) { //////////////////////////////////////////////////////////////////////// type mkDirOp struct { - internalOp + opCommon wrapped fuseops.MkDirOp } @@ -104,7 +104,7 @@ func (o *mkDirOp) kernelResponse() (b buffer.OutMessage) { } type createFileOp struct { - internalOp + opCommon wrapped fuseops.CreateFileOp } @@ -122,7 +122,7 @@ func (o *createFileOp) kernelResponse() (b buffer.OutMessage) { } type createSymlinkOp struct { - internalOp + opCommon wrapped fuseops.CreateSymlinkOp } @@ -140,7 +140,7 @@ func (o *createSymlinkOp) kernelResponse() (b buffer.OutMessage) { //////////////////////////////////////////////////////////////////////// type renameOp struct { - internalOp + opCommon wrapped fuseops.RenameOp } @@ -150,7 +150,7 @@ func (o *renameOp) kernelResponse() (b buffer.OutMessage) { } type rmDirOp struct { - internalOp + opCommon wrapped fuseops.RmDirOp } @@ -160,7 +160,7 @@ func (o *rmDirOp) kernelResponse() (b buffer.OutMessage) { } type unlinkOp struct { - internalOp + opCommon wrapped fuseops.UnlinkOp } @@ -174,7 +174,7 @@ func (o *unlinkOp) kernelResponse() (b buffer.OutMessage) { //////////////////////////////////////////////////////////////////////// type openDirOp struct { - internalOp + opCommon wrapped fuseops.OpenDirOp } @@ -187,7 +187,7 @@ func (o *openDirOp) kernelResponse() (b buffer.OutMessage) { } type readDirOp struct { - internalOp + opCommon wrapped fuseops.ReadDirOp } @@ -198,7 +198,7 @@ func (o *readDirOp) kernelResponse() (b buffer.OutMessage) { } type releaseDirHandleOp struct { - internalOp + opCommon wrapped fuseops.ReleaseDirHandleOp } @@ -212,7 +212,7 @@ func (o *releaseDirHandleOp) kernelResponse() (b buffer.OutMessage) { //////////////////////////////////////////////////////////////////////// type openFileOp struct { - internalOp + opCommon wrapped fuseops.OpenFileOp } @@ -225,7 +225,7 @@ func (o *openFileOp) kernelResponse() (b buffer.OutMessage) { } type readFileOp struct { - internalOp + opCommon wrapped fuseops.ReadFileOp } @@ -236,7 +236,7 @@ func (o *readFileOp) kernelResponse() (b buffer.OutMessage) { } type writeFileOp struct { - internalOp + opCommon wrapped fuseops.WriteFileOp } @@ -249,7 +249,7 @@ func (o *writeFileOp) kernelResponse() (b buffer.OutMessage) { } type syncFileOp struct { - internalOp + opCommon wrapped fuseops.SyncFileOp } @@ -259,7 +259,7 @@ func (o *syncFileOp) kernelResponse() (b buffer.OutMessage) { } type flushFileOp struct { - internalOp + opCommon wrapped fuseops.FlushFileOp } @@ -269,7 +269,7 @@ func (o *flushFileOp) kernelResponse() (b buffer.OutMessage) { } type releaseFileHandleOp struct { - internalOp + opCommon wrapped fuseops.ReleaseFileHandleOp } @@ -281,7 +281,7 @@ func (o *releaseFileHandleOp) kernelResponse() (b buffer.OutMessage) { // A sentinel used for unknown ops. The user is expected to respond with a // non-nil error. type unknownOp struct { - internalOp + opCommon opCode uint32 inode fuseops.InodeID } @@ -295,7 +295,7 @@ func (o *unknownOp) kernelResponse() (b buffer.OutMessage) { //////////////////////////////////////////////////////////////////////// type readSymlinkOp struct { - internalOp + opCommon wrapped fuseops.ReadSymlinkOp } @@ -310,7 +310,7 @@ func (o *readSymlinkOp) kernelResponse() (b buffer.OutMessage) { //////////////////////////////////////////////////////////////////////// type internalStatFSOp struct { - internalOp + opCommon } func (o *internalStatFSOp) kernelResponse() (b buffer.OutMessage) { @@ -321,7 +321,7 @@ func (o *internalStatFSOp) kernelResponse() (b buffer.OutMessage) { } type internalInterruptOp struct { - internalOp + opCommon FuseID uint64 } @@ -330,7 +330,7 @@ func (o *internalInterruptOp) kernelResponse() (b buffer.OutMessage) { } type internalInitOp struct { - internalOp + opCommon // In Kernel fusekernel.Protocol From 623ae99fae7fc61a1d5908032c09d18760dcf588 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:27:34 +1000 Subject: [PATCH 14/49] Fixed a build error. --- connection.go | 4 ++-- ops.go | 14 +++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/connection.go b/connection.go index 3ac152c..3a60c70 100644 --- a/connection.go +++ b/connection.go @@ -119,9 +119,9 @@ func (c *Connection) Init() (err error) { return } - initOp, ok := op.(*fuseops.InternalInitOp) + initOp, ok := op.(*internalInitOp) if !ok { - err = fmt.Errorf("Expected *fuseops.InternalInitOp, got %T", op) + err = fmt.Errorf("Expected *internalInitOp, got %T", op) return } diff --git a/ops.go b/ops.go index 567bbee..4f0e3a9 100644 --- a/ops.go +++ b/ops.go @@ -309,10 +309,18 @@ func (o *readSymlinkOp) kernelResponse() (b buffer.OutMessage) { // Internal //////////////////////////////////////////////////////////////////////// -type internalStatFSOp struct { +// Common implementation for our "internal" ops that don't need to be pretty. +type internalOp struct { opCommon } +func (o *internalOp) ShortDesc() string { return "" } +func (o *internalOp) DebugString() string { return "" } + +type internalStatFSOp struct { + internalOp +} + func (o *internalStatFSOp) kernelResponse() (b buffer.OutMessage) { b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{})) b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{})) @@ -321,7 +329,7 @@ func (o *internalStatFSOp) kernelResponse() (b buffer.OutMessage) { } type internalInterruptOp struct { - opCommon + internalOp FuseID uint64 } @@ -330,7 +338,7 @@ func (o *internalInterruptOp) kernelResponse() (b buffer.OutMessage) { } type internalInitOp struct { - opCommon + internalOp // In Kernel fusekernel.Protocol From 585af568492c633ba0818dffd20d131c78b28d70 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:28:44 +1000 Subject: [PATCH 15/49] Fixed a few more build errors. --- connection.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/connection.go b/connection.go index 3a60c70..85fe914 100644 --- a/connection.go +++ b/connection.go @@ -415,11 +415,11 @@ func (c *Connection) ReadOp() (op fuseops.Op, err error) { c.finishOp(m.Header().Opcode, m.Header().Unique) // Debug logging - if c.debugLogger != nil { + if debugLogForOp != nil { if opErr == nil { - op.Logf("-> OK: %s", op.DebugString()) + debugLogForOp(1, "-> OK: %s", op.DebugString()) } else { - op.Logf("-> error: %v", opErr) + debugLogForOp(1, "-> error: %v", opErr) } } From f93dfe355f51ff8115659c2f0a6cf31f898d6a4b Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:30:34 +1000 Subject: [PATCH 16/49] Fixed the signature for Convert. --- conversions.go | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/conversions.go b/conversions.go index 5b2c41e..ceb6976 100644 --- a/conversions.go +++ b/conversions.go @@ -17,36 +17,23 @@ package fuse import ( "bytes" "errors" - "log" "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 a kernel message to an appropriate implementation of fuseops.Op. If +// the op is unknown, a special unexported type will be used. // -// 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, +// The caller is responsible for arranging for the message to be destroyed. +func convertInMessage( m *buffer.InMessage, - protocol fusekernel.Protocol, - debugLogForOp func(int, string, ...interface{}), - errorLogger *log.Logger, - sendReply replyFunc) (o Op, err error) { - var co *commonOp - - var io internalOp + protocol fusekernel.Protocol) (o fuseops.Op, err error) { switch m.Header().Opcode { case fusekernel.OpLookup: buf := m.ConsumeBytes(m.Len()) From b78e8f91d7d86b0a99c958cce5679cd0842bc599 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:32:29 +1000 Subject: [PATCH 17/49] Fixed several build errors. --- conversions.go | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/conversions.go b/conversions.go index ceb6976..1acca28 100644 --- a/conversions.go +++ b/conversions.go @@ -43,7 +43,7 @@ func convertInMessage( return } - to := &LookUpInodeOp{ + to := &fuseops.LookUpInodeOp{ protocol: protocol, Parent: InodeID(m.Header().Nodeid), Name: string(buf[:n-1]), @@ -52,7 +52,7 @@ func convertInMessage( co = &to.commonOp case fusekernel.OpGetattr: - to := &GetInodeAttributesOp{ + to := &fuseops.GetInodeAttributesOp{ protocol: protocol, Inode: InodeID(m.Header().Nodeid), } @@ -67,7 +67,7 @@ func convertInMessage( return } - to := &SetInodeAttributesOp{ + to := &fuseops.SetInodeAttributesOp{ protocol: protocol, Inode: InodeID(m.Header().Nodeid), } @@ -103,7 +103,7 @@ func convertInMessage( return } - to := &ForgetInodeOp{ + to := &fuseops.ForgetInodeOp{ Inode: InodeID(m.Header().Nodeid), N: in.Nlookup, } @@ -125,7 +125,7 @@ func convertInMessage( } name = name[:i] - to := &MkDirOp{ + to := &fuseops.MkDirOp{ protocol: protocol, Parent: InodeID(m.Header().Nodeid), Name: string(name), @@ -157,7 +157,7 @@ func convertInMessage( } name = name[:i] - to := &CreateFileOp{ + to := &fuseops.CreateFileOp{ protocol: protocol, Parent: InodeID(m.Header().Nodeid), Name: string(name), @@ -180,7 +180,7 @@ func convertInMessage( } newName, target := names[0:i], names[i+1:len(names)-1] - to := &CreateSymlinkOp{ + to := &fuseops.CreateSymlinkOp{ protocol: protocol, Parent: InodeID(m.Header().Nodeid), Name: string(newName), @@ -214,7 +214,7 @@ func convertInMessage( } oldName, newName := names[:i], names[i+1:len(names)-1] - to := &RenameOp{ + to := &fuseops.RenameOp{ OldParent: InodeID(m.Header().Nodeid), OldName: string(oldName), NewParent: InodeID(in.Newdir), @@ -231,7 +231,7 @@ func convertInMessage( return } - to := &UnlinkOp{ + to := &fuseops.UnlinkOp{ Parent: InodeID(m.Header().Nodeid), Name: string(buf[:n-1]), } @@ -246,7 +246,7 @@ func convertInMessage( return } - to := &RmDirOp{ + to := &fuseops.RmDirOp{ Parent: InodeID(m.Header().Nodeid), Name: string(buf[:n-1]), } @@ -254,14 +254,14 @@ func convertInMessage( co = &to.commonOp case fusekernel.OpOpen: - to := &OpenFileOp{ + to := &fuseops.OpenFileOp{ Inode: InodeID(m.Header().Nodeid), } io = to co = &to.commonOp case fusekernel.OpOpendir: - to := &OpenDirOp{ + to := &fuseops.OpenDirOp{ Inode: InodeID(m.Header().Nodeid), } io = to @@ -274,7 +274,7 @@ func convertInMessage( return } - to := &ReadFileOp{ + to := &fuseops.ReadFileOp{ Inode: InodeID(m.Header().Nodeid), Handle: HandleID(in.Fh), Offset: int64(in.Offset), @@ -290,7 +290,7 @@ func convertInMessage( return } - to := &ReadDirOp{ + to := &fuseops.ReadDirOp{ Inode: InodeID(m.Header().Nodeid), Handle: HandleID(in.Fh), Offset: DirOffset(in.Offset), @@ -307,7 +307,7 @@ func convertInMessage( return } - to := &ReleaseFileHandleOp{ + to := &fuseops.ReleaseFileHandleOp{ Handle: HandleID(in.Fh), } io = to @@ -321,7 +321,7 @@ func convertInMessage( return } - to := &ReleaseDirHandleOp{ + to := &fuseops.ReleaseDirHandleOp{ Handle: HandleID(in.Fh), } io = to @@ -340,7 +340,7 @@ func convertInMessage( return } - to := &WriteFileOp{ + to := &fuseops.WriteFileOp{ Inode: InodeID(m.Header().Nodeid), Handle: HandleID(in.Fh), Data: buf, @@ -357,7 +357,7 @@ func convertInMessage( return } - to := &SyncFileOp{ + to := &fuseops.SyncFileOp{ Inode: InodeID(m.Header().Nodeid), Handle: HandleID(in.Fh), } @@ -372,7 +372,7 @@ func convertInMessage( return } - to := &FlushFileOp{ + to := &fuseops.FlushFileOp{ Inode: InodeID(m.Header().Nodeid), Handle: HandleID(in.Fh), } @@ -380,14 +380,14 @@ func convertInMessage( co = &to.commonOp case fusekernel.OpReadlink: - to := &ReadSymlinkOp{ + to := &fuseops.ReadSymlinkOp{ Inode: InodeID(m.Header().Nodeid), } io = to co = &to.commonOp case fusekernel.OpStatfs: - to := &InternalStatFSOp{} + to := &fuseops.InternalStatFSOp{} io = to co = &to.commonOp @@ -399,7 +399,7 @@ func convertInMessage( return } - to := &InternalInterruptOp{ + to := &fuseops.InternalInterruptOp{ FuseID: in.Unique, } io = to @@ -413,7 +413,7 @@ func convertInMessage( return } - to := &InternalInitOp{ + to := &fuseops.InternalInitOp{ Kernel: fusekernel.Protocol{in.Major, in.Minor}, MaxReadahead: in.MaxReadahead, Flags: fusekernel.InitFlags(in.Flags), From 2b8c659517a9f8aaafc465bb017caa86b4efb69d Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:33:19 +1000 Subject: [PATCH 18/49] Fixed several more build errors. --- conversions.go | 60 +++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/conversions.go b/conversions.go index 1acca28..5a8ce97 100644 --- a/conversions.go +++ b/conversions.go @@ -44,17 +44,15 @@ func convertInMessage( } to := &fuseops.LookUpInodeOp{ - protocol: protocol, - Parent: InodeID(m.Header().Nodeid), - Name: string(buf[:n-1]), + Parent: fuseops.InodeID(m.Header().Nodeid), + Name: string(buf[:n-1]), } io = to co = &to.commonOp case fusekernel.OpGetattr: to := &fuseops.GetInodeAttributesOp{ - protocol: protocol, - Inode: InodeID(m.Header().Nodeid), + Inode: fuseops.InodeID(m.Header().Nodeid), } io = to co = &to.commonOp @@ -68,8 +66,7 @@ func convertInMessage( } to := &fuseops.SetInodeAttributesOp{ - protocol: protocol, - Inode: InodeID(m.Header().Nodeid), + Inode: fuseops.InodeID(m.Header().Nodeid), } valid := fusekernel.SetattrValid(in.Valid) @@ -104,7 +101,7 @@ func convertInMessage( } to := &fuseops.ForgetInodeOp{ - Inode: InodeID(m.Header().Nodeid), + Inode: fuseops.InodeID(m.Header().Nodeid), N: in.Nlookup, } io = to @@ -126,9 +123,8 @@ func convertInMessage( name = name[:i] to := &fuseops.MkDirOp{ - protocol: protocol, - Parent: InodeID(m.Header().Nodeid), - Name: string(name), + 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 @@ -158,10 +154,9 @@ func convertInMessage( name = name[:i] to := &fuseops.CreateFileOp{ - protocol: protocol, - Parent: InodeID(m.Header().Nodeid), - Name: string(name), - Mode: convertFileMode(in.Mode), + Parent: fuseops.InodeID(m.Header().Nodeid), + Name: string(name), + Mode: convertFileMode(in.Mode), } io = to co = &to.commonOp @@ -181,10 +176,9 @@ func convertInMessage( newName, target := names[0:i], names[i+1:len(names)-1] to := &fuseops.CreateSymlinkOp{ - protocol: protocol, - Parent: InodeID(m.Header().Nodeid), - Name: string(newName), - Target: string(target), + Parent: fuseops.InodeID(m.Header().Nodeid), + Name: string(newName), + Target: string(target), } io = to co = &to.commonOp @@ -215,9 +209,9 @@ func convertInMessage( oldName, newName := names[:i], names[i+1:len(names)-1] to := &fuseops.RenameOp{ - OldParent: InodeID(m.Header().Nodeid), + OldParent: fuseops.InodeID(m.Header().Nodeid), OldName: string(oldName), - NewParent: InodeID(in.Newdir), + NewParent: fuseops.InodeID(in.Newdir), NewName: string(newName), } io = to @@ -232,7 +226,7 @@ func convertInMessage( } to := &fuseops.UnlinkOp{ - Parent: InodeID(m.Header().Nodeid), + Parent: fuseops.InodeID(m.Header().Nodeid), Name: string(buf[:n-1]), } io = to @@ -247,7 +241,7 @@ func convertInMessage( } to := &fuseops.RmDirOp{ - Parent: InodeID(m.Header().Nodeid), + Parent: fuseops.InodeID(m.Header().Nodeid), Name: string(buf[:n-1]), } io = to @@ -255,14 +249,14 @@ func convertInMessage( case fusekernel.OpOpen: to := &fuseops.OpenFileOp{ - Inode: InodeID(m.Header().Nodeid), + Inode: fuseops.InodeID(m.Header().Nodeid), } io = to co = &to.commonOp case fusekernel.OpOpendir: to := &fuseops.OpenDirOp{ - Inode: InodeID(m.Header().Nodeid), + Inode: fuseops.InodeID(m.Header().Nodeid), } io = to co = &to.commonOp @@ -275,7 +269,7 @@ func convertInMessage( } to := &fuseops.ReadFileOp{ - Inode: InodeID(m.Header().Nodeid), + Inode: fuseops.InodeID(m.Header().Nodeid), Handle: HandleID(in.Fh), Offset: int64(in.Offset), Size: int(in.Size), @@ -291,7 +285,7 @@ func convertInMessage( } to := &fuseops.ReadDirOp{ - Inode: InodeID(m.Header().Nodeid), + Inode: fuseops.InodeID(m.Header().Nodeid), Handle: HandleID(in.Fh), Offset: DirOffset(in.Offset), Size: int(in.Size), @@ -341,7 +335,7 @@ func convertInMessage( } to := &fuseops.WriteFileOp{ - Inode: InodeID(m.Header().Nodeid), + Inode: fuseops.InodeID(m.Header().Nodeid), Handle: HandleID(in.Fh), Data: buf, Offset: int64(in.Offset), @@ -358,7 +352,7 @@ func convertInMessage( } to := &fuseops.SyncFileOp{ - Inode: InodeID(m.Header().Nodeid), + Inode: fuseops.InodeID(m.Header().Nodeid), Handle: HandleID(in.Fh), } io = to @@ -373,7 +367,7 @@ func convertInMessage( } to := &fuseops.FlushFileOp{ - Inode: InodeID(m.Header().Nodeid), + Inode: fuseops.InodeID(m.Header().Nodeid), Handle: HandleID(in.Fh), } io = to @@ -381,7 +375,7 @@ func convertInMessage( case fusekernel.OpReadlink: to := &fuseops.ReadSymlinkOp{ - Inode: InodeID(m.Header().Nodeid), + Inode: fuseops.InodeID(m.Header().Nodeid), } io = to co = &to.commonOp @@ -424,7 +418,7 @@ func convertInMessage( default: to := &unknownOp{ opCode: m.Header().Opcode, - inode: InodeID(m.Header().Nodeid), + inode: fuseops.InodeID(m.Header().Nodeid), } io = to co = &to.commonOp @@ -450,7 +444,7 @@ func convertTime(t time.Time) (secs uint64, nsec uint32) { } func convertAttributes( - inodeID InodeID, + inodeID fuseops.InodeID, in *InodeAttributes, out *fusekernel.Attr) { out.Ino = uint64(inodeID) From 7035b69b93f0b7ee1557d20a31e13c5299b90357 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:35:26 +1000 Subject: [PATCH 19/49] Fixed a bunch more errors. --- conversions.go | 122 +++++++++++++------------------------------------ 1 file changed, 32 insertions(+), 90 deletions(-) diff --git a/conversions.go b/conversions.go index 5a8ce97..5ea99f4 100644 --- a/conversions.go +++ b/conversions.go @@ -43,19 +43,15 @@ func convertInMessage( return } - to := &fuseops.LookUpInodeOp{ + o = &fuseops.LookUpInodeOp{ Parent: fuseops.InodeID(m.Header().Nodeid), Name: string(buf[:n-1]), } - io = to - co = &to.commonOp case fusekernel.OpGetattr: - to := &fuseops.GetInodeAttributesOp{ + o = &fuseops.GetInodeAttributesOp{ Inode: fuseops.InodeID(m.Header().Nodeid), } - io = to - co = &to.commonOp case fusekernel.OpSetattr: type input fusekernel.SetattrIn @@ -68,6 +64,7 @@ func convertInMessage( to := &fuseops.SetInodeAttributesOp{ Inode: fuseops.InodeID(m.Header().Nodeid), } + o = to valid := fusekernel.SetattrValid(in.Valid) if valid&fusekernel.SetattrSize != 0 { @@ -89,9 +86,6 @@ func convertInMessage( to.Mtime = &t } - io = to - co = &to.commonOp - case fusekernel.OpForget: type input fusekernel.ForgetIn in := (*input)(m.Consume(unsafe.Sizeof(input{}))) @@ -100,12 +94,10 @@ func convertInMessage( return } - to := &fuseops.ForgetInodeOp{ + 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))) @@ -122,7 +114,7 @@ func convertInMessage( } name = name[:i] - to := &fuseops.MkDirOp{ + o = &fuseops.MkDirOp{ Parent: fuseops.InodeID(m.Header().Nodeid), Name: string(name), @@ -135,9 +127,6 @@ func convertInMessage( 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 { @@ -153,13 +142,11 @@ func convertInMessage( } name = name[:i] - to := &fuseops.CreateFileOp{ + 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". @@ -175,13 +162,11 @@ func convertInMessage( } newName, target := names[0:i], names[i+1:len(names)-1] - to := &fuseops.CreateSymlinkOp{ + 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 @@ -208,14 +193,12 @@ func convertInMessage( } oldName, newName := names[:i], names[i+1:len(names)-1] - to := &fuseops.RenameOp{ + o = &fuseops.RenameOp{ OldParent: fuseops.InodeID(m.Header().Nodeid), OldName: string(oldName), NewParent: fuseops.InodeID(in.Newdir), NewName: string(newName), } - io = to - co = &to.commonOp case fusekernel.OpUnlink: buf := m.ConsumeBytes(m.Len()) @@ -225,12 +208,10 @@ func convertInMessage( return } - to := &fuseops.UnlinkOp{ + 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()) @@ -240,26 +221,20 @@ func convertInMessage( return } - to := &fuseops.RmDirOp{ + o = &fuseops.RmDirOp{ Parent: fuseops.InodeID(m.Header().Nodeid), Name: string(buf[:n-1]), } - io = to - co = &to.commonOp case fusekernel.OpOpen: - to := &fuseops.OpenFileOp{ + o = &fuseops.OpenFileOp{ Inode: fuseops.InodeID(m.Header().Nodeid), } - io = to - co = &to.commonOp case fusekernel.OpOpendir: - to := &fuseops.OpenDirOp{ + 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))) @@ -268,14 +243,12 @@ func convertInMessage( return } - to := &fuseops.ReadFileOp{ + o = &fuseops.ReadFileOp{ Inode: fuseops.InodeID(m.Header().Nodeid), - Handle: HandleID(in.Fh), + 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))) @@ -284,14 +257,12 @@ func convertInMessage( return } - to := &fuseops.ReadDirOp{ + o = &fuseops.ReadDirOp{ Inode: fuseops.InodeID(m.Header().Nodeid), - Handle: HandleID(in.Fh), - Offset: DirOffset(in.Offset), + 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 @@ -301,11 +272,9 @@ func convertInMessage( return } - to := &fuseops.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 @@ -315,11 +284,9 @@ func convertInMessage( return } - to := &fuseops.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))) @@ -334,14 +301,12 @@ func convertInMessage( return } - to := &fuseops.WriteFileOp{ + o = &fuseops.WriteFileOp{ Inode: fuseops.InodeID(m.Header().Nodeid), - Handle: HandleID(in.Fh), + Handle: fuseops.HandleID(in.Fh), Data: buf, Offset: int64(in.Offset), } - io = to - co = &to.commonOp case fusekernel.OpFsync: type input fusekernel.FsyncIn @@ -351,12 +316,10 @@ func convertInMessage( return } - to := &fuseops.SyncFileOp{ + o = &fuseops.SyncFileOp{ Inode: fuseops.InodeID(m.Header().Nodeid), - Handle: HandleID(in.Fh), + Handle: fuseops.HandleID(in.Fh), } - io = to - co = &to.commonOp case fusekernel.OpFlush: type input fusekernel.FlushIn @@ -366,24 +329,18 @@ func convertInMessage( return } - to := &fuseops.FlushFileOp{ + o = &fuseops.FlushFileOp{ Inode: fuseops.InodeID(m.Header().Nodeid), - Handle: HandleID(in.Fh), + Handle: fuseops.HandleID(in.Fh), } - io = to - co = &to.commonOp case fusekernel.OpReadlink: - to := &fuseops.ReadSymlinkOp{ + o = &fuseops.ReadSymlinkOp{ Inode: fuseops.InodeID(m.Header().Nodeid), } - io = to - co = &to.commonOp case fusekernel.OpStatfs: - to := &fuseops.InternalStatFSOp{} - io = to - co = &to.commonOp + o = &internalStatFSOp{} case fusekernel.OpInterrupt: type input fusekernel.InterruptIn @@ -393,11 +350,9 @@ func convertInMessage( return } - to := &fuseops.InternalInterruptOp{ + o = &internalInterruptOp{ FuseID: in.Unique, } - io = to - co = &to.commonOp case fusekernel.OpInit: type input fusekernel.InitIn @@ -407,32 +362,19 @@ func convertInMessage( return } - to := &fuseops.InternalInitOp{ + o = &internalInitOp{ 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: fuseops.InodeID(m.Header().Nodeid), } - io = to - co = &to.commonOp } - co.init( - opCtx, - io, - m.Header().Unique, - sendReply, - debugLogForOp, - errorLogger) - - o = io return } From 125c36dba8f1357905f1f8d84982837da37d983f Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:37:59 +1000 Subject: [PATCH 20/49] Fixed up unknownOp. --- ops.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ops.go b/ops.go index 4f0e3a9..578e59c 100644 --- a/ops.go +++ b/ops.go @@ -286,8 +286,18 @@ type unknownOp struct { inode fuseops.InodeID } +func (o *unknownOp) ShortDesc() string { + return fmt.Sprintf("(inode=%d)", o.opCode, o.inode) +} + +func (o *unknownOp) DebugString() string { + return o.ShortDesc() +} + func (o *unknownOp) kernelResponse() (b buffer.OutMessage) { - panic(fmt.Sprintf("Should never get here for unknown op: %s", o.ShortDesc())) + panic(fmt.Sprintf( + "Expected an error response for unknown op: %s", + o.ShortDesc())) } //////////////////////////////////////////////////////////////////////// From 35d5c4c3fe3673001790d51d7a7944b62080411a Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:38:18 +1000 Subject: [PATCH 21/49] Fixed conversions.go. --- conversions.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conversions.go b/conversions.go index 5ea99f4..a011c91 100644 --- a/conversions.go +++ b/conversions.go @@ -387,7 +387,7 @@ func convertTime(t time.Time) (secs uint64, nsec uint32) { func convertAttributes( inodeID fuseops.InodeID, - in *InodeAttributes, + in *fuseops.InodeAttributes, out *fusekernel.Attr) { out.Ino = uint64(inodeID) out.Size = in.Size @@ -438,7 +438,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) From a7bcfed288b880cb5bd467914ad027111e2a3d1c Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:45:59 +1000 Subject: [PATCH 22/49] Refactored kernelResponse methods into a single big function. --- ops.go | 462 +++++++++++++++------------------------------------------ 1 file changed, 118 insertions(+), 344 deletions(-) diff --git a/ops.go b/ops.go index 578e59c..38a9d9e 100644 --- a/ops.go +++ b/ops.go @@ -15,7 +15,7 @@ package fuse import ( - "fmt" + "syscall" "unsafe" "github.com/jacobsa/fuse/fuseops" @@ -23,352 +23,126 @@ import ( "github.com/jacobsa/fuse/internal/fusekernel" ) -type opCommon struct { -} +// Given an op, return the response that should be sent to the kernel. If the +// op requires no response, return a nil response. If the op is unknown, return +// an error that should be sent to the kernel. +func kernelResponse( + untyped fuseops.Op, + protocol fusekernel.Protocol) (b buffer.OutMessage, err error) { + switch o := untyped.(type) { + case *fuseops.LookUpInodeOp: + size := fusekernel.EntryOutSize(protocol) + b = buffer.NewOutMessage(size) + out := (*fusekernel.EntryOut)(b.Grow(size)) + convertChildInodeEntry(&o.wrapped.Entry, out) -//////////////////////////////////////////////////////////////////////// -// Inodes -//////////////////////////////////////////////////////////////////////// + case *fuseops.GetInodeAttributesOp: + 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) -type lookUpInodeOp struct { - opCommon - wrapped fuseops.LookUpInodeOp -} + case *fuseops.SetInodeAttributesOp: + 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) -func (o *lookUpInodeOp) kernelResponse( - protocol fusekernel.Protocol) (b buffer.OutMessage) { - size := fusekernel.EntryOutSize(protocol) - b = buffer.NewOutMessage(size) - out := (*fusekernel.EntryOut)(b.Grow(size)) - convertChildInodeEntry(&o.wrapped.Entry, out) - - return -} - -type getInodeAttributesOp struct { - opCommon - wrapped fuseops.GetInodeAttributesOp -} - -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 -} - -type setInodeAttributesOp struct { - opCommon - wrapped fuseops.SetInodeAttributesOp -} - -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 -} - -type forgetInodeOp struct { - opCommon - wrapped fuseops.ForgetInodeOp -} - -func (o *forgetInodeOp) kernelResponse() (b buffer.OutMessage) { - // No response. - return -} - -//////////////////////////////////////////////////////////////////////// -// Inode creation -//////////////////////////////////////////////////////////////////////// - -type mkDirOp struct { - opCommon - wrapped fuseops.MkDirOp -} - -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 -} - -type createFileOp struct { - opCommon - wrapped fuseops.CreateFileOp -} - -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 -} - -type createSymlinkOp struct { - opCommon - wrapped fuseops.CreateSymlinkOp -} - -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 -//////////////////////////////////////////////////////////////////////// - -type renameOp struct { - opCommon - wrapped fuseops.RenameOp -} - -func (o *renameOp) kernelResponse() (b buffer.OutMessage) { - b = buffer.NewOutMessage(0) - return -} - -type rmDirOp struct { - opCommon - wrapped fuseops.RmDirOp -} - -func (o *rmDirOp) kernelResponse() (b buffer.OutMessage) { - b = buffer.NewOutMessage(0) - return -} - -type unlinkOp struct { - opCommon - wrapped fuseops.UnlinkOp -} - -func (o *unlinkOp) kernelResponse() (b buffer.OutMessage) { - b = buffer.NewOutMessage(0) - return -} - -//////////////////////////////////////////////////////////////////////// -// Directory handles -//////////////////////////////////////////////////////////////////////// - -type openDirOp struct { - opCommon - wrapped fuseops.OpenDirOp -} - -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 -} - -type readDirOp struct { - opCommon - wrapped fuseops.ReadDirOp -} - -func (o *readDirOp) kernelResponse() (b buffer.OutMessage) { - b = buffer.NewOutMessage(uintptr(len(o.Data))) - b.Append(o.Data) - return -} - -type releaseDirHandleOp struct { - opCommon - wrapped fuseops.ReleaseDirHandleOp -} - -func (o *releaseDirHandleOp) kernelResponse() (b buffer.OutMessage) { - b = buffer.NewOutMessage(0) - return -} - -//////////////////////////////////////////////////////////////////////// -// File handles -//////////////////////////////////////////////////////////////////////// - -type openFileOp struct { - opCommon - wrapped fuseops.OpenFileOp -} - -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 -} - -type readFileOp struct { - opCommon - wrapped fuseops.ReadFileOp -} - -func (o *readFileOp) kernelResponse() (b buffer.OutMessage) { - b = buffer.NewOutMessage(uintptr(len(o.Data))) - b.Append(o.Data) - return -} - -type writeFileOp struct { - opCommon - wrapped fuseops.WriteFileOp -} - -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 -} - -type syncFileOp struct { - opCommon - wrapped fuseops.SyncFileOp -} - -func (o *syncFileOp) kernelResponse() (b buffer.OutMessage) { - b = buffer.NewOutMessage(0) - return -} - -type flushFileOp struct { - opCommon - wrapped fuseops.FlushFileOp -} - -func (o *flushFileOp) kernelResponse() (b buffer.OutMessage) { - b = buffer.NewOutMessage(0) - return -} - -type releaseFileHandleOp struct { - opCommon - wrapped fuseops.ReleaseFileHandleOp -} - -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 { - opCommon - opCode uint32 - inode fuseops.InodeID -} - -func (o *unknownOp) ShortDesc() string { - return fmt.Sprintf("(inode=%d)", o.opCode, o.inode) -} - -func (o *unknownOp) DebugString() string { - return o.ShortDesc() -} - -func (o *unknownOp) kernelResponse() (b buffer.OutMessage) { - panic(fmt.Sprintf( - "Expected an error response for unknown op: %s", - o.ShortDesc())) -} - -//////////////////////////////////////////////////////////////////////// -// Reading symlinks -//////////////////////////////////////////////////////////////////////// - -type readSymlinkOp struct { - opCommon - wrapped fuseops.ReadSymlinkOp -} - -func (o *readSymlinkOp) kernelResponse() (b buffer.OutMessage) { - b = buffer.NewOutMessage(uintptr(len(o.Target))) - b.AppendString(o.Target) - return -} - -//////////////////////////////////////////////////////////////////////// -// Internal -//////////////////////////////////////////////////////////////////////// - -// Common implementation for our "internal" ops that don't need to be pretty. -type internalOp struct { - opCommon -} - -func (o *internalOp) ShortDesc() string { return "" } -func (o *internalOp) DebugString() string { return "" } - -type internalStatFSOp struct { - internalOp -} - -func (o *internalStatFSOp) kernelResponse() (b buffer.OutMessage) { - b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{})) - b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{})) - - return -} - -type internalInterruptOp struct { - internalOp - FuseID uint64 -} - -func (o *internalInterruptOp) kernelResponse() (b buffer.OutMessage) { - panic("Shouldn't get here.") -} - -type internalInitOp struct { - internalOp - - // 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 + case *fuseops.ForgetInodeOp: + // No response. + + case *fuseops.MkDirOp: + size := fusekernel.EntryOutSize(o.protocol) + b = buffer.NewOutMessage(size) + out := (*fusekernel.EntryOut)(b.Grow(size)) + convertChildInodeEntry(&o.Entry, out) + + case *fuseops.CreateFileOp: + 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) + + case *fuseops.CreateSymlinkOp: + size := fusekernel.EntryOutSize(o.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 *internalStatFSOp: + b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{})) + b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{})) + + case *internalInterruptOp: + // No response. + + case *internalInitOp: + 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: + err = syscall.ENOSYS + } return } From 2f927892199450e95da149dfcef0f8a039088394 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:46:41 +1000 Subject: [PATCH 23/49] Fixed some build errors. --- ops.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ops.go b/ops.go index 38a9d9e..48cc6fb 100644 --- a/ops.go +++ b/ops.go @@ -34,17 +34,17 @@ func kernelResponse( size := fusekernel.EntryOutSize(protocol) b = buffer.NewOutMessage(size) out := (*fusekernel.EntryOut)(b.Grow(size)) - convertChildInodeEntry(&o.wrapped.Entry, out) + convertChildInodeEntry(&o.Entry, out) case *fuseops.GetInodeAttributesOp: - size := fusekernel.AttrOutSize(o.protocol) + 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(o.protocol) + size := fusekernel.AttrOutSize(protocol) b = buffer.NewOutMessage(size) out := (*fusekernel.AttrOut)(b.Grow(size)) out.AttrValid, out.AttrValidNsec = convertExpirationTime(o.AttributesExpiration) @@ -54,13 +54,13 @@ func kernelResponse( // No response. case *fuseops.MkDirOp: - size := fusekernel.EntryOutSize(o.protocol) + size := fusekernel.EntryOutSize(protocol) b = buffer.NewOutMessage(size) out := (*fusekernel.EntryOut)(b.Grow(size)) convertChildInodeEntry(&o.Entry, out) case *fuseops.CreateFileOp: - eSize := fusekernel.EntryOutSize(o.protocol) + eSize := fusekernel.EntryOutSize(protocol) b = buffer.NewOutMessage(eSize + unsafe.Sizeof(fusekernel.OpenOut{})) e := (*fusekernel.EntryOut)(b.Grow(eSize)) @@ -70,7 +70,7 @@ func kernelResponse( oo.Fh = uint64(o.Handle) case *fuseops.CreateSymlinkOp: - size := fusekernel.EntryOutSize(o.protocol) + size := fusekernel.EntryOutSize(protocol) b = buffer.NewOutMessage(size) out := (*fusekernel.EntryOut)(b.Grow(size)) convertChildInodeEntry(&o.Entry, out) From 8305caad559ab9709cbfb11da659d633d46b05ee Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:47:29 +1000 Subject: [PATCH 24/49] Restored the internal ops. --- ops.go | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/ops.go b/ops.go index 48cc6fb..50e7be7 100644 --- a/ops.go +++ b/ops.go @@ -146,3 +146,60 @@ func kernelResponse( return } + +//////////////////////////////////////////////////////////////////////// +// Internal +//////////////////////////////////////////////////////////////////////// + +// Common implementation for our "internal" ops that don't need to be pretty. +type internalOp struct { +} + +func (o *internalOp) ShortDesc() string { return "" } +func (o *internalOp) DebugString() string { return "" } + +type internalStatFSOp struct { + internalOp +} + +func (o *internalStatFSOp) kernelResponse() (b buffer.OutMessage) { + b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{})) + b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{})) + + return +} + +type internalInterruptOp struct { + internalOp + FuseID uint64 +} + +func (o *internalInterruptOp) kernelResponse() (b buffer.OutMessage) { + panic("Shouldn't get here.") +} + +type internalInitOp struct { + internalOp + + // 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 +} From 8c5f6d86850bf052a00bbd74542bbfc601b93fda Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 14:52:35 +1000 Subject: [PATCH 25/49] Updated the ReadOp signature. --- connection.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/connection.go b/connection.go index 85fe914..951adc6 100644 --- a/connection.go +++ b/connection.go @@ -82,6 +82,13 @@ 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 { + op Op + inMsg *buffer.InMessage +} + // Create a connection wrapping the supplied file descriptor connected to the // kernel. You must eventually call c.close(). // @@ -372,14 +379,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 fuseops.Op, err error) { // Keep going until we find a request we know how to convert. for { // Read the next message from the kernel. From fd0e095b3b093ccd627f965c50c125e73d89370b Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:08:41 +1000 Subject: [PATCH 26/49] Reimplemented ReadOp. --- connection.go | 101 +++++++++++++++----------------------------------- 1 file changed, 29 insertions(+), 72 deletions(-) diff --git a/connection.go b/connection.go index 951adc6..3ef9c42 100644 --- a/connection.go +++ b/connection.go @@ -31,6 +31,10 @@ import ( "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: @@ -85,8 +89,9 @@ type Connection struct { // 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 { - op Op inMsg *buffer.InMessage + op Op + opID uint64 // For logging } // Create a connection wrapping the supplied file descriptor connected to the @@ -400,86 +405,38 @@ func (c *Connection) ReadOp() (ctx context.Context, 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 debugLogForOp != nil { - if opErr == nil { - debugLogForOp(1, "-> OK: %s", op.DebugString()) - } else { - debugLogForOp(1, "-> 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. + // Special case: handle interrupt requests inline. if interruptOp, ok := op.(*fuseops.InternalInterruptOp); 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, opID, op}) + + // 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.(*internalStatFSOp); ok { + c.Reply(ctx, nil) + continue + } + + // Return the op to the user. return } } From d6d4cb87c51540ac400e41d1ecf7db15f27574c4 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:10:00 +1000 Subject: [PATCH 27/49] Fixed Connection.Init. --- connection.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/connection.go b/connection.go index 3ef9c42..964bf38 100644 --- a/connection.go +++ b/connection.go @@ -90,7 +90,7 @@ type Connection struct { // context that the user uses to reply to the op. type opState struct { inMsg *buffer.InMessage - op Op + op fuseops.Op opID uint64 // For logging } @@ -125,7 +125,7 @@ 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 @@ -133,6 +133,7 @@ func (c *Connection) Init() (err error) { initOp, ok := op.(*internalInitOp) if !ok { + c.Reply(ctx, syscall.EPROTO) err = fmt.Errorf("Expected *internalInitOp, got %T", op) return } @@ -144,7 +145,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 } @@ -164,8 +165,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 } From dd1fcfedf542de708c39b1774632f83ba93802bc Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:12:43 +1000 Subject: [PATCH 28/49] Fixed more build errors. --- connection.go | 16 ++++++++++++---- ops.go | 17 +++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/connection.go b/connection.go index 964bf38..ee7dee3 100644 --- a/connection.go +++ b/connection.go @@ -91,7 +91,7 @@ type Connection struct { type opState struct { inMsg *buffer.InMessage op fuseops.Op - opID uint64 // For logging + opID uint32 // For logging } // Create a connection wrapping the supplied file descriptor connected to the @@ -407,7 +407,7 @@ func (c *Connection) ReadOp() (ctx context.Context, op fuseops.Op, err error) { } // Convert the message to an op. - op, err := convertInMessage(m, c.protocol) + op, err = convertInMessage(m, c.protocol) if err != nil { err = fmt.Errorf("convertInMessage: %v", err) return @@ -420,14 +420,14 @@ func (c *Connection) ReadOp() (ctx context.Context, op fuseops.Op, err error) { c.debugLog(opID, 1, "<- %#v", op) // Special case: handle interrupt requests inline. - if interruptOp, ok := op.(*fuseops.InternalInterruptOp); ok { + if interruptOp, ok := op.(*internalInterruptOp); 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, opID, op}) + 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 @@ -442,6 +442,14 @@ func (c *Connection) ReadOp() (ctx context.Context, op fuseops.Op, err error) { } } +// 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, err error) { + panic("TODO") +} + // 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/ops.go b/ops.go index 50e7be7..9fdfff2 100644 --- a/ops.go +++ b/ops.go @@ -15,6 +15,7 @@ package fuse import ( + "fmt" "syscall" "unsafe" @@ -151,6 +152,22 @@ func kernelResponse( // Internal //////////////////////////////////////////////////////////////////////// +// 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 +} + +func (o *unknownOp) ShortDesc() (desc string) { + desc = fmt.Sprintf("(inode=%v)", o.opCode, o.inode) + return +} + +func (o *unknownOp) DebugString() string { + return o.ShortDesc() +} + // Common implementation for our "internal" ops that don't need to be pretty. type internalOp struct { } From ea210ae2bbffde9fc63a9cb4788a6e4d92b0090a Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:15:07 +1000 Subject: [PATCH 29/49] Fixed package fuseutil. --- fuseutil/file_system.go | 93 +++++++++++++------------ fuseutil/not_implemented_file_system.go | 21 ++++++ 2 files changed, 70 insertions(+), 44 deletions(-) diff --git a/fuseutil/file_system.go b/fuseutil/file_system.go index 7aa1b45..f02ac31 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" ) @@ -33,26 +35,26 @@ import ( // 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 fuseops.Op) { 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 From dfb1b2d88186e64657e34f8feaa5408abc75f61f Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:15:53 +1000 Subject: [PATCH 30/49] Fixed package memfs. --- samples/memfs/memfs.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) 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() From 448c910c1300532ed688935624decb1d7d8bb0d8 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:21:07 +1000 Subject: [PATCH 31/49] Write some of Connection.Reply. --- connection.go | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/connection.go b/connection.go index ee7dee3..ba6056d 100644 --- a/connection.go +++ b/connection.go @@ -446,8 +446,45 @@ func (c *Connection) ReadOp() (ctx context.Context, op fuseops.Op, err error) { // if successful). The context must be the context returned by ReadOp. // // LOCKS_EXCLUDED(c.mu) -func (c *Connection) Reply(ctx context.Context, err error) { - panic("TODO") +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. + err = c.writeMessage(replyMsg) + if err != nil { + err = fmt.Errorf("writeMessage: %v", err) + return + } + + return } // Close the connection. Must not be called until operations that were read From 612735d5900f08b5fea39c798a20bec18f07bf55 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:32:17 +1000 Subject: [PATCH 32/49] Retooled kernelResponse. --- ops.go | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/ops.go b/ops.go index 9fdfff2..e6fcf75 100644 --- a/ops.go +++ b/ops.go @@ -24,13 +24,45 @@ import ( "github.com/jacobsa/fuse/internal/fusekernel" ) -// Given an op, return the response that should be sent to the kernel. If the -// op requires no response, return a nil response. If the op is unknown, return -// an error that should be sent to the kernel. +// Return the response that should be sent to the kernel. If the op requires no +// response, return a nil response. func kernelResponse( - untyped fuseops.Op, - protocol fusekernel.Protocol) (b buffer.OutMessage, err error) { - switch o := untyped.(type) { + fuseID uint64, + op fuseops.Op, + 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. + 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. +func kernelResponseForOp( + op fuseops.Op, + 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) @@ -142,7 +174,7 @@ func kernelResponse( out.MaxWrite = o.MaxWrite default: - err = syscall.ENOSYS + panic(fmt.Sprintf("Unknown op: %#v", op)) } return From 9b9a86cf80f24208e04cebf451596cd7f4776661 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:33:33 +1000 Subject: [PATCH 33/49] Fixed Connection.Reply. --- connection.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/connection.go b/connection.go index ba6056d..e888516 100644 --- a/connection.go +++ b/connection.go @@ -478,13 +478,11 @@ func (c *Connection) Reply(ctx context.Context, opErr error) { } // Send the reply to the kernel. - err = c.writeMessage(replyMsg) - if err != nil { - err = fmt.Errorf("writeMessage: %v", err) + replyMsg := kernelResponse(m.Header().Unique, op, opErr, c.protocol) + if err := c.writeMessage(replyMsg); err != nil { + log.Fatalf("writeMessage: %v", err) return } - - return } // Close the connection. Must not be called until operations that were read From 20a01b67ef9abc7d981875217db9f09bc5b402c2 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:34:14 +1000 Subject: [PATCH 34/49] Fixed more build errors. --- ops.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ops.go b/ops.go index e6fcf75..9bf884a 100644 --- a/ops.go +++ b/ops.go @@ -34,7 +34,7 @@ func kernelResponse( // 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 + var b buffer.OutMessage if opErr != nil { b = buffer.NewOutMessage(0) if errno, ok := opErr.(syscall.Errno); ok { @@ -60,7 +60,7 @@ func kernelResponse( // op. func kernelResponseForOp( op fuseops.Op, - protocol fusekernel.Protocol) (b *buffer.OutMessage) { + protocol fusekernel.Protocol) (b buffer.OutMessage) { // Create the appropriate output message switch o := op.(type) { case *fuseops.LookUpInodeOp: From ef3d11e2b1a25bfdfa1057d83da20e8007bc1637 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:40:11 +1000 Subject: [PATCH 35/49] It seems the write returns errors as a matter of course when unmounting. --- connection.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connection.go b/connection.go index e888516..24647f0 100644 --- a/connection.go +++ b/connection.go @@ -480,7 +480,10 @@ func (c *Connection) Reply(ctx context.Context, opErr error) { // Send the reply to the kernel. replyMsg := kernelResponse(m.Header().Unique, op, opErr, c.protocol) if err := c.writeMessage(replyMsg); err != nil { - log.Fatalf("writeMessage: %v", err) + if c.errorLogger != nil { + c.errorLogger.Printf("writeMessage: %v", err) + } + return } } From dfadf1e8df7ec85167c21353b8bc670f612310e5 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:40:36 +1000 Subject: [PATCH 36/49] Deleted Op. --- fuseops/ops.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index 11d9271..b487ac8 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -22,17 +22,6 @@ import ( "github.com/jacobsa/fuse/internal/fusekernel" ) -// A common interface implemented by all ops in this package. Use a type switch -// to find particular concrete types, responding with fuse.ENOSYS if a type is -// not supported. -type Op interface { - // A short description of the op, to be used in logging. - ShortDesc() string - - // A long description of the op, to be used in debug logging. - DebugString() string -} - //////////////////////////////////////////////////////////////////////// // Inodes //////////////////////////////////////////////////////////////////////// From b1b941d4506b63c5abdd728158491a953ba845a4 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:40:46 +1000 Subject: [PATCH 37/49] Deleted commonOp. --- fuseops/common_op.go | 174 ------------------------------------------- 1 file changed, 174 deletions(-) delete mode 100644 fuseops/common_op.go 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) - } -} From 56b1f10c47ed9d1f1342b9f5d7536dad47d7c052 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:41:46 +1000 Subject: [PATCH 38/49] Killed unexported fields. --- fuseops/ops.go | 61 -------------------------------------------------- 1 file changed, 61 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index b487ac8..5115a34 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -18,8 +18,6 @@ import ( "fmt" "os" "time" - - "github.com/jacobsa/fuse/internal/fusekernel" ) //////////////////////////////////////////////////////////////////////// @@ -29,9 +27,6 @@ import ( // 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 @@ -63,9 +58,6 @@ func (o *LookUpInodeOp) ShortDesc() (desc string) { // 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 @@ -89,9 +81,6 @@ func (o *GetInodeAttributesOp) DebugString() string { // 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 @@ -148,8 +137,6 @@ type SetInodeAttributesOp struct { // Rather they should take fuse.Connection.ReadOp returning io.EOF as // implicitly decrementing all lookup counts to zero. type ForgetInodeOp struct { - commonOp - // The inode whose reference count should be decremented. Inode InodeID @@ -173,9 +160,6 @@ type ForgetInodeOp struct { // // Therefore the file system should return EEXIST if the name already exists. type MkDirOp struct { - commonOp - protocol fusekernel.Protocol - // The ID of parent directory inode within which to create the child. Parent InodeID @@ -206,9 +190,6 @@ func (o *MkDirOp) ShortDesc() (desc string) { // // Therefore the file system should return EEXIST if the name already exists. type CreateFileOp struct { - commonOp - protocol fusekernel.Protocol - // The ID of parent directory inode within which to create the child file. Parent InodeID @@ -241,9 +222,6 @@ func (o *CreateFileOp) ShortDesc() (desc string) { // 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 @@ -310,8 +288,6 @@ func (o *CreateSymlinkOp) ShortDesc() (desc string) { // about this. // type RenameOp struct { - commonOp - // The old parent directory, and the name of the entry within it to be // relocated. OldParent InodeID @@ -331,8 +307,6 @@ type RenameOp struct { // // Sample implementation in ext2: ext2_rmdir (http://goo.gl/B9QmFf) type RmDirOp struct { - commonOp - // The ID of parent directory inode, and the name of the directory being // removed within it. Parent InodeID @@ -346,8 +320,6 @@ type RmDirOp struct { // // Sample implementation in ext2: ext2_unlink (http://goo.gl/hY6r6C) type UnlinkOp struct { - commonOp - // The ID of parent directory inode, and the name of the entry being removed // within it. Parent InodeID @@ -365,8 +337,6 @@ type UnlinkOp struct { // user-space process. On OS X it may not be sent for every open(2) (cf. // https://github.com/osxfuse/osxfuse/issues/199). type OpenDirOp struct { - commonOp - // The ID of the inode to be opened. Inode InodeID @@ -383,8 +353,6 @@ type OpenDirOp struct { // Read entries from a directory previously opened with OpenDir. type ReadDirOp struct { - commonOp - // The directory inode that we are reading, and the handle previously // returned by OpenDir when opening that inode. Inode InodeID @@ -480,8 +448,6 @@ type ReadDirOp struct { // // Errors from this op are ignored by the kernel (cf. http://goo.gl/RL38Do). type ReleaseDirHandleOp struct { - commonOp - // The handle ID to be released. The kernel guarantees that this ID will not // be used in further calls to the file system (unless it is reissued by the // file system). @@ -499,8 +465,6 @@ type ReleaseDirHandleOp struct { // process. On OS X it may not be sent for every open(2) // (cf.https://github.com/osxfuse/osxfuse/issues/199). type OpenFileOp struct { - commonOp - // The ID of the inode to be opened. Inode InodeID @@ -520,8 +484,6 @@ type OpenFileOp struct { // some reads may be served by the page cache. See notes on WriteFileOp for // more. type ReadFileOp struct { - commonOp - // The file inode that we are reading, and the handle previously returned by // CreateFile or OpenFile when opening that inode. Inode InodeID @@ -575,8 +537,6 @@ type ReadFileOp struct { // (See also http://goo.gl/ocdTdM, fuse-devel thread "Fuse guarantees on // concurrent requests".) type WriteFileOp struct { - commonOp - // The file inode that we are modifying, and the handle previously returned // by CreateFile or OpenFile when opening that inode. Inode InodeID @@ -630,8 +590,6 @@ type WriteFileOp struct { // See also: FlushFileOp, which may perform a similar function when closing a // file (but which is not used in "real" file systems). type SyncFileOp struct { - commonOp - // The file and handle being sync'd. Inode InodeID Handle HandleID @@ -685,8 +643,6 @@ type SyncFileOp struct { // to at least schedule a real flush, and maybe do it immediately in order to // return any errors that occur. type FlushFileOp struct { - commonOp - // The file and handle being flushed. Inode InodeID Handle HandleID @@ -701,35 +657,18 @@ type FlushFileOp struct { // // Errors from this op are ignored by the kernel (cf. http://goo.gl/RL38Do). type ReleaseFileHandleOp struct { - commonOp - // The handle ID to be released. The kernel guarantees that this ID will not // be used in further calls to the file system (unless it is reissued by the // file system). Handle HandleID } -// A sentinel used for unknown ops. The user is expected to respond with a -// non-nil error. -type unknownOp struct { - commonOp - opCode uint32 - inode InodeID -} - -func (o *unknownOp) ShortDesc() (desc string) { - desc = fmt.Sprintf("(inode=%v)", o.opCode, o.inode) - return -} - //////////////////////////////////////////////////////////////////////// // Reading symlinks //////////////////////////////////////////////////////////////////////// // Read the target of a symlink inode. type ReadSymlinkOp struct { - commonOp - // The symlink inode that we are reading. Inode InodeID From e59dbd14f4a8b2edcbaf43d04dd9d69f908bbcc5 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:42:02 +1000 Subject: [PATCH 39/49] Killed methods. --- fuseops/ops.go | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index 5115a34..d73c42c 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -15,7 +15,6 @@ package fuseops import ( - "fmt" "os" "time" ) @@ -48,11 +47,6 @@ type LookUpInodeOp struct { Entry ChildInodeEntry } -func (o *LookUpInodeOp) ShortDesc() (desc string) { - desc = fmt.Sprintf("LookUpInode(parent=%v, name=%q)", o.Parent, o.Name) - return -} - // Refresh the attributes for an inode whose ID was previously returned in a // LookUpInodeOp. The kernel sends this when the FUSE VFS layer's cache of // inode attributes is stale. This is controlled by the AttributesExpiration @@ -68,14 +62,6 @@ type GetInodeAttributesOp struct { AttributesExpiration time.Time } -func (o *GetInodeAttributesOp) DebugString() string { - return fmt.Sprintf( - "Inode: %d, Exp: %v, Attr: %s", - o.Inode, - o.AttributesExpiration, - o.Attributes.DebugString()) -} - // Change attributes for an inode. // // The kernel sends this for obvious cases like chmod(2), and for less obvious @@ -174,11 +160,6 @@ type MkDirOp struct { Entry ChildInodeEntry } -func (o *MkDirOp) ShortDesc() (desc string) { - desc = fmt.Sprintf("MkDir(parent=%v, name=%q)", o.Parent, o.Name) - return -} - // Create a file inode and open it. // // The kernel sends this when the user asks to open a file with the O_CREAT @@ -214,11 +195,6 @@ type CreateFileOp struct { Handle HandleID } -func (o *CreateFileOp) ShortDesc() (desc string) { - desc = fmt.Sprintf("CreateFile(parent=%v, name=%q)", o.Parent, o.Name) - return -} - // Create a symlink inode. If the name already exists, the file system should // return EEXIST (cf. the notes on CreateFileOp and MkDirOp). type CreateSymlinkOp struct { @@ -239,16 +215,6 @@ type CreateSymlinkOp struct { Entry ChildInodeEntry } -func (o *CreateSymlinkOp) ShortDesc() (desc string) { - desc = fmt.Sprintf( - "CreateSymlink(parent=%v, name=%q, target=%q)", - o.Parent, - o.Name, - o.Target) - - return -} - //////////////////////////////////////////////////////////////////////// // Unlinking //////////////////////////////////////////////////////////////////////// From 3755e3c1a1f54589f88f093c66a2b452244c1801 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:43:41 +1000 Subject: [PATCH 40/49] Fixed package fuse. --- connection.go | 5 ++--- conversions.go | 6 +++--- ops.go | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/connection.go b/connection.go index 24647f0..2fe72ed 100644 --- a/connection.go +++ b/connection.go @@ -26,7 +26,6 @@ import ( "golang.org/x/net/context" - "github.com/jacobsa/fuse/fuseops" "github.com/jacobsa/fuse/internal/buffer" "github.com/jacobsa/fuse/internal/fusekernel" ) @@ -90,7 +89,7 @@ type Connection struct { // context that the user uses to reply to the op. type opState struct { inMsg *buffer.InMessage - op fuseops.Op + op interface{} opID uint32 // For logging } @@ -396,7 +395,7 @@ func (c *Connection) writeMessage(msg []byte) (err error) { // /dev/fuse. It must not be called multiple times concurrently. // // LOCKS_EXCLUDED(c.mu) -func (c *Connection) ReadOp() (ctx context.Context, op fuseops.Op, err error) { +func (c *Connection) ReadOp() (ctx context.Context, op interface{}, err error) { // Keep going until we find a request we know how to convert. for { // Read the next message from the kernel. diff --git a/conversions.go b/conversions.go index a011c91..e454466 100644 --- a/conversions.go +++ b/conversions.go @@ -27,13 +27,13 @@ import ( "github.com/jacobsa/fuse/internal/fusekernel" ) -// Convert a kernel message to an appropriate implementation of fuseops.Op. If -// the op is unknown, a special unexported type will be used. +// Convert a kernel message to an appropriate op. If the op is unknown, a +// special unexported type will be used. // // The caller is responsible for arranging for the message to be destroyed. func convertInMessage( m *buffer.InMessage, - protocol fusekernel.Protocol) (o fuseops.Op, err error) { + protocol fusekernel.Protocol) (o interface{}, err error) { switch m.Header().Opcode { case fusekernel.OpLookup: buf := m.ConsumeBytes(m.Len()) diff --git a/ops.go b/ops.go index 9bf884a..158337b 100644 --- a/ops.go +++ b/ops.go @@ -28,7 +28,7 @@ import ( // response, return a nil response. func kernelResponse( fuseID uint64, - op fuseops.Op, + op interface{}, opErr error, protocol fusekernel.Protocol) (msg []byte) { // If the user replied with an error, create room enough just for the result @@ -59,7 +59,7 @@ func kernelResponse( // Like kernelResponse, but assumes the user replied with a nil error to the // op. func kernelResponseForOp( - op fuseops.Op, + op interface{}, protocol fusekernel.Protocol) (b buffer.OutMessage) { // Create the appropriate output message switch o := op.(type) { From 522995296e7a9397065562b8231574ba6c4f3eed Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:44:17 +1000 Subject: [PATCH 41/49] Fixed package fuse. --- fuseutil/file_system.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fuseutil/file_system.go b/fuseutil/file_system.go index f02ac31..3ebcdf8 100644 --- a/fuseutil/file_system.go +++ b/fuseutil/file_system.go @@ -29,8 +29,8 @@ import ( // loop" that switches on op types, instead receiving typed method calls // directly. // -// The FileSystem implementation should not call Op.Respond, instead returning -// the error with which the caller should respond. +// The FileSystem implementation should not call Connection.Reply, instead +// returning the error with which the caller should respond. // // See NotImplementedFileSystem for a convenient way to embed default // implementations for methods you don't care about. @@ -110,7 +110,7 @@ func (s *fileSystemServer) ServeOps(c *fuse.Connection) { func (s *fileSystemServer) handleOp( c *fuse.Connection, ctx context.Context, - op fuseops.Op) { + op interface{}) { defer s.opsInFlight.Done() // Dispatch to the appropriate method. From a99d69abcecd356d2903066ea65246f835adcfd9 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:44:54 +1000 Subject: [PATCH 42/49] Fixed a couple of out of date references. --- errors.go | 2 +- fuseops/doc.go | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) 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/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 From 82289ebdd47616eb5f394b3c20cf1f7e157063fc Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:47:45 +1000 Subject: [PATCH 43/49] Fixed sample build errors. --- samples/cachingfs/caching_fs.go | 6 ++++++ samples/flushfs/flush_fs.go | 11 +++++++++++ samples/forgetfs/forget_fs.go | 9 +++++++++ samples/hellofs/hello_fs.go | 11 ++++++++++- samples/interruptfs/interrupt_fs.go | 10 ++++++++-- 5 files changed, 44 insertions(+), 3 deletions(-) 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 } From c9971434119be0b0f1bfb4befdf3dcc8ccb1d286 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:49:38 +1000 Subject: [PATCH 44/49] Fixed a bug. --- ops.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ops.go b/ops.go index 158337b..3ef2614 100644 --- a/ops.go +++ b/ops.go @@ -48,16 +48,18 @@ func kernelResponse( msg = b.Bytes() - // Fill in the rest of the header. - h := b.OutHeader() - h.Unique = fuseID - h.Len = uint32(len(msg)) + // 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. +// op. Returns a nil response if no response is required. func kernelResponseForOp( op interface{}, protocol fusekernel.Protocol) (b buffer.OutMessage) { From 9441e60ecec05fc61ed6e9de7c096adeef800508 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:50:05 +1000 Subject: [PATCH 45/49] Don't write zero-length replies. --- connection.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/connection.go b/connection.go index 2fe72ed..f0632df 100644 --- a/connection.go +++ b/connection.go @@ -478,12 +478,15 @@ func (c *Connection) Reply(ctx context.Context, opErr error) { // Send the reply to the kernel. replyMsg := kernelResponse(m.Header().Unique, op, opErr, c.protocol) - if err := c.writeMessage(replyMsg); err != nil { - if c.errorLogger != nil { - c.errorLogger.Printf("writeMessage: %v", err) + if replyMsg != nil { + if err := c.writeMessage(replyMsg); err != nil { + if c.errorLogger != nil { + c.errorLogger.Printf("writeMessage: %v", err) + } + + return } - return } } From 407b005d68933eeb5d6cb321301a7de340607238 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:51:03 +1000 Subject: [PATCH 46/49] Deleted some dead code. --- ops.go | 44 -------------------------------------------- 1 file changed, 44 deletions(-) diff --git a/ops.go b/ops.go index 3ef2614..acdadc3 100644 --- a/ops.go +++ b/ops.go @@ -193,45 +193,14 @@ type unknownOp struct { inode fuseops.InodeID } -func (o *unknownOp) ShortDesc() (desc string) { - desc = fmt.Sprintf("(inode=%v)", o.opCode, o.inode) - return -} - -func (o *unknownOp) DebugString() string { - return o.ShortDesc() -} - -// Common implementation for our "internal" ops that don't need to be pretty. -type internalOp struct { -} - -func (o *internalOp) ShortDesc() string { return "" } -func (o *internalOp) DebugString() string { return "" } - type internalStatFSOp struct { - internalOp -} - -func (o *internalStatFSOp) kernelResponse() (b buffer.OutMessage) { - b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{})) - b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{})) - - return } type internalInterruptOp struct { - internalOp FuseID uint64 } -func (o *internalInterruptOp) kernelResponse() (b buffer.OutMessage) { - panic("Shouldn't get here.") -} - type internalInitOp struct { - internalOp - // In Kernel fusekernel.Protocol @@ -241,16 +210,3 @@ type internalInitOp struct { 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 -} From 95a6177c0cfda90f3c6c937b6a06925570a27870 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:52:13 +1000 Subject: [PATCH 47/49] Fixed redundant internal op names. --- connection.go | 8 ++++---- conversions.go | 6 +++--- ops.go | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/connection.go b/connection.go index f0632df..bd15d7a 100644 --- a/connection.go +++ b/connection.go @@ -130,10 +130,10 @@ func (c *Connection) Init() (err error) { return } - initOp, ok := op.(*internalInitOp) + initOp, ok := op.(*initOp) if !ok { c.Reply(ctx, syscall.EPROTO) - err = fmt.Errorf("Expected *internalInitOp, got %T", op) + err = fmt.Errorf("Expected *initOp, got %T", op) return } @@ -419,7 +419,7 @@ func (c *Connection) ReadOp() (ctx context.Context, op interface{}, err error) { c.debugLog(opID, 1, "<- %#v", op) // Special case: handle interrupt requests inline. - if interruptOp, ok := op.(*internalInterruptOp); ok { + if interruptOp, ok := op.(*interruptOp); ok { c.handleInterrupt(interruptOp.FuseID) continue } @@ -431,7 +431,7 @@ func (c *Connection) ReadOp() (ctx context.Context, op interface{}, err error) { // 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.(*internalStatFSOp); ok { + if _, ok := op.(*statFSOp); ok { c.Reply(ctx, nil) continue } diff --git a/conversions.go b/conversions.go index e454466..436c07d 100644 --- a/conversions.go +++ b/conversions.go @@ -340,7 +340,7 @@ func convertInMessage( } case fusekernel.OpStatfs: - o = &internalStatFSOp{} + o = &statFSOp{} case fusekernel.OpInterrupt: type input fusekernel.InterruptIn @@ -350,7 +350,7 @@ func convertInMessage( return } - o = &internalInterruptOp{ + o = &interruptOp{ FuseID: in.Unique, } @@ -362,7 +362,7 @@ func convertInMessage( return } - o = &internalInitOp{ + o = &initOp{ Kernel: fusekernel.Protocol{in.Major, in.Minor}, MaxReadahead: in.MaxReadahead, Flags: fusekernel.InitFlags(in.Flags), diff --git a/ops.go b/ops.go index acdadc3..1cbee9c 100644 --- a/ops.go +++ b/ops.go @@ -158,14 +158,14 @@ func kernelResponseForOp( b = buffer.NewOutMessage(uintptr(len(o.Target))) b.AppendString(o.Target) - case *internalStatFSOp: + case *statFSOp: b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.StatfsOut{})) b.Grow(unsafe.Sizeof(fusekernel.StatfsOut{})) - case *internalInterruptOp: + case *interruptOp: // No response. - case *internalInitOp: + case *initOp: b = buffer.NewOutMessage(unsafe.Sizeof(fusekernel.InitOut{})) out := (*fusekernel.InitOut)(b.Grow(unsafe.Sizeof(fusekernel.InitOut{}))) @@ -193,14 +193,14 @@ type unknownOp struct { inode fuseops.InodeID } -type internalStatFSOp struct { +type statFSOp struct { } -type internalInterruptOp struct { +type interruptOp struct { FuseID uint64 } -type internalInitOp struct { +type initOp struct { // In Kernel fusekernel.Protocol From 4c33dd3f406a04f66d7a323b3ebe529344d15728 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:53:20 +1000 Subject: [PATCH 48/49] Fixed some long lines. --- ops.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ops.go b/ops.go index 1cbee9c..af04b2a 100644 --- a/ops.go +++ b/ops.go @@ -75,14 +75,16 @@ func kernelResponseForOp( size := fusekernel.AttrOutSize(protocol) b = buffer.NewOutMessage(size) out := (*fusekernel.AttrOut)(b.Grow(size)) - out.AttrValid, out.AttrValidNsec = convertExpirationTime(o.AttributesExpiration) + 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) + out.AttrValid, out.AttrValidNsec = convertExpirationTime( + o.AttributesExpiration) convertAttributes(o.Inode, &o.Attributes, &out.Attr) case *fuseops.ForgetInodeOp: From 5c332f767bc198c369aa1ca691efd100dd0c3aef Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 27 Jul 2015 15:55:14 +1000 Subject: [PATCH 49/49] Moved the kernel response functions to a more appropriate place. --- conversions.go | 173 +++++++++++++++++++++++++++++++++++++++++++++++++ ops.go | 172 +----------------------------------------------- 2 files changed, 176 insertions(+), 169 deletions(-) diff --git a/conversions.go b/conversions.go index 436c07d..ee3b97d 100644 --- a/conversions.go +++ b/conversions.go @@ -17,6 +17,7 @@ package fuse import ( "bytes" "errors" + "fmt" "os" "syscall" "time" @@ -27,6 +28,10 @@ import ( "github.com/jacobsa/fuse/internal/fusekernel" ) +//////////////////////////////////////////////////////////////////////// +// Incoming messages +//////////////////////////////////////////////////////////////////////// + // Convert a kernel message to an appropriate op. If the op is unknown, a // special unexported type will be used. // @@ -378,6 +383,174 @@ func convertInMessage( 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) diff --git a/ops.go b/ops.go index af04b2a..13ede33 100644 --- a/ops.go +++ b/ops.go @@ -15,179 +15,10 @@ package fuse import ( - "fmt" - "syscall" - "unsafe" - "github.com/jacobsa/fuse/fuseops" - "github.com/jacobsa/fuse/internal/buffer" "github.com/jacobsa/fuse/internal/fusekernel" ) -// 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 -} - -//////////////////////////////////////////////////////////////////////// -// Internal -//////////////////////////////////////////////////////////////////////// - // A sentinel used for unknown ops. The user is expected to respond with a // non-nil error. type unknownOp struct { @@ -195,13 +26,16 @@ type unknownOp struct { 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