From 84559e623405e47ac126fb738b0102fc99e220c3 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 24 Mar 2015 13:57:43 +1100 Subject: [PATCH 01/13] Copied and pasted from file_system.go. --- fuseops/ops.go | 888 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 888 insertions(+) create mode 100644 fuseops/ops.go diff --git a/fuseops/ops.go b/fuseops/ops.go new file mode 100644 index 0000000..52f8998 --- /dev/null +++ b/fuseops/ops.go @@ -0,0 +1,888 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fuse + +import ( + "fmt" + "os" + "time" + + "github.com/jacobsa/bazilfuse" + + "golang.org/x/net/context" +) + +// An interface that must be implemented by file systems to be mounted with +// FUSE. See also the comments on request and response structs. +// +// Not all methods need to have interesting implementations. Embed a field of +// type fuseutil.NotImplementedFileSystem to inherit defaults that return +// ENOSYS to the kernel. +// +// Must be safe for concurrent access via all methods. +type FileSystem interface { + // This method is called once when mounting the file system. It must succeed + // in order for the mount to succeed. + Init( + ctx context.Context, + req *InitRequest) (*InitResponse, error) + + /////////////////////////////////// + // Inodes + /////////////////////////////////// + + // Look up a child by name within a parent directory. The kernel calls this + // when resolving user paths to dentry structs, which are then cached. + LookUpInode( + ctx context.Context, + req *LookUpInodeRequest) (*LookUpInodeResponse, error) + + // Refresh the attributes for an inode whose ID was previously returned by + // LookUpInode. The kernel calls this when the FUSE VFS layer's cache of + // inode attributes is stale. This is controlled by the AttributesExpiration + // field of responses to LookUp, etc. + GetInodeAttributes( + ctx context.Context, + req *GetInodeAttributesRequest) (*GetInodeAttributesResponse, error) + + // Change attributes for an inode. + // + // The kernel calls this for obvious cases like chmod(2), and for less + // obvious cases like ftrunctate(2). + SetInodeAttributes( + ctx context.Context, + req *SetInodeAttributesRequest) (*SetInodeAttributesResponse, error) + + // Forget an inode ID previously issued (e.g. by LookUpInode or MkDir). The + // kernel calls this when removing an inode from its internal caches. + ForgetInode( + ctx context.Context, + req *ForgetInodeRequest) (*ForgetInodeResponse, error) + + /////////////////////////////////// + // 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 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). But volatile file systems and paranoid non-volatile + // file systems should check for the reasons described below on CreateFile. + MkDir( + ctx context.Context, + req *MkDirRequest) (*MkDirResponse, error) + + // Create a file inode and open it. + // + // The kernel calls this method 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 it's impossible to tell for sure that all kernels make this check + // in all cases and the official fuse documentation is less than encouraging + // (" the file does not exist, first create it with the specified mode, and + // then open it"). Therefore file systems would be smart to be paranoid and + // check themselves, returning EEXIST when the file already exists. This of + // course particularly applies to file systems that are volatile from the + // kernel's point of view. + CreateFile( + ctx context.Context, + req *CreateFileRequest) (*CreateFileResponse, error) + + /////////////////////////////////// + // Inode destruction + /////////////////////////////////// + + // 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 calls ForgetInode. + // + // The file system is responsible for checking that the directory is empty. + // + // Sample implementation in ext2: ext2_rmdir (http://goo.gl/B9QmFf) + RmDir( + ctx context.Context, + req *RmDirRequest) (*RmDirResponse, error) + + // Unlink a file from its parent. If this brings the inode's link count to + // zero, the inode should be deleted once the kernel calls ForgetInode. 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) + Unlink( + ctx context.Context, + req *UnlinkRequest) (*UnlinkResponse, error) + + /////////////////////////////////// + // Directory handles + /////////////////////////////////// + + // Open a directory inode. + // + // On Linux the kernel calls this method 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 called for every + // open(2) (cf. https://github.com/osxfuse/osxfuse/issues/199). + OpenDir( + ctx context.Context, + req *OpenDirRequest) (*OpenDirResponse, error) + + // Read entries from a directory previously opened with OpenDir. + ReadDir( + ctx context.Context, + req *ReadDirRequest) (*ReadDirResponse, error) + + // Release a previously-minted directory handle. The kernel calls 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 calls + // to the file system (unless it is reissued by the file system). + ReleaseDirHandle( + ctx context.Context, + req *ReleaseDirHandleRequest) (*ReleaseDirHandleResponse, error) + + /////////////////////////////////// + // File handles + /////////////////////////////////// + + // Open a file inode. + // + // On Linux the kernel calls this method 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 called for every open(2) + // (cf.https://github.com/osxfuse/osxfuse/issues/199). + OpenFile( + ctx context.Context, + req *OpenFileRequest) (*OpenFileResponse, error) + + // Read data from a file previously opened with CreateFile or OpenFile. + // + // Note that this method is not called for every call to read(2) by the end + // user; some reads may be served by the page cache. See notes on Write for + // more. + ReadFile( + ctx context.Context, + req *ReadFileRequest) (*ReadFileResponse, error) + + // 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 method to be called: + // + // * 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 writes *will* be received before a call to Flush 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. + // + WriteFile( + ctx context.Context, + req *WriteFileRequest) (*WriteFileResponse, error) + + // 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 called by fdatasync(2) (cf. http://goo.gl/01R7rF), + // and may be called for msync(2) with the MS_SYNC flag (see the notes on + // FlushFile). + // + // See also: FlushFile, which may perform a similar purpose when closing a + // file (but which is not used in "real" file systems). + SyncFile( + ctx context.Context, + req *SyncFileRequest) (*SyncFileResponse, error) + + // Flush the current state of an open file to storage upon closing a file + // descriptor. + // + // vfs.txt documents this as being called 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 called 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 called 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 WriteFile calls for the + // modifications may not be received before the FlushFile request 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 WriteFile 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 + // SyncFile (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 by calling msync(2) + // with MS_SYNC, followed by munmap(2), followed by close(2). On Linux, the + // msync(2) appears to be optional because close(2) implies dirty page + // writeback (cf. http://goo.gl/HyzLTT). + // + // Because of cases like dup2(2), calls to FlushFile are not necessarily one + // to one with calls to OpenFile. They should not be used for reference + // counting, and the handle must remain valid even after the method is called + // (use ReleaseFileHandle to dispose 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. + FlushFile( + ctx context.Context, + req *FlushFileRequest) (*FlushFileResponse, error) + + // 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). + ReleaseFileHandle( + ctx context.Context, + req *ReleaseFileHandleRequest) (*ReleaseFileHandleResponse, error) +} + +//////////////////////////////////////////////////////////////////////// +// Simple types +//////////////////////////////////////////////////////////////////////// + +// A 64-bit number used to uniquely identify a file or directory in the file +// system. File systems may mint inode IDs with any value except for +// RootInodeID. +// +// This corresponds to struct inode::i_no in the VFS layer. +// (Cf. http://goo.gl/tvYyQt) +type InodeID uint64 + +// A distinguished inode ID that identifies the root of the file system, e.g. +// in a request to OpenDir or LookUpInode. Unlike all other inode IDs, which +// are minted by the file system, the FUSE VFS layer may send a request for +// this ID without the file system ever having referenced it in a previous +// response. +const RootInodeID = 1 + +func init() { + // Make sure the constant above is correct. We do this at runtime rather than + // defining the constant in terms of bazilfuse.RootID for two reasons: + // + // 1. Users can more clearly see that the root ID is low and can therefore + // be used as e.g. an array index, with space reserved up to the root. + // + // 2. The constant can be untyped and can therefore more easily be used as + // an array index. + // + if RootInodeID != bazilfuse.RootID { + panic( + fmt.Sprintf( + "Oops, RootInodeID is wrong: %v vs. %v", + RootInodeID, + bazilfuse.RootID)) + } +} + +// Attributes for a file or directory inode. Corresponds to struct inode (cf. +// http://goo.gl/tvYyQt). +type InodeAttributes struct { + Size uint64 + + // The number of incoming hard links to this inode. + Nlink uint64 + + // The mode of the inode. This is exposed to the user in e.g. the result of + // fstat(2). + // + // Note that in contrast to the defaults for FUSE, this package mounts file + // systems in a manner such that the kernel checks inode permissions in the + // standard posix way. This is implemented by setting the default_permissions + // mount option (cf. http://goo.gl/1LxOop and http://goo.gl/1pTjuk). + // + // For example, in the case of mkdir: + // + // * (http://goo.gl/JkdxDI) sys_mkdirat calls inode_permission. + // + // * (...) inode_permission eventually calls do_inode_permission. + // + // * (http://goo.gl/aGCsmZ) calls i_op->permission, which is + // fuse_permission (cf. http://goo.gl/VZ9beH). + // + // * (http://goo.gl/5kqUKO) fuse_permission doesn't do anything at all for + // several code paths if FUSE_DEFAULT_PERMISSIONS is unset. In contrast, + // if that flag *is* set, then it calls generic_permission. + // + Mode os.FileMode + + // Time information. See `man 2 stat` for full details. + Atime time.Time // Time of last access + Mtime time.Time // Time of last modification + Ctime time.Time // Time of last modification to inode + Crtime time.Time // Time of creation (OS X only) + + // Ownership information + Uid uint32 + Gid uint32 +} + +// A generation number for an inode. Irrelevant for file systems that won't be +// exported over NFS. For those that will and that reuse inode IDs when they +// become free, the generation number must change when an ID is reused. +// +// This corresponds to struct inode::i_generation in the VFS layer. +// (Cf. http://goo.gl/tvYyQt) +// +// Some related reading: +// +// http://fuse.sourceforge.net/doxygen/structfuse__entry__param.html +// http://stackoverflow.com/q/11071996/1505451 +// http://goo.gl/CqvwyX +// http://julipedia.meroh.net/2005/09/nfs-file-handles.html +// http://goo.gl/wvo3MB +// +type GenerationNumber uint64 + +// An opaque 64-bit number used to identify a particular open handle to a file +// or directory. +// +// This corresponds to fuse_file_info::fh. +type HandleID uint64 + +// An offset into an open directory handle. This is opaque to FUSE, and can be +// used for whatever purpose the file system desires. See notes on +// ReadDirRequest.Offset for details. +type DirOffset uint64 + +// A header that is included with every request. +type RequestHeader struct { + // Credentials information for the process making the request. + Uid uint32 + Gid uint32 +} + +// Information about a child inode within its parent directory. Shared by the +// responses for LookUpInode, MkDir, CreateFile, etc. Consumed by the kernel in +// order to set up a dcache entry. +type ChildInodeEntry struct { + // The ID of the child inode. The file system must ensure that the returned + // inode ID remains valid until a later call to ForgetInode. + Child InodeID + + // A generation number for this incarnation of the inode with the given ID. + // See comments on type GenerationNumber for more. + Generation GenerationNumber + + // Current attributes for the child inode. + // + // When creating a new inode, the file system is responsible for initializing + // and recording (where supported) attributes like time information, + // ownership information, etc. + // + // Ownership information in particular must be set to something reasonable or + // by default root will own everything and unprivileged users won't be able + // to do anything useful. In traditional file systems in the kernel, the + // function inode_init_owner (http://goo.gl/5qavg8) contains the + // standards-compliant logic for this. + Attributes InodeAttributes + + // The FUSE VFS layer in the kernel maintains a cache of file attributes, + // used whenever up to date information about size, mode, etc. is needed. + // + // For example, this is the abridged call chain for fstat(2): + // + // * (http://goo.gl/tKBH1p) fstat calls vfs_fstat. + // * (http://goo.gl/3HeITq) vfs_fstat eventuall calls vfs_getattr_nosec. + // * (http://goo.gl/DccFQr) vfs_getattr_nosec calls i_op->getattr. + // * (http://goo.gl/dpKkst) fuse_getattr calls fuse_update_attributes. + // * (http://goo.gl/yNlqPw) fuse_update_attributes uses the values in the + // struct inode if allowed, otherwise calling out to the user-space code. + // + // In addition to obvious cases like fstat, this is also used in more subtle + // cases like updating size information before seeking (http://goo.gl/2nnMFa) + // or reading (http://goo.gl/FQSWs8). + // + // Most 'real' file systems do not set inode_operations::getattr, and + // therefore vfs_getattr_nosec calls generic_fillattr which simply grabs the + // information from the inode struct. This makes sense because these file + // systems cannot spontaneously change; all modifications go through the + // kernel which can update the inode struct as appropriate. + // + // In contrast, a FUSE file system may have spontaneous changes, so it calls + // out to user space to fetch attributes. However this is expensive, so the + // FUSE layer in the kernel caches the attributes if requested. + // + // This field controls when the attributes returned in this response and + // stashed in the struct inode should be re-queried. Leave at the zero value + // to disable caching. + // + // More reading: + // http://stackoverflow.com/q/21540315/1505451 + AttributesExpiration time.Time + + // The time until which the kernel may maintain an entry for this name to + // inode mapping in its dentry cache. After this time, it will revalidate the + // dentry. + // + // As in the discussion of attribute caching above, unlike real file systems, + // FUSE file systems may spontaneously change their name -> inode mapping. + // Therefore the FUSE VFS layer uses dentry_operations::d_revalidate + // (http://goo.gl/dVea0h) to intercept lookups and revalidate by calling the + // user-space LookUpInode method. However the latter may be slow, so it + // caches the entries until the time defined by this field. + // + // Example code walk: + // + // * (http://goo.gl/M2G3tO) lookup_dcache calls d_revalidate if enabled. + // * (http://goo.gl/ef0Elu) fuse_dentry_revalidate just uses the dentry's + // inode if fuse_dentry_time(entry) hasn't passed. Otherwise it sends a + // lookup request. + // + // Leave at the zero value to disable caching. + // + // Beware: this value is ignored on OS X, where entry caching is disabled by + // default. See notes on MountConfig.EnableVnodeCaching for more. + EntryExpiration time.Time +} + +//////////////////////////////////////////////////////////////////////// +// Requests and responses +//////////////////////////////////////////////////////////////////////// + +type InitRequest struct { + Header RequestHeader +} + +type InitResponse struct { +} + +type LookUpInodeRequest struct { + Header RequestHeader + + // 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 +} + +type LookUpInodeResponse struct { + Entry ChildInodeEntry +} + +type GetInodeAttributesRequest struct { + Header RequestHeader + + // The inode of interest. + Inode InodeID +} + +type GetInodeAttributesResponse struct { + // Attributes for the inode, and the time at which they should expire. See + // notes on ChildInodeEntry.AttributesExpiration for more. + Attributes InodeAttributes + AttributesExpiration time.Time +} + +type SetInodeAttributesRequest struct { + Header RequestHeader + + // 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 +} + +type SetInodeAttributesResponse struct { + // 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 +} + +type ForgetInodeRequest struct { + Header RequestHeader + + // The inode to be forgotten. The kernel guarantees that the node ID will not + // be used in further calls to the file system (unless it is reissued by the + // file system). + ID InodeID +} + +type ForgetInodeResponse struct { +} + +type MkDirRequest struct { + Header RequestHeader + + // 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 +} + +type MkDirResponse struct { + // Information about the inode that was created. + Entry ChildInodeEntry +} + +type CreateFileRequest struct { + Header RequestHeader + + // 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 + + // Flags for the open operation. + Flags bazilfuse.OpenFlags +} + +type CreateFileResponse struct { + // Information about the inode that was created. + Entry ChildInodeEntry + + // 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 calls to methods like ReadFile that + // accept a file handle. The file system must ensure this ID remains valid + // until a later call to ReleaseFileHandle. + Handle HandleID +} + +type RmDirRequest struct { + Header RequestHeader + + // The ID of parent directory inode, and the name of the directory being + // removed within it. + Parent InodeID + Name string +} + +type UnlinkResponse struct { +} + +type UnlinkRequest struct { + Header RequestHeader + + // The ID of parent directory inode, and the name of the file being removed + // within it. + Parent InodeID + Name string +} + +type RmDirResponse struct { +} + +type OpenDirRequest struct { + Header RequestHeader + + // The ID of the inode to be opened. + Inode InodeID + + // Mode and options flags. + Flags bazilfuse.OpenFlags +} + +type OpenDirResponse struct { + // 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 calls to methods like ReadDir that + // accept a directory handle. The file system must ensure this ID remains + // valid until a later call to ReleaseDirHandle. + Handle HandleID +} + +type ReadDirRequest struct { + Header RequestHeader + + // 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 +} + +type ReadDirResponse struct { + // 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 +} + +type ReleaseDirHandleRequest struct { + Header RequestHeader + + // 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 +} + +type ReleaseDirHandleResponse struct { +} + +type OpenFileRequest struct { + Header RequestHeader + + // The ID of the inode to be opened. + Inode InodeID + + // Mode and options flags. + Flags bazilfuse.OpenFlags +} + +type OpenFileResponse struct { + // 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 calls to methods like ReadFile that + // accept a file handle. The file system must ensure this ID remains valid + // until a later call to ReleaseFileHandle. + Handle HandleID +} + +type ReadFileRequest struct { + Header RequestHeader + + // 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 +} + +type ReadFileResponse struct { + // 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 +} + +type WriteFileRequest struct { + Header RequestHeader + + // 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 +} + +type WriteFileResponse struct { +} + +type SyncFileRequest struct { + Header RequestHeader + + // The file and handle being sync'd. + Inode InodeID + Handle HandleID +} + +type SyncFileResponse struct { +} + +type FlushFileRequest struct { + Header RequestHeader + + // The file and handle being flushed. + Inode InodeID + Handle HandleID +} + +type FlushFileResponse struct { +} + +type ReleaseFileHandleRequest struct { + Header RequestHeader + + // 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 +} + +type ReleaseFileHandleResponse struct { +} From 8e746f35b52136a4edabec227366a4886c68cfa6 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 24 Mar 2015 13:58:25 +1100 Subject: [PATCH 02/13] Updated the package statement. --- fuseops/ops.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index 52f8998..1cf821a 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -package fuse +// 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 import ( "fmt" From 386855c5591faa857c49b130deccd5552d3d5d88 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 24 Mar 2015 14:02:10 +1100 Subject: [PATCH 03/13] Carved up FileSystem. --- fuseops/ops.go | 476 +++++++++++++++++++++++-------------------------- 1 file changed, 223 insertions(+), 253 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index 1cf821a..579cdf0 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -23,278 +23,248 @@ import ( "time" "github.com/jacobsa/bazilfuse" - - "golang.org/x/net/context" ) -// An interface that must be implemented by file systems to be mounted with -// FUSE. See also the comments on request and response structs. +// This method is called once when mounting the file system. It must succeed +// in order for the mount to succeed. +type InitOp struct { +} + +//////////////////////////////////////////////////////////////////////// +// Inodes +//////////////////////////////////////////////////////////////////////// + +// Look up a child by name within a parent directory. The kernel calls this +// when resolving user paths to dentry structs, which are then cached. +type LookUpInodeOp struct { +} + +// Refresh the attributes for an inode whose ID was previously returned by +// LookUpInode. The kernel calls this when the FUSE VFS layer's cache of +// inode attributes is stale. This is controlled by the AttributesExpiration +// field of responses to LookUp, etc. +type GetInodeAttributesOp struct { +} + +// Change attributes for an inode. // -// Not all methods need to have interesting implementations. Embed a field of -// type fuseutil.NotImplementedFileSystem to inherit defaults that return -// ENOSYS to the kernel. +// The kernel calls this for obvious cases like chmod(2), and for less +// obvious cases like ftrunctate(2). +type SetInodeAttributesOp struct { +} + +// Forget an inode ID previously issued (e.g. by LookUpInode or MkDir). The +// kernel calls this when removing an inode from its internal caches. +type ForgetInodeOp struct { +} + +//////////////////////////////////////////////////////////////////////// +// 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. // -// Must be safe for concurrent access via all methods. -type FileSystem interface { - // This method is called once when mounting the file system. It must succeed - // in order for the mount to succeed. - Init( - ctx context.Context, - req *InitRequest) (*InitResponse, error) +// The 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). But volatile file systems and paranoid non-volatile +// file systems should check for the reasons described below on CreateFile. +type MkDirOp struct { +} - /////////////////////////////////// - // Inodes - /////////////////////////////////// +// Create a file inode and open it. +// +// The kernel calls this method 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 it's impossible to tell for sure that all kernels make this check +// in all cases and the official fuse documentation is less than encouraging +// (" the file does not exist, first create it with the specified mode, and +// then open it"). Therefore file systems would be smart to be paranoid and +// check themselves, returning EEXIST when the file already exists. This of +// course particularly applies to file systems that are volatile from the +// kernel's point of view. +type CreateFileOp struct { +} - // Look up a child by name within a parent directory. The kernel calls this - // when resolving user paths to dentry structs, which are then cached. - LookUpInode( - ctx context.Context, - req *LookUpInodeRequest) (*LookUpInodeResponse, error) +//////////////////////////////////////////////////////////////////////// +// Unlinking +//////////////////////////////////////////////////////////////////////// - // Refresh the attributes for an inode whose ID was previously returned by - // LookUpInode. The kernel calls this when the FUSE VFS layer's cache of - // inode attributes is stale. This is controlled by the AttributesExpiration - // field of responses to LookUp, etc. - GetInodeAttributes( - ctx context.Context, - req *GetInodeAttributesRequest) (*GetInodeAttributesResponse, error) +// 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 calls ForgetInode. +// +// 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 { +} - // Change attributes for an inode. - // - // The kernel calls this for obvious cases like chmod(2), and for less - // obvious cases like ftrunctate(2). - SetInodeAttributes( - ctx context.Context, - req *SetInodeAttributesRequest) (*SetInodeAttributesResponse, error) +// Unlink a file from its parent. If this brings the inode's link count to +// zero, the inode should be deleted once the kernel calls ForgetInode. 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 { +} - // Forget an inode ID previously issued (e.g. by LookUpInode or MkDir). The - // kernel calls this when removing an inode from its internal caches. - ForgetInode( - ctx context.Context, - req *ForgetInodeRequest) (*ForgetInodeResponse, error) +//////////////////////////////////////////////////////////////////////// +// Directory handles +//////////////////////////////////////////////////////////////////////// - /////////////////////////////////// - // Inode creation - /////////////////////////////////// +// Open a directory inode. +// +// On Linux the kernel calls this method 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 called for every +// open(2) (cf. https://github.com/osxfuse/osxfuse/issues/199). +type OpenDirOp struct { +} - // Create a directory inode as a child of an existing directory inode. The - // kernel sends this in response to a mkdir(2) call. - // - // The 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). But volatile file systems and paranoid non-volatile - // file systems should check for the reasons described below on CreateFile. - MkDir( - ctx context.Context, - req *MkDirRequest) (*MkDirResponse, error) +// Read entries from a directory previously opened with OpenDir. +type ReadDirOp struct { +} - // Create a file inode and open it. - // - // The kernel calls this method 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 it's impossible to tell for sure that all kernels make this check - // in all cases and the official fuse documentation is less than encouraging - // (" the file does not exist, first create it with the specified mode, and - // then open it"). Therefore file systems would be smart to be paranoid and - // check themselves, returning EEXIST when the file already exists. This of - // course particularly applies to file systems that are volatile from the - // kernel's point of view. - CreateFile( - ctx context.Context, - req *CreateFileRequest) (*CreateFileResponse, error) +// Release a previously-minted directory handle. The kernel calls 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 calls +// to the file system (unless it is reissued by the file system). +type ReleaseDirHandleOp struct { +} - /////////////////////////////////// - // Inode destruction - /////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// File handles +//////////////////////////////////////////////////////////////////////// - // 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 calls ForgetInode. - // - // The file system is responsible for checking that the directory is empty. - // - // Sample implementation in ext2: ext2_rmdir (http://goo.gl/B9QmFf) - RmDir( - ctx context.Context, - req *RmDirRequest) (*RmDirResponse, error) +// Open a file inode. +// +// On Linux the kernel calls this method 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 called for every open(2) +// (cf.https://github.com/osxfuse/osxfuse/issues/199). +type OpenFileOp struct { +} - // Unlink a file from its parent. If this brings the inode's link count to - // zero, the inode should be deleted once the kernel calls ForgetInode. 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) - Unlink( - ctx context.Context, - req *UnlinkRequest) (*UnlinkResponse, error) +// Read data from a file previously opened with CreateFile or OpenFile. +// +// Note that this method is not called for every call to read(2) by the end +// user; some reads may be served by the page cache. See notes on Write for +// more. +type ReadFileOp struct { +} - /////////////////////////////////// - // Directory handles - /////////////////////////////////// +// 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 method to be called: +// +// * 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 writes *will* be received before a call to Flush 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. +// +type WriteFileOp struct { +} - // Open a directory inode. - // - // On Linux the kernel calls this method 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 called for every - // open(2) (cf. https://github.com/osxfuse/osxfuse/issues/199). - OpenDir( - ctx context.Context, - req *OpenDirRequest) (*OpenDirResponse, error) +// 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 called by fdatasync(2) (cf. http://goo.gl/01R7rF), +// and may be called for msync(2) with the MS_SYNC flag (see the notes on +// FlushFile). +// +// See also: FlushFile, which may perform a similar purpose when closing a +// file (but which is not used in "real" file systems). +type SyncFileOp struct { +} - // Read entries from a directory previously opened with OpenDir. - ReadDir( - ctx context.Context, - req *ReadDirRequest) (*ReadDirResponse, error) +// Flush the current state of an open file to storage upon closing a file +// descriptor. +// +// vfs.txt documents this as being called 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 called 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 called 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 WriteFile calls for the +// modifications may not be received before the FlushFile request 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 WriteFile 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 +// SyncFile (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 by calling msync(2) +// with MS_SYNC, followed by munmap(2), followed by close(2). On Linux, the +// msync(2) appears to be optional because close(2) implies dirty page +// writeback (cf. http://goo.gl/HyzLTT). +// +// Because of cases like dup2(2), calls to FlushFile are not necessarily one +// to one with calls to OpenFile. They should not be used for reference +// counting, and the handle must remain valid even after the method is called +// (use ReleaseFileHandle to dispose 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 { +} - // Release a previously-minted directory handle. The kernel calls 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 calls - // to the file system (unless it is reissued by the file system). - ReleaseDirHandle( - ctx context.Context, - req *ReleaseDirHandleRequest) (*ReleaseDirHandleResponse, error) - - /////////////////////////////////// - // File handles - /////////////////////////////////// - - // Open a file inode. - // - // On Linux the kernel calls this method 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 called for every open(2) - // (cf.https://github.com/osxfuse/osxfuse/issues/199). - OpenFile( - ctx context.Context, - req *OpenFileRequest) (*OpenFileResponse, error) - - // Read data from a file previously opened with CreateFile or OpenFile. - // - // Note that this method is not called for every call to read(2) by the end - // user; some reads may be served by the page cache. See notes on Write for - // more. - ReadFile( - ctx context.Context, - req *ReadFileRequest) (*ReadFileResponse, error) - - // 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 method to be called: - // - // * 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 writes *will* be received before a call to Flush 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. - // - WriteFile( - ctx context.Context, - req *WriteFileRequest) (*WriteFileResponse, error) - - // 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 called by fdatasync(2) (cf. http://goo.gl/01R7rF), - // and may be called for msync(2) with the MS_SYNC flag (see the notes on - // FlushFile). - // - // See also: FlushFile, which may perform a similar purpose when closing a - // file (but which is not used in "real" file systems). - SyncFile( - ctx context.Context, - req *SyncFileRequest) (*SyncFileResponse, error) - - // Flush the current state of an open file to storage upon closing a file - // descriptor. - // - // vfs.txt documents this as being called 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 called 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 called 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 WriteFile calls for the - // modifications may not be received before the FlushFile request 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 WriteFile 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 - // SyncFile (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 by calling msync(2) - // with MS_SYNC, followed by munmap(2), followed by close(2). On Linux, the - // msync(2) appears to be optional because close(2) implies dirty page - // writeback (cf. http://goo.gl/HyzLTT). - // - // Because of cases like dup2(2), calls to FlushFile are not necessarily one - // to one with calls to OpenFile. They should not be used for reference - // counting, and the handle must remain valid even after the method is called - // (use ReleaseFileHandle to dispose 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. - FlushFile( - ctx context.Context, - req *FlushFileRequest) (*FlushFileResponse, error) - - // 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). - ReleaseFileHandle( - ctx context.Context, - req *ReleaseFileHandleRequest) (*ReleaseFileHandleResponse, error) +// 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). +type ReleaseFileHandleOp struct { } //////////////////////////////////////////////////////////////////////// From d84933bedfab62e67cf32099d3ca89b560e38cf6 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 24 Mar 2015 14:05:56 +1100 Subject: [PATCH 04/13] Moved "simple types" to a separate file. --- fuseops/ops.go | 200 ------------------------------------ fuseops/simple_types.go | 217 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+), 200 deletions(-) create mode 100644 fuseops/simple_types.go diff --git a/fuseops/ops.go b/fuseops/ops.go index 579cdf0..77e4cbc 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -18,7 +18,6 @@ package fuseops import ( - "fmt" "os" "time" @@ -267,205 +266,6 @@ type FlushFileOp struct { type ReleaseFileHandleOp struct { } -//////////////////////////////////////////////////////////////////////// -// Simple types -//////////////////////////////////////////////////////////////////////// - -// A 64-bit number used to uniquely identify a file or directory in the file -// system. File systems may mint inode IDs with any value except for -// RootInodeID. -// -// This corresponds to struct inode::i_no in the VFS layer. -// (Cf. http://goo.gl/tvYyQt) -type InodeID uint64 - -// A distinguished inode ID that identifies the root of the file system, e.g. -// in a request to OpenDir or LookUpInode. Unlike all other inode IDs, which -// are minted by the file system, the FUSE VFS layer may send a request for -// this ID without the file system ever having referenced it in a previous -// response. -const RootInodeID = 1 - -func init() { - // Make sure the constant above is correct. We do this at runtime rather than - // defining the constant in terms of bazilfuse.RootID for two reasons: - // - // 1. Users can more clearly see that the root ID is low and can therefore - // be used as e.g. an array index, with space reserved up to the root. - // - // 2. The constant can be untyped and can therefore more easily be used as - // an array index. - // - if RootInodeID != bazilfuse.RootID { - panic( - fmt.Sprintf( - "Oops, RootInodeID is wrong: %v vs. %v", - RootInodeID, - bazilfuse.RootID)) - } -} - -// Attributes for a file or directory inode. Corresponds to struct inode (cf. -// http://goo.gl/tvYyQt). -type InodeAttributes struct { - Size uint64 - - // The number of incoming hard links to this inode. - Nlink uint64 - - // The mode of the inode. This is exposed to the user in e.g. the result of - // fstat(2). - // - // Note that in contrast to the defaults for FUSE, this package mounts file - // systems in a manner such that the kernel checks inode permissions in the - // standard posix way. This is implemented by setting the default_permissions - // mount option (cf. http://goo.gl/1LxOop and http://goo.gl/1pTjuk). - // - // For example, in the case of mkdir: - // - // * (http://goo.gl/JkdxDI) sys_mkdirat calls inode_permission. - // - // * (...) inode_permission eventually calls do_inode_permission. - // - // * (http://goo.gl/aGCsmZ) calls i_op->permission, which is - // fuse_permission (cf. http://goo.gl/VZ9beH). - // - // * (http://goo.gl/5kqUKO) fuse_permission doesn't do anything at all for - // several code paths if FUSE_DEFAULT_PERMISSIONS is unset. In contrast, - // if that flag *is* set, then it calls generic_permission. - // - Mode os.FileMode - - // Time information. See `man 2 stat` for full details. - Atime time.Time // Time of last access - Mtime time.Time // Time of last modification - Ctime time.Time // Time of last modification to inode - Crtime time.Time // Time of creation (OS X only) - - // Ownership information - Uid uint32 - Gid uint32 -} - -// A generation number for an inode. Irrelevant for file systems that won't be -// exported over NFS. For those that will and that reuse inode IDs when they -// become free, the generation number must change when an ID is reused. -// -// This corresponds to struct inode::i_generation in the VFS layer. -// (Cf. http://goo.gl/tvYyQt) -// -// Some related reading: -// -// http://fuse.sourceforge.net/doxygen/structfuse__entry__param.html -// http://stackoverflow.com/q/11071996/1505451 -// http://goo.gl/CqvwyX -// http://julipedia.meroh.net/2005/09/nfs-file-handles.html -// http://goo.gl/wvo3MB -// -type GenerationNumber uint64 - -// An opaque 64-bit number used to identify a particular open handle to a file -// or directory. -// -// This corresponds to fuse_file_info::fh. -type HandleID uint64 - -// An offset into an open directory handle. This is opaque to FUSE, and can be -// used for whatever purpose the file system desires. See notes on -// ReadDirRequest.Offset for details. -type DirOffset uint64 - -// A header that is included with every request. -type RequestHeader struct { - // Credentials information for the process making the request. - Uid uint32 - Gid uint32 -} - -// Information about a child inode within its parent directory. Shared by the -// responses for LookUpInode, MkDir, CreateFile, etc. Consumed by the kernel in -// order to set up a dcache entry. -type ChildInodeEntry struct { - // The ID of the child inode. The file system must ensure that the returned - // inode ID remains valid until a later call to ForgetInode. - Child InodeID - - // A generation number for this incarnation of the inode with the given ID. - // See comments on type GenerationNumber for more. - Generation GenerationNumber - - // Current attributes for the child inode. - // - // When creating a new inode, the file system is responsible for initializing - // and recording (where supported) attributes like time information, - // ownership information, etc. - // - // Ownership information in particular must be set to something reasonable or - // by default root will own everything and unprivileged users won't be able - // to do anything useful. In traditional file systems in the kernel, the - // function inode_init_owner (http://goo.gl/5qavg8) contains the - // standards-compliant logic for this. - Attributes InodeAttributes - - // The FUSE VFS layer in the kernel maintains a cache of file attributes, - // used whenever up to date information about size, mode, etc. is needed. - // - // For example, this is the abridged call chain for fstat(2): - // - // * (http://goo.gl/tKBH1p) fstat calls vfs_fstat. - // * (http://goo.gl/3HeITq) vfs_fstat eventuall calls vfs_getattr_nosec. - // * (http://goo.gl/DccFQr) vfs_getattr_nosec calls i_op->getattr. - // * (http://goo.gl/dpKkst) fuse_getattr calls fuse_update_attributes. - // * (http://goo.gl/yNlqPw) fuse_update_attributes uses the values in the - // struct inode if allowed, otherwise calling out to the user-space code. - // - // In addition to obvious cases like fstat, this is also used in more subtle - // cases like updating size information before seeking (http://goo.gl/2nnMFa) - // or reading (http://goo.gl/FQSWs8). - // - // Most 'real' file systems do not set inode_operations::getattr, and - // therefore vfs_getattr_nosec calls generic_fillattr which simply grabs the - // information from the inode struct. This makes sense because these file - // systems cannot spontaneously change; all modifications go through the - // kernel which can update the inode struct as appropriate. - // - // In contrast, a FUSE file system may have spontaneous changes, so it calls - // out to user space to fetch attributes. However this is expensive, so the - // FUSE layer in the kernel caches the attributes if requested. - // - // This field controls when the attributes returned in this response and - // stashed in the struct inode should be re-queried. Leave at the zero value - // to disable caching. - // - // More reading: - // http://stackoverflow.com/q/21540315/1505451 - AttributesExpiration time.Time - - // The time until which the kernel may maintain an entry for this name to - // inode mapping in its dentry cache. After this time, it will revalidate the - // dentry. - // - // As in the discussion of attribute caching above, unlike real file systems, - // FUSE file systems may spontaneously change their name -> inode mapping. - // Therefore the FUSE VFS layer uses dentry_operations::d_revalidate - // (http://goo.gl/dVea0h) to intercept lookups and revalidate by calling the - // user-space LookUpInode method. However the latter may be slow, so it - // caches the entries until the time defined by this field. - // - // Example code walk: - // - // * (http://goo.gl/M2G3tO) lookup_dcache calls d_revalidate if enabled. - // * (http://goo.gl/ef0Elu) fuse_dentry_revalidate just uses the dentry's - // inode if fuse_dentry_time(entry) hasn't passed. Otherwise it sends a - // lookup request. - // - // Leave at the zero value to disable caching. - // - // Beware: this value is ignored on OS X, where entry caching is disabled by - // default. See notes on MountConfig.EnableVnodeCaching for more. - EntryExpiration time.Time -} - //////////////////////////////////////////////////////////////////////// // Requests and responses //////////////////////////////////////////////////////////////////////// diff --git a/fuseops/simple_types.go b/fuseops/simple_types.go new file mode 100644 index 0000000..d581d96 --- /dev/null +++ b/fuseops/simple_types.go @@ -0,0 +1,217 @@ +// 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" + + "github.com/jacobsa/bazilfuse" +) + +// A 64-bit number used to uniquely identify a file or directory in the file +// system. File systems may mint inode IDs with any value except for +// RootInodeID. +// +// This corresponds to struct inode::i_no in the VFS layer. +// (Cf. http://goo.gl/tvYyQt) +type InodeID uint64 + +// A distinguished inode ID that identifies the root of the file system, e.g. +// in an OpenDirOp or LookUpInodeOp. Unlike all other inode IDs, which are +// minted by the file system, the FUSE VFS layer may send a request for this ID +// without the file system ever having referenced it in a previous response. +const RootInodeID = 1 + +func init() { + // Make sure the constant above is correct. We do this at runtime rather than + // defining the constant in terms of bazilfuse.RootID for two reasons: + // + // 1. Users can more clearly see that the root ID is low and can therefore + // be used as e.g. an array index, with space reserved up to the root. + // + // 2. The constant can be untyped and can therefore more easily be used as + // an array index. + // + if RootInodeID != bazilfuse.RootID { + panic( + fmt.Sprintf( + "Oops, RootInodeID is wrong: %v vs. %v", + RootInodeID, + bazilfuse.RootID)) + } +} + +// Attributes for a file or directory inode. Corresponds to struct inode (cf. +// http://goo.gl/tvYyQt). +type InodeAttributes struct { + Size uint64 + + // The number of incoming hard links to this inode. + Nlink uint64 + + // The mode of the inode. This is exposed to the user in e.g. the result of + // fstat(2). + // + // Note that in contrast to the defaults for FUSE, this package mounts file + // systems in a manner such that the kernel checks inode permissions in the + // standard posix way. This is implemented by setting the default_permissions + // mount option (cf. http://goo.gl/1LxOop and http://goo.gl/1pTjuk). + // + // For example, in the case of mkdir: + // + // * (http://goo.gl/JkdxDI) sys_mkdirat calls inode_permission. + // + // * (...) inode_permission eventually calls do_inode_permission. + // + // * (http://goo.gl/aGCsmZ) calls i_op->permission, which is + // fuse_permission (cf. http://goo.gl/VZ9beH). + // + // * (http://goo.gl/5kqUKO) fuse_permission doesn't do anything at all for + // several code paths if FUSE_DEFAULT_PERMISSIONS is unset. In contrast, + // if that flag *is* set, then it calls generic_permission. + // + Mode os.FileMode + + // Time information. See `man 2 stat` for full details. + Atime time.Time // Time of last access + Mtime time.Time // Time of last modification + Ctime time.Time // Time of last modification to inode + Crtime time.Time // Time of creation (OS X only) + + // Ownership information + Uid uint32 + Gid uint32 +} + +// A generation number for an inode. Irrelevant for file systems that won't be +// exported over NFS. For those that will and that reuse inode IDs when they +// become free, the generation number must change when an ID is reused. +// +// This corresponds to struct inode::i_generation in the VFS layer. +// (Cf. http://goo.gl/tvYyQt) +// +// Some related reading: +// +// http://fuse.sourceforge.net/doxygen/structfuse__entry__param.html +// http://stackoverflow.com/q/11071996/1505451 +// http://goo.gl/CqvwyX +// http://julipedia.meroh.net/2005/09/nfs-file-handles.html +// http://goo.gl/wvo3MB +// +type GenerationNumber uint64 + +// An opaque 64-bit number used to identify a particular open handle to a file +// or directory. +// +// This corresponds to fuse_file_info::fh. +type HandleID uint64 + +// An offset into an open directory handle. This is opaque to FUSE, and can be +// used for whatever purpose the file system desires. See notes on +// ReadDirOp.Offset for details. +type DirOffset uint64 + +// A header that is included with every request. +type RequestHeader struct { + // Credentials information for the process making the request. + Uid uint32 + Gid uint32 +} + +// Information about a child inode within its parent directory. Shared by +// LookUpInodeOp, MkDirOp, CreateFileOp, etc. Consumed by the kernel in order +// to set up a dcache entry. +type ChildInodeEntry struct { + // The ID of the child inode. The file system must ensure that the returned + // inode ID remains valid until a later ForgetInodeOp. + Child InodeID + + // A generation number for this incarnation of the inode with the given ID. + // See comments on type GenerationNumber for more. + Generation GenerationNumber + + // Current attributes for the child inode. + // + // When creating a new inode, the file system is responsible for initializing + // and recording (where supported) attributes like time information, + // ownership information, etc. + // + // Ownership information in particular must be set to something reasonable or + // by default root will own everything and unprivileged users won't be able + // to do anything useful. In traditional file systems in the kernel, the + // function inode_init_owner (http://goo.gl/5qavg8) contains the + // standards-compliant logic for this. + Attributes InodeAttributes + + // The FUSE VFS layer in the kernel maintains a cache of file attributes, + // used whenever up to date information about size, mode, etc. is needed. + // + // For example, this is the abridged call chain for fstat(2): + // + // * (http://goo.gl/tKBH1p) fstat calls vfs_fstat. + // * (http://goo.gl/3HeITq) vfs_fstat eventuall calls vfs_getattr_nosec. + // * (http://goo.gl/DccFQr) vfs_getattr_nosec calls i_op->getattr. + // * (http://goo.gl/dpKkst) fuse_getattr calls fuse_update_attributes. + // * (http://goo.gl/yNlqPw) fuse_update_attributes uses the values in the + // struct inode if allowed, otherwise calling out to the user-space code. + // + // In addition to obvious cases like fstat, this is also used in more subtle + // cases like updating size information before seeking (http://goo.gl/2nnMFa) + // or reading (http://goo.gl/FQSWs8). + // + // Most 'real' file systems do not set inode_operations::getattr, and + // therefore vfs_getattr_nosec calls generic_fillattr which simply grabs the + // information from the inode struct. This makes sense because these file + // systems cannot spontaneously change; all modifications go through the + // kernel which can update the inode struct as appropriate. + // + // In contrast, a FUSE file system may have spontaneous changes, so it calls + // out to user space to fetch attributes. However this is expensive, so the + // FUSE layer in the kernel caches the attributes if requested. + // + // This field controls when the attributes returned in this response and + // stashed in the struct inode should be re-queried. Leave at the zero value + // to disable caching. + // + // More reading: + // http://stackoverflow.com/q/21540315/1505451 + AttributesExpiration time.Time + + // The time until which the kernel may maintain an entry for this name to + // inode mapping in its dentry cache. After this time, it will revalidate the + // dentry. + // + // As in the discussion of attribute caching above, unlike real file systems, + // FUSE file systems may spontaneously change their name -> inode mapping. + // Therefore the FUSE VFS layer uses dentry_operations::d_revalidate + // (http://goo.gl/dVea0h) to intercept lookups and revalidate by calling the + // user-space LookUpInode method. However the latter may be slow, so it + // caches the entries until the time defined by this field. + // + // Example code walk: + // + // * (http://goo.gl/M2G3tO) lookup_dcache calls d_revalidate if enabled. + // * (http://goo.gl/ef0Elu) fuse_dentry_revalidate just uses the dentry's + // inode if fuse_dentry_time(entry) hasn't passed. Otherwise it sends a + // lookup request. + // + // Leave at the zero value to disable caching. + // + // Beware: this value is ignored on OS X, where entry caching is disabled by + // default. See notes on MountConfig.EnableVnodeCaching for more. + EntryExpiration time.Time +} From d43618adecd175f1e635368816eef8d2c71fa878 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 24 Mar 2015 14:11:07 +1100 Subject: [PATCH 05/13] Refined a bunch of wording. --- fuseops/ops.go | 115 ++++++++++++++++++++-------------------- fuseops/simple_types.go | 4 +- 2 files changed, 59 insertions(+), 60 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index 77e4cbc..df5dc38 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -24,8 +24,8 @@ import ( "github.com/jacobsa/bazilfuse" ) -// This method is called once when mounting the file system. It must succeed -// in order for the mount to succeed. +// Sent once when mounting the file system. It must succeed in order for the +// mount to succeed. type InitOp struct { } @@ -33,27 +33,27 @@ type InitOp struct { // Inodes //////////////////////////////////////////////////////////////////////// -// Look up a child by name within a parent directory. The kernel calls this +// 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 { } // Refresh the attributes for an inode whose ID was previously returned by -// LookUpInode. The kernel calls this when the FUSE VFS layer's cache of -// inode attributes is stale. This is controlled by the AttributesExpiration -// field of responses to LookUp, etc. +// LookUpInode. The kernel sends this when the FUSE VFS layer's cache of inode +// attributes is stale. This is controlled by the AttributesExpiration field of +// responses to LookUp, etc. type GetInodeAttributesOp struct { } // Change attributes for an inode. // -// The kernel calls this for obvious cases like chmod(2), and for less -// obvious cases like ftrunctate(2). +// The kernel sends this for obvious cases like chmod(2), and for less obvious +// cases like ftrunctate(2). type SetInodeAttributesOp struct { } // Forget an inode ID previously issued (e.g. by LookUpInode or MkDir). The -// kernel calls this when removing an inode from its internal caches. +// kernel sends this when removing an inode from its internal caches. type ForgetInodeOp struct { } @@ -73,9 +73,9 @@ type MkDirOp struct { // Create a file inode and open it. // -// The kernel calls this method 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). +// 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 it's impossible to tell for sure that all kernels make this check // in all cases and the official fuse documentation is less than encouraging @@ -93,7 +93,7 @@ type CreateFileOp struct { // 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 calls ForgetInode. +// once the kernel sends ForgetInodeOp. // // The file system is responsible for checking that the directory is empty. // @@ -102,7 +102,7 @@ type RmDirOp struct { } // Unlink a file from its parent. If this brings the inode's link count to -// zero, the inode should be deleted once the kernel calls ForgetInode. It +// 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) @@ -115,10 +115,10 @@ type UnlinkOp struct { // Open a directory inode. // -// On Linux the kernel calls this method 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 called for every -// open(2) (cf. https://github.com/osxfuse/osxfuse/issues/199). +// 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 { } @@ -126,12 +126,12 @@ type OpenDirOp struct { type ReadDirOp struct { } -// Release a previously-minted directory handle. The kernel calls this when -// there are no more references to an open directory: all file descriptors -// are closed and all memory mappings are unmapped. +// 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 calls -// to the file system (unless it is reissued by the file system). +// 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). type ReleaseDirHandleOp struct { } @@ -141,17 +141,17 @@ type ReleaseDirHandleOp struct { // Open a file inode. // -// On Linux the kernel calls this method 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 called for every open(2) +// 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 { } // Read data from a file previously opened with CreateFile or OpenFile. // -// Note that this method is not called for every call to read(2) by the end -// user; some reads may be served by the page cache. See notes on Write for +// 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 { } @@ -160,7 +160,7 @@ type ReadFileOp struct { // // 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 method to be called: +// 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 @@ -171,8 +171,8 @@ type ReadFileOp struct { // * (http://goo.gl/RqYIxY) fuse_writepage_locked makes a write request to // the userspace server. // -// Note that writes *will* be received before a call to Flush when closing -// the file descriptor to which they were written: +// Note that writes *will* be received before a FlushOp 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 @@ -197,11 +197,11 @@ type WriteFileOp struct { // // * (http://goo.gl/5L2SMy) vfs_fsync_range calls f_op->fsync. // -// Note that this is also called by fdatasync(2) (cf. http://goo.gl/01R7rF), -// and may be called for msync(2) with the MS_SYNC flag (see the notes on -// FlushFile). +// 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: FlushFile, which may perform a similar purpose when closing a +// 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 { } @@ -209,26 +209,25 @@ type SyncFileOp struct { // Flush the current state of an open file to storage upon closing a file // descriptor. // -// vfs.txt documents this as being called for each close(2) system call (cf. +// 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 called 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. +// 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 called is mmap'd -// files, where the behavior is complicated: +// 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 WriteFile calls for the -// modifications may not be received before the FlushFile request for the -// close(2) (cf. http://goo.gl/kVmNcx). +// 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) @@ -243,10 +242,10 @@ type SyncFileOp struct { // msync(2) appears to be optional because close(2) implies dirty page // writeback (cf. http://goo.gl/HyzLTT). // -// Because of cases like dup2(2), calls to FlushFile are not necessarily one -// to one with calls to OpenFile. They should not be used for reference -// counting, and the handle must remain valid even after the method is called -// (use ReleaseFileHandle to dispose of it). +// 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. @@ -383,9 +382,9 @@ type CreateFileResponse struct { // 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 calls to methods like ReadFile that - // accept a file handle. The file system must ensure this ID remains valid - // until a later call to ReleaseFileHandle. + // 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 } @@ -428,9 +427,9 @@ type OpenDirResponse struct { // 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 calls to methods like ReadDir that - // accept a directory handle. The file system must ensure this ID remains - // valid until a later call to ReleaseDirHandle. + // 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 } @@ -552,9 +551,9 @@ type OpenFileResponse struct { // 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 calls to methods like ReadFile that - // accept a file handle. The file system must ensure this ID remains valid - // until a later call to ReleaseFileHandle. + // 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 } diff --git a/fuseops/simple_types.go b/fuseops/simple_types.go index d581d96..ae9260f 100644 --- a/fuseops/simple_types.go +++ b/fuseops/simple_types.go @@ -125,8 +125,8 @@ type HandleID uint64 // ReadDirOp.Offset for details. type DirOffset uint64 -// A header that is included with every request. -type RequestHeader struct { +// A header that is included with every op. +type Header struct { // Credentials information for the process making the request. Uid uint32 Gid uint32 From 9d282740dd44ba4b1aafada7055dbddf691cdb5b Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 24 Mar 2015 14:11:28 +1100 Subject: [PATCH 06/13] Ported Init. --- fuseops/ops.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index df5dc38..1285f36 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -27,6 +27,7 @@ import ( // Sent once when mounting the file system. It must succeed in order for the // mount to succeed. type InitOp struct { + Header RequestHeader } //////////////////////////////////////////////////////////////////////// @@ -269,13 +270,6 @@ type ReleaseFileHandleOp struct { // Requests and responses //////////////////////////////////////////////////////////////////////// -type InitRequest struct { - Header RequestHeader -} - -type InitResponse struct { -} - type LookUpInodeRequest struct { Header RequestHeader From 39c6e2e5ce11608f7e56b6a9e4113a572bb57ab2 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 24 Mar 2015 14:12:10 +1100 Subject: [PATCH 07/13] Ported LookUpInode. --- fuseops/ops.go | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index 1285f36..cbe6763 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -37,6 +37,24 @@ type InitOp struct { // 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 { + Header RequestHeader + + // 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. + Entry ChildInodeEntry } // Refresh the attributes for an inode whose ID was previously returned by @@ -270,28 +288,6 @@ type ReleaseFileHandleOp struct { // Requests and responses //////////////////////////////////////////////////////////////////////// -type LookUpInodeRequest struct { - Header RequestHeader - - // 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 -} - -type LookUpInodeResponse struct { - Entry ChildInodeEntry -} - type GetInodeAttributesRequest struct { Header RequestHeader From 5203b8289a18333c8356956a7d2f62d2a52edfa0 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 24 Mar 2015 14:13:28 +1100 Subject: [PATCH 08/13] Ported two more methods. --- fuseops/ops.go | 68 ++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index cbe6763..2742237 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -57,11 +57,21 @@ type LookUpInodeOp struct { Entry ChildInodeEntry } -// Refresh the attributes for an inode whose ID was previously returned by -// LookUpInode. The kernel sends this when the FUSE VFS layer's cache of inode -// attributes is stale. This is controlled by the AttributesExpiration field of -// responses to LookUp, etc. +// 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 { + Header RequestHeader + + // 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 } // Change attributes for an inode. @@ -69,6 +79,22 @@ type GetInodeAttributesOp struct { // The kernel sends this for obvious cases like chmod(2), and for less obvious // cases like ftrunctate(2). type SetInodeAttributesOp struct { + Header RequestHeader + + // 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 } // Forget an inode ID previously issued (e.g. by LookUpInode or MkDir). The @@ -288,40 +314,6 @@ type ReleaseFileHandleOp struct { // Requests and responses //////////////////////////////////////////////////////////////////////// -type GetInodeAttributesRequest struct { - Header RequestHeader - - // The inode of interest. - Inode InodeID -} - -type GetInodeAttributesResponse struct { - // Attributes for the inode, and the time at which they should expire. See - // notes on ChildInodeEntry.AttributesExpiration for more. - Attributes InodeAttributes - AttributesExpiration time.Time -} - -type SetInodeAttributesRequest struct { - Header RequestHeader - - // 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 -} - -type SetInodeAttributesResponse struct { - // 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 -} - type ForgetInodeRequest struct { Header RequestHeader From e930166b68dd183839ae2edec09589032aaa7f0a Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 24 Mar 2015 14:13:59 +1100 Subject: [PATCH 09/13] Ported two more methods. --- fuseops/ops.go | 45 +++++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index 2742237..ebb810a 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -100,6 +100,12 @@ type SetInodeAttributesOp struct { // Forget an inode ID previously issued (e.g. by LookUpInode or MkDir). The // kernel sends this when removing an inode from its internal caches. type ForgetInodeOp struct { + Header RequestHeader + + // The inode to be forgotten. The kernel guarantees that the node ID will not + // be used in further calls to the file system (unless it is reissued by the + // file system). + ID InodeID } //////////////////////////////////////////////////////////////////////// @@ -114,6 +120,17 @@ type ForgetInodeOp struct { // http://goo.gl/FZpLu5). But volatile file systems and paranoid non-volatile // file systems should check for the reasons described below on CreateFile. type MkDirOp struct { + Header RequestHeader + + // 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. + Entry ChildInodeEntry } // Create a file inode and open it. @@ -314,34 +331,6 @@ type ReleaseFileHandleOp struct { // Requests and responses //////////////////////////////////////////////////////////////////////// -type ForgetInodeRequest struct { - Header RequestHeader - - // The inode to be forgotten. The kernel guarantees that the node ID will not - // be used in further calls to the file system (unless it is reissued by the - // file system). - ID InodeID -} - -type ForgetInodeResponse struct { -} - -type MkDirRequest struct { - Header RequestHeader - - // 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 -} - -type MkDirResponse struct { - // Information about the inode that was created. - Entry ChildInodeEntry -} - type CreateFileRequest struct { Header RequestHeader From f4c58fac66595d8f2d2b0e3a2a17e5c3c345438a Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 24 Mar 2015 14:15:33 +1100 Subject: [PATCH 10/13] Ported more methods. --- fuseops/ops.go | 126 +++++++++++++++++++++---------------------------- 1 file changed, 53 insertions(+), 73 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index ebb810a..d9bacc5 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -147,6 +147,30 @@ type MkDirOp struct { // course particularly applies to file systems that are volatile from the // kernel's point of view. type CreateFileOp struct { + Header RequestHeader + + // 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 + + // Flags for the open operation. + Flags bazilfuse.OpenFlags + + // Set by the file system: information about the inode that was created. + 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 } //////////////////////////////////////////////////////////////////////// @@ -161,6 +185,12 @@ type CreateFileOp struct { // // Sample implementation in ext2: ext2_rmdir (http://goo.gl/B9QmFf) type RmDirOp struct { + Header RequestHeader + + // The ID of parent directory inode, and the name of the directory being + // removed within it. + Parent InodeID + Name string } // Unlink a file from its parent. If this brings the inode's link count to @@ -169,6 +199,12 @@ type RmDirOp struct { // // Sample implementation in ext2: ext2_unlink (http://goo.gl/hY6r6C) type UnlinkOp struct { + Header RequestHeader + + // The ID of parent directory inode, and the name of the file being removed + // within it. + Parent InodeID + Name string } //////////////////////////////////////////////////////////////////////// @@ -182,6 +218,23 @@ 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 { + Header RequestHeader + + // The ID of the inode to be opened. + Inode InodeID + + // Mode and options flags. + Flags bazilfuse.OpenFlags + + // 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 } // Read entries from a directory previously opened with OpenDir. @@ -331,79 +384,6 @@ type ReleaseFileHandleOp struct { // Requests and responses //////////////////////////////////////////////////////////////////////// -type CreateFileRequest struct { - Header RequestHeader - - // 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 - - // Flags for the open operation. - Flags bazilfuse.OpenFlags -} - -type CreateFileResponse struct { - // Information about the inode that was created. - Entry ChildInodeEntry - - // 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 -} - -type RmDirRequest struct { - Header RequestHeader - - // The ID of parent directory inode, and the name of the directory being - // removed within it. - Parent InodeID - Name string -} - -type UnlinkResponse struct { -} - -type UnlinkRequest struct { - Header RequestHeader - - // The ID of parent directory inode, and the name of the file being removed - // within it. - Parent InodeID - Name string -} - -type RmDirResponse struct { -} - -type OpenDirRequest struct { - Header RequestHeader - - // The ID of the inode to be opened. - Inode InodeID - - // Mode and options flags. - Flags bazilfuse.OpenFlags -} - -type OpenDirResponse struct { - // 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 -} - type ReadDirRequest struct { Header RequestHeader From 680eaa899d48aa993ee134802a661210182c77bf Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 24 Mar 2015 14:16:59 +1100 Subject: [PATCH 11/13] Ported more methods. --- fuseops/ops.go | 357 +++++++++++++++++++++++-------------------------- 1 file changed, 165 insertions(+), 192 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index d9bacc5..5df0c8f 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -239,6 +239,92 @@ type OpenDirOp struct { // Read entries from a directory previously opened with OpenDir. type ReadDirOp struct { + Header RequestHeader + + // 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 } // Release a previously-minted directory handle. The kernel sends this when @@ -248,6 +334,12 @@ type ReadDirOp struct { // 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). type ReleaseDirHandleOp struct { + Header RequestHeader + + // 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 } //////////////////////////////////////////////////////////////////////// @@ -261,6 +353,22 @@ 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 { + Header RequestHeader + + // The ID of the inode to be opened. + Inode InodeID + + // Mode and options flags. + Flags bazilfuse.OpenFlags + + // 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 } // Read data from a file previously opened with CreateFile or OpenFile. @@ -269,6 +377,27 @@ type OpenFileOp struct { // some reads may be served by the page cache. See notes on WriteFileOp for // more. type ReadFileOp struct { + Header RequestHeader + + // 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 } // Write data to a file previously opened with CreateFile or OpenFile. @@ -300,6 +429,42 @@ type ReadFileOp struct { // flush request. // type WriteFileOp struct { + Header RequestHeader + + // 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 } // Synchronize the current contents of an open file to storage. @@ -384,198 +549,6 @@ type ReleaseFileHandleOp struct { // Requests and responses //////////////////////////////////////////////////////////////////////// -type ReadDirRequest struct { - Header RequestHeader - - // 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 -} - -type ReadDirResponse struct { - // 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 -} - -type ReleaseDirHandleRequest struct { - Header RequestHeader - - // 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 -} - -type ReleaseDirHandleResponse struct { -} - -type OpenFileRequest struct { - Header RequestHeader - - // The ID of the inode to be opened. - Inode InodeID - - // Mode and options flags. - Flags bazilfuse.OpenFlags -} - -type OpenFileResponse struct { - // 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 -} - -type ReadFileRequest struct { - Header RequestHeader - - // 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 -} - -type ReadFileResponse struct { - // 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 -} - -type WriteFileRequest struct { - Header RequestHeader - - // 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 -} - -type WriteFileResponse struct { -} - type SyncFileRequest struct { Header RequestHeader From 9aff9a66a8f030309eae217dc539927f3c2d1ae7 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 24 Mar 2015 14:17:30 +1100 Subject: [PATCH 12/13] Finished porting methods. --- fuseops/ops.go | 42 ++++++++++-------------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index 5df0c8f..6d4a1af 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -484,6 +484,11 @@ 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 { + Header RequestHeader + + // The file and handle being sync'd. + Inode InodeID + Handle HandleID } // Flush the current state of an open file to storage upon closing a file @@ -534,6 +539,11 @@ 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 { + Header RequestHeader + + // The file and handle being flushed. + Inode InodeID + Handle HandleID } // Release a previously-minted file handle. The kernel calls this when there @@ -543,35 +553,6 @@ type FlushFileOp struct { // 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). type ReleaseFileHandleOp struct { -} - -//////////////////////////////////////////////////////////////////////// -// Requests and responses -//////////////////////////////////////////////////////////////////////// - -type SyncFileRequest struct { - Header RequestHeader - - // The file and handle being sync'd. - Inode InodeID - Handle HandleID -} - -type SyncFileResponse struct { -} - -type FlushFileRequest struct { - Header RequestHeader - - // The file and handle being flushed. - Inode InodeID - Handle HandleID -} - -type FlushFileResponse struct { -} - -type ReleaseFileHandleRequest struct { Header RequestHeader // The handle ID to be released. The kernel guarantees that this ID will not @@ -579,6 +560,3 @@ type ReleaseFileHandleRequest struct { // file system). Handle HandleID } - -type ReleaseFileHandleResponse struct { -} From d611ddfab03d9eb3e2dc3d035ab6ee450e3f0a33 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 24 Mar 2015 14:18:01 +1100 Subject: [PATCH 13/13] Fixed build errors. --- fuseops/ops.go | 36 ++++++++++++++++++------------------ fuseops/simple_types.go | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/fuseops/ops.go b/fuseops/ops.go index 6d4a1af..a337710 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -27,7 +27,7 @@ import ( // Sent once when mounting the file system. It must succeed in order for the // mount to succeed. type InitOp struct { - Header RequestHeader + Header OpHeader } //////////////////////////////////////////////////////////////////////// @@ -37,7 +37,7 @@ type InitOp struct { // 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 { - Header RequestHeader + Header OpHeader // The ID of the directory inode to which the child belongs. Parent InodeID @@ -62,7 +62,7 @@ type LookUpInodeOp struct { // inode attributes is stale. This is controlled by the AttributesExpiration // field of ChildInodeEntry, etc. type GetInodeAttributesOp struct { - Header RequestHeader + Header OpHeader // The inode of interest. Inode InodeID @@ -79,7 +79,7 @@ type GetInodeAttributesOp struct { // The kernel sends this for obvious cases like chmod(2), and for less obvious // cases like ftrunctate(2). type SetInodeAttributesOp struct { - Header RequestHeader + Header OpHeader // The inode of interest. Inode InodeID @@ -100,7 +100,7 @@ type SetInodeAttributesOp struct { // Forget an inode ID previously issued (e.g. by LookUpInode or MkDir). The // kernel sends this when removing an inode from its internal caches. type ForgetInodeOp struct { - Header RequestHeader + Header OpHeader // The inode to be forgotten. The kernel guarantees that the node ID will not // be used in further calls to the file system (unless it is reissued by the @@ -120,7 +120,7 @@ type ForgetInodeOp struct { // http://goo.gl/FZpLu5). But volatile file systems and paranoid non-volatile // file systems should check for the reasons described below on CreateFile. type MkDirOp struct { - Header RequestHeader + Header OpHeader // The ID of parent directory inode within which to create the child. Parent InodeID @@ -147,7 +147,7 @@ type MkDirOp struct { // course particularly applies to file systems that are volatile from the // kernel's point of view. type CreateFileOp struct { - Header RequestHeader + Header OpHeader // The ID of parent directory inode within which to create the child file. Parent InodeID @@ -185,7 +185,7 @@ type CreateFileOp struct { // // Sample implementation in ext2: ext2_rmdir (http://goo.gl/B9QmFf) type RmDirOp struct { - Header RequestHeader + Header OpHeader // The ID of parent directory inode, and the name of the directory being // removed within it. @@ -199,7 +199,7 @@ type RmDirOp struct { // // Sample implementation in ext2: ext2_unlink (http://goo.gl/hY6r6C) type UnlinkOp struct { - Header RequestHeader + Header OpHeader // The ID of parent directory inode, and the name of the file being removed // within it. @@ -218,7 +218,7 @@ 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 { - Header RequestHeader + Header OpHeader // The ID of the inode to be opened. Inode InodeID @@ -239,7 +239,7 @@ type OpenDirOp struct { // Read entries from a directory previously opened with OpenDir. type ReadDirOp struct { - Header RequestHeader + Header OpHeader // The directory inode that we are reading, and the handle previously // returned by OpenDir when opening that inode. @@ -334,7 +334,7 @@ type ReadDirOp struct { // 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). type ReleaseDirHandleOp struct { - Header RequestHeader + Header OpHeader // 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 @@ -353,7 +353,7 @@ 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 { - Header RequestHeader + Header OpHeader // The ID of the inode to be opened. Inode InodeID @@ -377,7 +377,7 @@ type OpenFileOp struct { // some reads may be served by the page cache. See notes on WriteFileOp for // more. type ReadFileOp struct { - Header RequestHeader + Header OpHeader // The file inode that we are reading, and the handle previously returned by // CreateFile or OpenFile when opening that inode. @@ -429,7 +429,7 @@ type ReadFileOp struct { // flush request. // type WriteFileOp struct { - Header RequestHeader + Header OpHeader // The file inode that we are modifying, and the handle previously returned // by CreateFile or OpenFile when opening that inode. @@ -484,7 +484,7 @@ 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 { - Header RequestHeader + Header OpHeader // The file and handle being sync'd. Inode InodeID @@ -539,7 +539,7 @@ 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 { - Header RequestHeader + Header OpHeader // The file and handle being flushed. Inode InodeID @@ -553,7 +553,7 @@ type FlushFileOp struct { // 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). type ReleaseFileHandleOp struct { - Header RequestHeader + Header OpHeader // 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 diff --git a/fuseops/simple_types.go b/fuseops/simple_types.go index ae9260f..d56e964 100644 --- a/fuseops/simple_types.go +++ b/fuseops/simple_types.go @@ -126,7 +126,7 @@ type HandleID uint64 type DirOffset uint64 // A header that is included with every op. -type Header struct { +type OpHeader struct { // Credentials information for the process making the request. Uid uint32 Gid uint32