2015-02-27 00:54:16 +03:00
|
|
|
// Copyright 2015 Google Inc. All Rights Reserved.
|
|
|
|
// Author: jacobsa@google.com (Aaron Jacobs)
|
|
|
|
|
2015-02-27 00:57:18 +03:00
|
|
|
package fuse
|
2015-02-27 00:54:16 +03:00
|
|
|
|
|
|
|
import (
|
2015-02-27 05:44:51 +03:00
|
|
|
"os"
|
2015-02-27 00:54:16 +03:00
|
|
|
"time"
|
|
|
|
|
2015-02-27 01:32:18 +03:00
|
|
|
bazilfuse "bazil.org/fuse"
|
2015-02-27 00:54:16 +03:00
|
|
|
|
|
|
|
"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
|
2015-02-27 01:59:51 +03:00
|
|
|
// type fuseutil.NotImplementedFileSystem to inherit defaults that return
|
|
|
|
// ENOSYS to the kernel.
|
2015-02-27 00:54:16 +03:00
|
|
|
//
|
|
|
|
// Must be safe for concurrent access via all methods.
|
|
|
|
type FileSystem interface {
|
2015-02-27 06:40:09 +03:00
|
|
|
// 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)
|
|
|
|
|
2015-02-27 00:54:16 +03:00
|
|
|
// 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.
|
2015-02-27 01:48:30 +03:00
|
|
|
LookUpInode(
|
2015-02-27 00:54:16 +03:00
|
|
|
ctx context.Context,
|
2015-02-27 01:48:30 +03:00
|
|
|
req *LookUpInodeRequest) (*LookUpInodeResponse, error)
|
2015-02-27 00:54:16 +03:00
|
|
|
|
2015-02-27 05:32:04 +03:00
|
|
|
// Refresh the attributes for an inode whose ID was previously returned by
|
2015-02-27 05:32:44 +03:00
|
|
|
// 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.
|
2015-02-27 05:32:04 +03:00
|
|
|
GetInodeAttributes(
|
|
|
|
ctx context.Context,
|
|
|
|
req *GetInodeAttributesRequest) (*GetInodeAttributesResponse, error)
|
|
|
|
|
2015-02-27 01:48:30 +03:00
|
|
|
// Forget an inode ID previously issued (e.g. by LookUpInode). The kernel
|
|
|
|
// calls this when removing an inode from its internal caches.
|
2015-02-27 01:42:36 +03:00
|
|
|
ForgetInode(
|
|
|
|
ctx context.Context,
|
|
|
|
req *ForgetInodeRequest) (*ForgetInodeResponse, error)
|
|
|
|
|
2015-02-27 05:29:02 +03:00
|
|
|
///////////////////////////////////
|
|
|
|
// Directory handles
|
|
|
|
///////////////////////////////////
|
|
|
|
|
2015-02-27 01:42:36 +03:00
|
|
|
// Open a directory inode. 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.
|
|
|
|
OpenDir(
|
2015-02-27 00:54:16 +03:00
|
|
|
ctx context.Context,
|
2015-02-27 01:42:36 +03:00
|
|
|
req *OpenDirRequest) (*OpenDirResponse, error)
|
|
|
|
|
2015-02-27 03:11:31 +03:00
|
|
|
// Read entries from a directory previously opened with OpenDir.
|
2015-02-27 02:43:01 +03:00
|
|
|
ReadDir(
|
|
|
|
ctx context.Context,
|
|
|
|
req *ReadDirRequest) (*ReadDirResponse, error)
|
|
|
|
|
2015-02-27 05:29:02 +03:00
|
|
|
// 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.
|
2015-02-27 01:52:34 +03:00
|
|
|
//
|
|
|
|
// 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).
|
2015-02-27 05:29:02 +03:00
|
|
|
ReleaseDirHandle(
|
2015-02-27 01:42:36 +03:00
|
|
|
ctx context.Context,
|
2015-02-27 05:29:02 +03:00
|
|
|
req *ReleaseDirHandleRequest) (*ReleaseDirHandleResponse, error)
|
2015-02-27 00:54:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
// 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.
|
2015-02-27 01:48:30 +03:00
|
|
|
// 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.
|
2015-02-27 01:32:18 +03:00
|
|
|
const RootInodeID InodeID = InodeID(bazilfuse.RootID)
|
2015-02-27 00:54:16 +03:00
|
|
|
|
2015-02-27 02:43:58 +03:00
|
|
|
// Attributes for a file or directory inode. Corresponds to struct inode (cf.
|
|
|
|
// http://goo.gl/tvYyQt).
|
|
|
|
type InodeAttributes struct {
|
2015-02-27 06:42:22 +03:00
|
|
|
Size uint64
|
|
|
|
Mode os.FileMode
|
|
|
|
|
|
|
|
// Time information
|
2015-02-27 06:05:52 +03:00
|
|
|
Atime time.Time
|
|
|
|
Mtime time.Time
|
|
|
|
Crtime time.Time
|
2015-02-27 06:42:22 +03:00
|
|
|
|
|
|
|
// Owner information
|
|
|
|
Uid uint32
|
|
|
|
Gid uint32
|
2015-02-27 02:43:58 +03:00
|
|
|
}
|
|
|
|
|
2015-02-27 00:54:16 +03:00
|
|
|
// 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
|
|
|
|
|
2015-02-27 02:43:58 +03:00
|
|
|
// 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
|
2015-02-27 00:54:16 +03:00
|
|
|
|
2015-02-27 03:03:19 +03:00
|
|
|
// 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
|
|
|
|
|
2015-02-27 00:54:16 +03:00
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
// Requests and responses
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2015-02-27 06:40:09 +03:00
|
|
|
type InitRequest struct {
|
|
|
|
// User and group IDs for the process that is mounting the file system.
|
|
|
|
Uid uint32
|
|
|
|
Gid uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
type InitResponse struct {
|
|
|
|
}
|
|
|
|
|
2015-02-27 01:48:30 +03:00
|
|
|
type LookUpInodeRequest struct {
|
2015-02-27 00:54:16 +03:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2015-02-27 01:48:30 +03:00
|
|
|
type LookUpInodeResponse struct {
|
2015-02-27 00:54:16 +03:00
|
|
|
// The ID of the child inode. The file system must ensure that the returned
|
2015-02-27 01:42:36 +03:00
|
|
|
// inode ID remains valid until a later call to ForgetInode.
|
2015-02-27 00:54:16 +03:00
|
|
|
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 ttributes for the child inode.
|
|
|
|
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
|
2015-02-27 01:48:30 +03:00
|
|
|
// user-space LookUpInode method. However the latter may be slow, so it
|
|
|
|
// caches the entries until the time defined by this field.
|
2015-02-27 00:54:16 +03:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
EntryExpiration time.Time
|
|
|
|
}
|
|
|
|
|
2015-02-27 05:32:04 +03:00
|
|
|
type GetInodeAttributesRequest struct {
|
|
|
|
// The inode of interest.
|
|
|
|
Inode InodeID
|
|
|
|
}
|
|
|
|
|
|
|
|
type GetInodeAttributesResponse struct {
|
|
|
|
// Attributes for the inode, and the time at which they should expire. See
|
|
|
|
// notes on LookUpInodeResponse.AttributesExpiration for more.
|
|
|
|
Attributes InodeAttributes
|
|
|
|
AttributesExpiration time.Time
|
|
|
|
}
|
|
|
|
|
2015-02-27 01:42:36 +03:00
|
|
|
type ForgetInodeRequest struct {
|
2015-02-27 00:54:16 +03:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2015-02-27 01:42:36 +03:00
|
|
|
type ForgetInodeResponse struct {
|
2015-02-27 00:54:16 +03:00
|
|
|
}
|
2015-02-27 01:47:32 +03:00
|
|
|
|
|
|
|
type OpenDirRequest struct {
|
|
|
|
// 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).
|
|
|
|
//
|
2015-02-27 02:43:01 +03:00
|
|
|
// The handle may be supplied to the following methods:
|
|
|
|
//
|
|
|
|
// * ReadDir
|
2015-02-27 05:29:02 +03:00
|
|
|
// * ReleaseDirHandle
|
2015-02-27 02:43:01 +03:00
|
|
|
//
|
2015-02-27 01:47:32 +03:00
|
|
|
// The file system must ensure this ID remains valid until a later call to
|
2015-02-27 05:29:02 +03:00
|
|
|
// ReleaseDirHandle.
|
2015-02-27 01:47:32 +03:00
|
|
|
Handle HandleID
|
|
|
|
}
|
2015-02-27 01:52:34 +03:00
|
|
|
|
2015-02-27 02:43:01 +03:00
|
|
|
type ReadDirRequest struct {
|
|
|
|
// 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.
|
|
|
|
//
|
2015-02-27 03:01:07 +03:00
|
|
|
// 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
|
2015-02-27 02:43:01 +03:00
|
|
|
|
2015-02-27 03:11:12 +03:00
|
|
|
// The maximum number of bytes to return in ReadDirResponse.Data. A smaller
|
|
|
|
// number is acceptable.
|
2015-02-27 04:23:32 +03:00
|
|
|
Size int
|
2015-02-27 02:43:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2015-02-27 03:01:07 +03:00
|
|
|
// by parse_dirfile (http://goo.gl/2WUmD2). Use fuseutil.AppendDirent to
|
|
|
|
// generate this data.
|
2015-02-27 02:43:01 +03:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
2015-02-27 03:01:07 +03:00
|
|
|
// 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.
|
2015-02-27 03:11:12 +03:00
|
|
|
//
|
|
|
|
// An empty buffer indicates the end of the directory has been reached.
|
2015-02-27 03:01:07 +03:00
|
|
|
Data []byte
|
2015-02-27 02:43:01 +03:00
|
|
|
}
|
|
|
|
|
2015-02-27 05:29:02 +03:00
|
|
|
type ReleaseDirHandleRequest struct {
|
2015-02-27 01:52:34 +03:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2015-02-27 05:29:02 +03:00
|
|
|
type ReleaseDirHandleResponse struct {
|
2015-02-27 01:52:34 +03:00
|
|
|
}
|