Commit fed41d2f authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

nodefs: split out Operations interface in single-method interfaces

This is the prevalent style for FUSE libraries in Go.
parent cc423d1b
...@@ -28,32 +28,34 @@ ...@@ -28,32 +28,34 @@
// expressed through index-nodes (also known as "inode", see Inode) which // expressed through index-nodes (also known as "inode", see Inode) which
// describe parent/child relation in between nodes and node-ID association. // describe parent/child relation in between nodes and node-ID association.
// //
// A particular filesystem should provide nodes with filesystem // All error reporting must use the syscall.Errno type. The value 0
// operations implemented as defined by Operations interface. When // (`OK`) should be used to indicate success. The method names are
// filesystem is mounted, its root Operations is associated with root // inspired on the system call names, so we have Listxattr rather than
// of the tree, and the tree is further build lazily when nodefs // ListXAttr.
// infrastructure needs to lookup children of nodes to process client //
// requests. For every new Operations, the filesystem infrastructure // A filesystem should provide nodes that embed InodeEmbed, so the
// automatically builds new index node and links it in the filesystem // node comply with the InodeLink interface. When filesystem is
// tree. Operations.Inode() can be used to get particular Inode // mounted, its root InodeEmbed is associated with root of the tree,
// associated with a Operations. // and the tree is further built lazily when nodefs infrastructure
// needs to lookup children of nodes to process client requests.
// //
// The kernel can evict inode data to free up memory. It does so by // The kernel can evict inode data to free up memory. It does so by
// issuing FORGET calls. When a node has no children, and no kernel // issuing FORGET calls. When a node has no children, and no kernel
// references, it is removed from the file system trees. // references, it is removed from the file system trees.
// //
// File system trees can also be constructed in advance. This is done // File system trees can also be constructed in advance. This is done
// by instantiating "persistent" inodes from the Operations.OnAdd // by instantiating "persistent" inodes from the OnAdder
// method. Persistent inodes remain in memory even if the kernel has // implementation. Persistent inodes remain in memory even if the
// forgotten them. See zip_test.go for an example of how to do this. // kernel has forgotten them. See zip_test.go for an example of how
// to do this.
// //
// File systems whose tree structures are on backing storage typically // File systems whose tree structures are on backing storage typically
// discover the file system tree on-demand, and if the kernel is tight // discover the file system tree on-demand, and if the kernel is tight
// on memory, parts of the tree are forgotten again. These file // on memory, parts of the tree are forgotten again. These file
// systems should implement Operations.Lookup instead. The loopback // systems should implement Lookuper instead. The loopback file
// file system created by `NewLoopbackRoot` provides a straightforward // system created by `NewLoopbackRoot` provides a straightforward
// example. // example.
//
package nodefs package nodefs
import ( import (
...@@ -64,15 +66,20 @@ import ( ...@@ -64,15 +66,20 @@ import (
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
) )
// InodeLink provides the machinery to connect Operations (user // InodeLink provides the machinery to connect user defined methods to
// defined methods) to Inode (a node in the filesystem tree). // an Inode (a node in the filesystem tree).
//
// In general, if InodeLink does not implement specific filesystem
// methods, the filesystem will react as if it is a read-only
// filesystem with a predefined tree structure. See zipfs_test.go for
// an example. A example is in zip_test.go
type InodeLink interface { type InodeLink interface {
// populateInode and inode are used by nodefs internally to // populateInode and inode are used by nodefs internally to
// link Inode to a Node. // link Inode to a Node.
// //
// See Inode() for the public API to retrieve an inode from Node. // See Inode() for the public API to retrieve an inode from Node.
inode() *Inode inode() *Inode
init(ops Operations, attr NodeAttr, bridge *rawBridge, persistent bool) init(ops InodeLink, attr NodeAttr, bridge *rawBridge, persistent bool)
// Inode returns the *Inode associated with this Operations // Inode returns the *Inode associated with this Operations
// instance. The identity of the Inode does not change over // instance. The identity of the Inode does not change over
...@@ -81,143 +88,162 @@ type InodeLink interface { ...@@ -81,143 +88,162 @@ type InodeLink interface {
Inode() *Inode Inode() *Inode
} }
// Operations is the interface that implements the filesystem inode. // Statfs implements statistics for the filesystem that holds this
// Each Operations instance must embed OperationStubs. All error // Inode. If not defined, the `out` argument will zeroed with an OK
// reporting must use the syscall.Errno type. The value 0 (`OK`) // result. This is because OSX filesystems must Statfs, or the mount
// should be used to indicate success. The method names are inspired // will not work.
// on the system call names, so we have Listxattr rather than type Statfser interface {
// ListXAttr.
type Operations interface {
InodeLink
// Statfs implements statistics for the filesystem that holds
// this Inode.
Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno
}
// Access should return if the caller can access the file with // Access should return if the caller can access the file with
// the given mode. In this case, the context has data about // the given mode. In this case, the context has data about
// the real UID. For example a root-SUID binary called by user // the real UID. For example a root-SUID binary called by user
// susan gets the UID and GID for susan here. // susan gets the UID and GID for susan here.
//
// If not defined, a default implementation will check traditional
// unix permissions of the Getattr result agains the caller.
type Accesser interface {
Access(ctx context.Context, mask uint32) syscall.Errno Access(ctx context.Context, mask uint32) syscall.Errno
}
// GetAttr reads attributes for an Inode. The library will // GetAttr reads attributes for an Inode. The library will
// ensure that Mode and Ino are set correctly. For regular // ensure that Mode and Ino are set correctly. For regular
// files, Size should be set so it can be read correctly. // files, Size should be set so it can be read correctly.
type Getattrer interface {
Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno
}
// SetAttr sets attributes for an Inode. // SetAttr sets attributes for an Inode.
Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno type Setattrer interface {
Setattr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno
}
// OnAdd is called once this Operations object is attached to // OnAdd is called once this Operations object is attached to
// an Inode. // an Inode.
type OnAdder interface {
OnAdd(ctx context.Context) OnAdd(ctx context.Context)
} }
// XAttrOperations is a collection of methods used to implement extended attributes. // Getxattr should read data for the given attribute into
type XAttrOperations interface { // `dest` and return the number of bytes. If `dest` is too
Operations // small, it should return ERANGE and the size of the attribute.
// If not defined, Getxattr will return ENOATTR.
// GetXAttr should read data for the given attribute into type Getxattrer interface {
// `dest` and return the number of bytes. If `dest` is too
// small, it should return ERANGE and the size of the attribute.
Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno)
}
// SetXAttr should store data for the given attribute. See // Setxattr should store data for the given attribute. See
// setxattr(2) for information about flags. // setxattr(2) for information about flags.
// If not defined, Setxattr will return ENOATTR.
type Setxattrer interface {
Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno Setxattr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno
}
// RemoveXAttr should delete the given attribute. // Removexattr should delete the given attribute.
// If not defined, Removexattr will return ENOATTR.
type Removexattrer interface {
Removexattr(ctx context.Context, attr string) syscall.Errno Removexattr(ctx context.Context, attr string) syscall.Errno
}
// ListXAttr should read all attributes (null terminated) into // Listxattr should read all attributes (null terminated) into
// `dest`. If the `dest` buffer is too small, it should return // `dest`. If the `dest` buffer is too small, it should return ERANGE
// ERANGE and the correct size. // and the correct size. If not defined, return an empty list and
// success.
type Listxattrer interface {
Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno) Listxattr(ctx context.Context, dest []byte) (uint32, syscall.Errno)
} }
// SymlinkOperations holds operations specific to symlinks. // Readlink reads the content of a symlink.
type SymlinkOperations interface { type Readlinker interface {
Operations
// Readlink reads the content of a symlink.
Readlink(ctx context.Context) ([]byte, syscall.Errno) Readlink(ctx context.Context) ([]byte, syscall.Errno)
} }
// FileOperations holds operations that apply to regular files. // Open opens an Inode (of regular file type) for reading. It
type FileOperations interface { // is optional but recommended to return a FileHandle.
Operations type Opener interface {
// Open opens an Inode (of regular file type) for reading. It
// is optional but recommended to return a FileHandle.
Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno)
}
// Reads data from a file. The data should be returned as // Reads data from a file. The data should be returned as
// ReadResult, which may be constructed from the incoming // ReadResult, which may be constructed from the incoming
// `dest` buffer. If the file was opened without FileHandle, // `dest` buffer. If the file was opened without FileHandle,
// the FileHandle argument here is nil. The default // the FileHandle argument here is nil. The default
// implementation forwards to the FileHandle. // implementation forwards to the FileHandle.
type Reader interface {
Read(ctx context.Context, f FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) Read(ctx context.Context, f FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno)
}
// Writes the data into the file handle at given offset. After // Writes the data into the file handle at given offset. After
// returning, the data will be reused and may not referenced. // returning, the data will be reused and may not referenced.
// The default implementation forwards to the FileHandle. // The default implementation forwards to the FileHandle.
type Writer interface {
Write(ctx context.Context, f FileHandle, data []byte, off int64) (written uint32, errno syscall.Errno) Write(ctx context.Context, f FileHandle, data []byte, off int64) (written uint32, errno syscall.Errno)
}
// Fsync is a signal to ensure writes to the Inode are flushed // Fsync is a signal to ensure writes to the Inode are flushed
// to stable storage. The default implementation forwards to the // to stable storage.
// FileHandle. type Fsyncer interface {
Fsync(ctx context.Context, f FileHandle, flags uint32) syscall.Errno Fsync(ctx context.Context, f FileHandle, flags uint32) syscall.Errno
}
// Flush is called for close() call on a file descriptor. In // Flush is called for close() call on a file descriptor. In
// case of duplicated descriptor, it may be called more than // case of duplicated descriptor, it may be called more than
// once for a file. The default implementation forwards to the // once for a file. The default implementation forwards to the
// FileHandle. // FileHandle.
type Flusher interface {
Flush(ctx context.Context, f FileHandle) syscall.Errno Flush(ctx context.Context, f FileHandle) syscall.Errno
}
// This is called to before the file handle is forgotten. The // This is called to before the file handle is forgotten. The
// kernel ingores the return value of this method, // kernel ingores the return value of this method,
// so any cleanup that requires specific synchronization or // so any cleanup that requires specific synchronization or
// could fail with I/O errors should happen in Flush instead. // could fail with I/O errors should happen in Flush instead.
// The default implementation forwards to the FileHandle. // The default implementation forwards to the FileHandle.
type Releaser interface {
Release(ctx context.Context, f FileHandle) syscall.Errno Release(ctx context.Context, f FileHandle) syscall.Errno
}
// Allocate preallocates space for future writes, so they will // Allocate preallocates space for future writes, so they will
// never encounter ESPACE. // never encounter ESPACE.
type Allocater interface {
Allocate(ctx context.Context, f FileHandle, off uint64, size uint64, mode uint32) syscall.Errno Allocate(ctx context.Context, f FileHandle, off uint64, size uint64, mode uint32) syscall.Errno
}
// FGetattr is like Getattr but provides a file handle if available. // CopyFileRange copies data between sections of two files,
Fgetattr(ctx context.Context, f FileHandle, out *fuse.AttrOut) syscall.Errno // without the data having to pass through the calling process.
type CopyFileRanger interface {
// FSetattr is like SetAttr but provides a file handle if available.
Fsetattr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno
// CopyFileRange copies data between sections of two files,
// without the data having to pass through the calling process.
CopyFileRange(ctx context.Context, fhIn FileHandle, CopyFileRange(ctx context.Context, fhIn FileHandle,
offIn uint64, out *Inode, fhOut FileHandle, offOut uint64, offIn uint64, out *Inode, fhOut FileHandle, offOut uint64,
len uint64, flags uint64) (uint32, syscall.Errno) len uint64, flags uint64) (uint32, syscall.Errno)
}
// Lseek is used to implement holes: it should return the // Lseek is used to implement holes: it should return the
// first offset beyond `off` where there is data (SEEK_DATA) // first offset beyond `off` where there is data (SEEK_DATA)
// or where there is a hole (SEEK_HOLE). // or where there is a hole (SEEK_HOLE).
type Lseeker interface {
Lseek(ctx context.Context, f FileHandle, Off uint64, whence uint32) (uint64, syscall.Errno) Lseek(ctx context.Context, f FileHandle, Off uint64, whence uint32) (uint64, syscall.Errno)
} }
// LockOperations are operations for locking regions of regular files. // LockOperations are operations for locking regions of regular files.
type LockOperations interface { // Getlk returns locks that would conflict with the given input
FileOperations // lock. If no locks conflict, the output has type L_UNLCK. See
// fcntl(2) for more information.
// Getlk returns locks that would conflict with the given // If not defined, returns ENOTSUP
// input lock. If no locks conflict, the output has type type Getlker interface {
// L_UNLCK. See fcntl(2) for more information.
Getlk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) syscall.Errno Getlk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) syscall.Errno
}
// Setlk obtains a lock on a file, or fail if the lock could not // Setlk obtains a lock on a file, or fail if the lock could not
// obtained. See fcntl(2) for more information. // obtained. See fcntl(2) for more information. If not defined,
// returns ENOTSUP
type Setlker interface {
Setlk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno Setlk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno
}
// Setlkw obtains a lock on a file, waiting if necessary. See fcntl(2) // Setlkw obtains a lock on a file, waiting if necessary. See fcntl(2)
// for more information. // for more information. If not defined, returns ENOTSUP
type Setlkwer interface {
Setlkw(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno Setlkw(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno
} }
...@@ -237,63 +263,87 @@ type DirStream interface { ...@@ -237,63 +263,87 @@ type DirStream interface {
Close() Close()
} }
// DirOperations are operations for directory nodes in the filesystem. // Lookup should find a direct child of the node by child
type DirOperations interface { // name. If the entry does not exist, it should return ENOENT
Operations // and optionally set a NegativeTimeout in `out`. If it does
// exist, it should return attribute data in `out` and return
// Lookup should find a direct child of the node by child // the Inode for the child. A new inode can be created using
// name. If the entry does not exist, it should return ENOENT // `Inode.NewInode`. The new Inode will be added to the FS
// and optionally set a NegativeTimeout in `out`. If it does // tree automatically if the return status is OK.
// exist, it should return attribute data in `out` and return //
// the Inode for the child. A new inode can be created using // If not defined, we look for an existing child with the given name,
// `Inode.NewInode`. The new Inode will be added to the FS // or returns ENOENT.
// tree automatically if the return status is OK. type Lookuper interface {
Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno)
}
// OpenDir opens a directory Inode for reading its // OpenDir opens a directory Inode for reading its
// contents. The actual reading is driven from ReadDir, so // contents. The actual reading is driven from ReadDir, so
// this method is just for performing sanity/permission // this method is just for performing sanity/permission
// checks. // checks. The default is to return success.
type Opendirer interface {
Opendir(ctx context.Context) syscall.Errno Opendir(ctx context.Context) syscall.Errno
}
// ReadDir opens a stream of directory entries. // ReadDir opens a stream of directory entries.
//
// The default ReadDir returns the list of currently known children
// from the tree
type Readdirer interface {
Readdir(ctx context.Context) (DirStream, syscall.Errno) Readdir(ctx context.Context) (DirStream, syscall.Errno)
} }
// MutableDirOperations are operations for directories that can add or // Mkdir is similar to Lookup, but must create a directory entry and Inode.
// remove entries. // Default is to return EROFS.
type MutableDirOperations interface { type Mkdirer interface {
DirOperations
// Mkdir is similar to Lookup, but must create a directory entry and Inode.
Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, syscall.Errno) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, syscall.Errno)
}
// Mknod is similar to Lookup, but must create a device entry and Inode. // Mknod is similar to Lookup, but must create a device entry and Inode.
// Default is to return EROFS.
type Mknoder interface {
Mknod(ctx context.Context, name string, mode uint32, dev uint32, out *fuse.EntryOut) (*Inode, syscall.Errno) Mknod(ctx context.Context, name string, mode uint32, dev uint32, out *fuse.EntryOut) (*Inode, syscall.Errno)
}
// Link is similar to Lookup, but must create a new link to an existing Inode. // Link is similar to Lookup, but must create a new link to an existing Inode.
Link(ctx context.Context, target Operations, name string, out *fuse.EntryOut) (node *Inode, errno syscall.Errno) // Default is to return EROFS.
type Linker interface {
Link(ctx context.Context, target InodeLink, name string, out *fuse.EntryOut) (node *Inode, errno syscall.Errno)
}
// Symlink is similar to Lookup, but must create a new symbolic link. // Symlink is similar to Lookup, but must create a new symbolic link.
// Default is to return EROFS.
type Symlinker interface {
Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (node *Inode, errno syscall.Errno) Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (node *Inode, errno syscall.Errno)
}
// Create is similar to Lookup, but should create a new // Create is similar to Lookup, but should create a new
// child. It typically also returns a FileHandle as a // child. It typically also returns a FileHandle as a
// reference for future reads/writes // reference for future reads/writes.
// Default is to return EROFS.
type Creater interface {
Create(ctx context.Context, name string, flags uint32, mode uint32) (node *Inode, fh FileHandle, fuseFlags uint32, errno syscall.Errno) Create(ctx context.Context, name string, flags uint32, mode uint32) (node *Inode, fh FileHandle, fuseFlags uint32, errno syscall.Errno)
}
// Unlink should remove a child from this directory. If the // Unlink should remove a child from this directory. If the
// return status is OK, the Inode is removed as child in the // return status is OK, the Inode is removed as child in the
// FS tree automatically. // FS tree automatically. Default is to return EROFS.
type Unlinker interface {
Unlink(ctx context.Context, name string) syscall.Errno Unlink(ctx context.Context, name string) syscall.Errno
}
// Rmdir is like Unlink but for directories. // Rmdir is like Unlink but for directories.
// Default is to return EROFS.
type Rmdirer interface {
Rmdir(ctx context.Context, name string) syscall.Errno Rmdir(ctx context.Context, name string) syscall.Errno
}
// Rename should move a child from one directory to a // Rename should move a child from one directory to a different
// different one. The changes is effected in the FS tree if // one. The change is effected in the FS tree if the return status is
// the return status is OK // OK.
Rename(ctx context.Context, name string, newParent Operations, newName string, flags uint32) syscall.Errno // Default is to return EROFS.
type Renamer interface {
Rename(ctx context.Context, name string, newParent InodeLink, newName string, flags uint32) syscall.Errno
} }
// FileHandle is a resource identifier for opened files. FileHandles // FileHandle is a resource identifier for opened files. FileHandles
...@@ -307,28 +357,67 @@ type MutableDirOperations interface { ...@@ -307,28 +357,67 @@ type MutableDirOperations interface {
// FileHandle. Files that have such dynamic content should return the // FileHandle. Files that have such dynamic content should return the
// FOPEN_DIRECT_IO flag from their `Open` method. See directio_test.go // FOPEN_DIRECT_IO flag from their `Open` method. See directio_test.go
// for an example. // for an example.
//
// For a description of individual operations, see the equivalent
// operations in FileOperations.
type FileHandle interface { type FileHandle interface {
}
// Release is called when forgetting the file handle. Default is to
// call Release on the Inode.
type FileReleaser interface {
Release(ctx context.Context) syscall.Errno
}
// See Getattrer. Default is to call Getattr on the Inode
type FileGetattrer interface {
Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno
}
// See Reader. Default is to call Read on the Inode
type FileReader interface {
Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, syscall.Errno)
}
// See Writer. Default is to call Write on the Inode
type FileWriter interface {
Write(ctx context.Context, data []byte, off int64) (written uint32, errno syscall.Errno) Write(ctx context.Context, data []byte, off int64) (written uint32, errno syscall.Errno)
}
// See Getlker. Default is to call Getlk on the Inode
type FileGetlker interface {
Getlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) syscall.Errno Getlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) syscall.Errno
}
// See Setlker. Default is to call Setlk on the Inode
type FileSetlker interface {
Setlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno Setlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno
}
// See Setlkwer. Default is to call Setlkw on the Inode
type FileSetlkwer interface {
Setlkw(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno Setlkw(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) syscall.Errno
}
// See Lseek. Default is to call Lseek on the Inode
type FileLseeker interface {
Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno) Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno)
}
// See Flusher. Default is to call Flush on the Inode
type FileFlusher interface {
Flush(ctx context.Context) syscall.Errno Flush(ctx context.Context) syscall.Errno
}
// See Fsync. Default is to call Fsync on the Inode
type FileFsyncer interface {
Fsync(ctx context.Context, flags uint32) syscall.Errno Fsync(ctx context.Context, flags uint32) syscall.Errno
}
Release(ctx context.Context) syscall.Errno // See Fsync. Default is to call Setattr on the Inode
type FileSetattrer interface {
Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno
Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno
}
// See Allocater. Default is to call Allocate on the Inode
type FileAllocater interface {
Allocate(ctx context.Context, off uint64, size uint64, mode uint32) syscall.Errno Allocate(ctx context.Context, off uint64, size uint64, mode uint32) syscall.Errno
} }
......
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"time" "time"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal"
) )
func errnoToStatus(errno syscall.Errno) fuse.Status { func errnoToStatus(errno syscall.Errno) fuse.Status {
...@@ -48,7 +49,7 @@ type rawBridge struct { ...@@ -48,7 +49,7 @@ type rawBridge struct {
} }
// newInode creates creates new inode pointing to ops. // newInode creates creates new inode pointing to ops.
func (b *rawBridge) newInode(ctx context.Context, ops Operations, id NodeAttr, persistent bool) *Inode { func (b *rawBridge) newInode(ctx context.Context, ops InodeLink, id NodeAttr, persistent bool) *Inode {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
...@@ -93,26 +94,11 @@ func (b *rawBridge) newInode(ctx context.Context, ops Operations, id NodeAttr, p ...@@ -93,26 +94,11 @@ func (b *rawBridge) newInode(ctx context.Context, ops Operations, id NodeAttr, p
id.Mode = fuse.S_IFREG id.Mode = fuse.S_IFREG
} }
switch id.Mode {
case fuse.S_IFDIR:
_ = ops.(DirOperations)
case fuse.S_IFLNK:
_ = ops.(SymlinkOperations)
case fuse.S_IFREG:
_ = ops.(FileOperations)
case fuse.S_IFIFO, syscall.S_IFSOCK:
// no type check necessary: FIFO and SOCK don't go
// through FUSE for open/read etc.
break
default:
// Remaining types are char and block devices. Not
// sure how those would work in FUSE
log.Panicf("filetype %o unimplemented", id.Mode)
}
b.nodes[id.Ino] = ops.inode() b.nodes[id.Ino] = ops.inode()
ops.init(ops, id, b, persistent) ops.init(ops, id, b, persistent)
ops.OnAdd(ctx) if oa, ok := ops.(OnAdder); ok {
oa.OnAdd(ctx)
}
return ops.inode() return ops.inode()
} }
...@@ -155,7 +141,7 @@ func (b *rawBridge) setAttrTimeout(out *fuse.AttrOut) { ...@@ -155,7 +141,7 @@ func (b *rawBridge) setAttrTimeout(out *fuse.AttrOut) {
// NewNodeFS creates a node based filesystem based on an Operations // NewNodeFS creates a node based filesystem based on an Operations
// instance for the root. // instance for the root.
func NewNodeFS(root DirOperations, opts *Options) fuse.RawFileSystem { func NewNodeFS(root InodeLink, opts *Options) fuse.RawFileSystem {
bridge := &rawBridge{ bridge := &rawBridge{
automaticIno: opts.FirstAutomaticIno, automaticIno: opts.FirstAutomaticIno,
} }
...@@ -192,7 +178,9 @@ func NewNodeFS(root DirOperations, opts *Options) fuse.RawFileSystem { ...@@ -192,7 +178,9 @@ func NewNodeFS(root DirOperations, opts *Options) fuse.RawFileSystem {
// Fh 0 means no file handle. // Fh 0 means no file handle.
bridge.files = []*fileEntry{{}} bridge.files = []*fileEntry{{}}
root.OnAdd(context.Background()) if oa, ok := root.(OnAdder); ok {
oa.OnAdd(context.Background())
}
return bridge return bridge
} }
...@@ -213,8 +201,9 @@ func (b *rawBridge) inode(id uint64, fh uint64) (*Inode, *fileEntry) { ...@@ -213,8 +201,9 @@ func (b *rawBridge) inode(id uint64, fh uint64) (*Inode, *fileEntry) {
func (b *rawBridge) Lookup(cancel <-chan struct{}, header *fuse.InHeader, name string, out *fuse.EntryOut) fuse.Status { func (b *rawBridge) Lookup(cancel <-chan struct{}, header *fuse.InHeader, name string, out *fuse.EntryOut) fuse.Status {
parent, _ := b.inode(header.NodeId, 0) parent, _ := b.inode(header.NodeId, 0)
ctx := &fuse.Context{Caller: header.Caller, Cancel: cancel}
child, errno := b.lookup(ctx, parent, name, out)
child, errno := parent.dirOps().Lookup(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name, out)
if errno != 0 { if errno != 0 {
if b.options.NegativeTimeout != nil && out.EntryTimeout() == 0 { if b.options.NegativeTimeout != nil && out.EntryTimeout() == 0 {
out.SetEntryTimeout(*b.options.NegativeTimeout) out.SetEntryTimeout(*b.options.NegativeTimeout)
...@@ -226,13 +215,35 @@ func (b *rawBridge) Lookup(cancel <-chan struct{}, header *fuse.InHeader, name s ...@@ -226,13 +215,35 @@ func (b *rawBridge) Lookup(cancel <-chan struct{}, header *fuse.InHeader, name s
b.setEntryOutTimeout(out) b.setEntryOutTimeout(out)
out.Mode = child.nodeAttr.Mode | (out.Mode & 07777) out.Mode = child.nodeAttr.Mode | (out.Mode & 07777)
return fuse.OK return fuse.OK
} }
func (b *rawBridge) lookup(ctx *fuse.Context, parent *Inode, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
if lu, ok := parent.ops.(Lookuper); ok {
return lu.Lookup(ctx, name, out)
}
child := parent.GetChild(name)
if child == nil {
return nil, syscall.ENOENT
}
if ga, ok := child.ops.(Getattrer); ok {
var a fuse.AttrOut
errno := ga.Getattr(ctx, &a)
if errno == 0 {
out.Attr = a.Attr
}
}
return child, OK
}
func (b *rawBridge) Rmdir(cancel <-chan struct{}, header *fuse.InHeader, name string) fuse.Status { func (b *rawBridge) Rmdir(cancel <-chan struct{}, header *fuse.InHeader, name string) fuse.Status {
parent, _ := b.inode(header.NodeId, 0) parent, _ := b.inode(header.NodeId, 0)
var errno syscall.Errno var errno syscall.Errno
if mops, ok := parent.ops.(MutableDirOperations); ok { if mops, ok := parent.ops.(Rmdirer); ok {
errno = mops.Rmdir(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name) errno = mops.Rmdir(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
} }
...@@ -245,7 +256,7 @@ func (b *rawBridge) Rmdir(cancel <-chan struct{}, header *fuse.InHeader, name st ...@@ -245,7 +256,7 @@ func (b *rawBridge) Rmdir(cancel <-chan struct{}, header *fuse.InHeader, name st
func (b *rawBridge) Unlink(cancel <-chan struct{}, header *fuse.InHeader, name string) fuse.Status { func (b *rawBridge) Unlink(cancel <-chan struct{}, header *fuse.InHeader, name string) fuse.Status {
parent, _ := b.inode(header.NodeId, 0) parent, _ := b.inode(header.NodeId, 0)
var errno syscall.Errno var errno syscall.Errno
if mops, ok := parent.ops.(MutableDirOperations); ok { if mops, ok := parent.ops.(Unlinker); ok {
errno = mops.Unlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name) errno = mops.Unlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, name)
} }
...@@ -260,7 +271,7 @@ func (b *rawBridge) Mkdir(cancel <-chan struct{}, input *fuse.MkdirIn, name stri ...@@ -260,7 +271,7 @@ func (b *rawBridge) Mkdir(cancel <-chan struct{}, input *fuse.MkdirIn, name stri
var child *Inode var child *Inode
var errno syscall.Errno var errno syscall.Errno
if mops, ok := parent.ops.(MutableDirOperations); ok { if mops, ok := parent.ops.(Mkdirer); ok {
child, errno = mops.Mkdir(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, out) child, errno = mops.Mkdir(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, out)
} }
...@@ -282,7 +293,7 @@ func (b *rawBridge) Mknod(cancel <-chan struct{}, input *fuse.MknodIn, name stri ...@@ -282,7 +293,7 @@ func (b *rawBridge) Mknod(cancel <-chan struct{}, input *fuse.MknodIn, name stri
var child *Inode var child *Inode
var errno syscall.Errno var errno syscall.Errno
if mops, ok := parent.ops.(MutableDirOperations); ok { if mops, ok := parent.ops.(Mknoder); ok {
child, errno = mops.Mknod(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, input.Rdev, out) child, errno = mops.Mknod(&fuse.Context{Caller: input.Caller, Cancel: cancel}, name, input.Mode, input.Rdev, out)
} }
...@@ -303,7 +314,7 @@ func (b *rawBridge) Create(cancel <-chan struct{}, input *fuse.CreateIn, name st ...@@ -303,7 +314,7 @@ func (b *rawBridge) Create(cancel <-chan struct{}, input *fuse.CreateIn, name st
var errno syscall.Errno var errno syscall.Errno
var f FileHandle var f FileHandle
var flags uint32 var flags uint32
if mops, ok := parent.ops.(MutableDirOperations); ok { if mops, ok := parent.ops.(Creater); ok {
child, f, flags, errno = mops.Create(ctx, name, input.Flags, input.Mode) child, f, flags, errno = mops.Create(ctx, name, input.Flags, input.Mode)
} }
...@@ -315,20 +326,17 @@ func (b *rawBridge) Create(cancel <-chan struct{}, input *fuse.CreateIn, name st ...@@ -315,20 +326,17 @@ func (b *rawBridge) Create(cancel <-chan struct{}, input *fuse.CreateIn, name st
} }
out.Fh = uint64(b.addNewChild(parent, name, child, f, input.Flags|syscall.O_CREAT, &out.EntryOut)) out.Fh = uint64(b.addNewChild(parent, name, child, f, input.Flags|syscall.O_CREAT, &out.EntryOut))
b.setEntryOutTimeout(&out.EntryOut)
out.OpenFlags = flags out.OpenFlags = flags
var temp fuse.AttrOut var temp fuse.AttrOut
f.Getattr(ctx, &temp) b.getattr(ctx, child, f, &temp)
out.Attr = temp.Attr out.Attr = temp.Attr
out.AttrValid = temp.AttrValid out.AttrValid = temp.AttrValid
out.AttrValidNsec = temp.AttrValidNsec out.AttrValidNsec = temp.AttrValidNsec
out.Attr.Ino = child.nodeAttr.Ino
out.Generation = child.nodeAttr.Gen
out.NodeId = child.nodeAttr.Ino
b.setEntryOutTimeout(&out.EntryOut) b.setEntryOutTimeout(&out.EntryOut)
out.NodeId = child.nodeAttr.Ino
out.Mode = (out.Attr.Mode & 07777) | child.nodeAttr.Mode out.Mode = (out.Attr.Mode & 07777) | child.nodeAttr.Mode
return fuse.OK return fuse.OK
} }
...@@ -342,31 +350,46 @@ func (b *rawBridge) SetDebug(debug bool) {} ...@@ -342,31 +350,46 @@ func (b *rawBridge) SetDebug(debug bool) {}
func (b *rawBridge) GetAttr(cancel <-chan struct{}, input *fuse.GetAttrIn, out *fuse.AttrOut) fuse.Status { func (b *rawBridge) GetAttr(cancel <-chan struct{}, input *fuse.GetAttrIn, out *fuse.AttrOut) fuse.Status {
n, fEntry := b.inode(input.NodeId, input.Fh()) n, fEntry := b.inode(input.NodeId, input.Fh())
f := fEntry.file
if f == nil {
// The linux kernel doesnt pass along the file
// descriptor, so we have to fake it here.
// See https://github.com/libfuse/libfuse/issues/62
b.mu.Lock()
for _, fh := range n.openFiles {
f = b.files[fh].file
b.files[fh].wg.Add(1)
defer b.files[fh].wg.Done()
break
}
b.mu.Unlock()
}
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel} ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
if fops, ok := n.ops.(FileOperations); ok { return errnoToStatus(b.getattr(ctx, n, f, out))
}
f := fEntry.file func (b *rawBridge) getattr(ctx context.Context, n *Inode, f FileHandle, out *fuse.AttrOut) syscall.Errno {
if input.Flags()&fuse.FUSE_GETATTR_FH == 0 { var errno syscall.Errno
// The linux kernel doesnt pass along the file
// descriptor, so we have to fake it here. var fg FileGetattrer
// See https://github.com/libfuse/libfuse/issues/62 if f != nil {
b.mu.Lock() fg, _ = f.(FileGetattrer)
for _, fh := range n.openFiles { }
f = b.files[fh].file
b.files[fh].wg.Add(1)
defer b.files[fh].wg.Done()
break
}
b.mu.Unlock()
}
errno := fops.Fgetattr(ctx, f, out) if fg != nil {
errno = fg.Getattr(ctx, out)
} else if fops, ok := n.ops.(Getattrer); ok {
errno = fops.Getattr(ctx, out)
} else {
// We set Mode below, which is the minimum for success
}
if errno == 0 {
b.setAttrTimeout(out) b.setAttrTimeout(out)
out.Ino = input.NodeId out.Ino = n.nodeAttr.Ino
out.Mode = (out.Attr.Mode & 07777) | n.nodeAttr.Mode out.Mode = (out.Attr.Mode & 07777) | n.nodeAttr.Mode
return errnoToStatus(errno)
} }
return errnoToStatus(n.ops.Getattr(ctx, out)) return errno
} }
func (b *rawBridge) SetAttr(cancel <-chan struct{}, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status { func (b *rawBridge) SetAttr(cancel <-chan struct{}, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status {
...@@ -378,18 +401,22 @@ func (b *rawBridge) SetAttr(cancel <-chan struct{}, in *fuse.SetAttrIn, out *fus ...@@ -378,18 +401,22 @@ func (b *rawBridge) SetAttr(cancel <-chan struct{}, in *fuse.SetAttrIn, out *fus
f = nil f = nil
} }
if fops, ok := n.ops.(FileOperations); ok { if fops, ok := f.(FileSetattrer); ok {
return errnoToStatus(fops.Fsetattr(ctx, f, in, out)) return errnoToStatus(fops.Setattr(ctx, in, out))
} }
return errnoToStatus(n.ops.Setattr(ctx, in, out)) if fops, ok := n.ops.(Setattrer); ok {
return errnoToStatus(fops.Setattr(ctx, f, in, out))
}
return fuse.ENOTSUP
} }
func (b *rawBridge) Rename(cancel <-chan struct{}, input *fuse.RenameIn, oldName string, newName string) fuse.Status { func (b *rawBridge) Rename(cancel <-chan struct{}, input *fuse.RenameIn, oldName string, newName string) fuse.Status {
p1, _ := b.inode(input.NodeId, 0) p1, _ := b.inode(input.NodeId, 0)
p2, _ := b.inode(input.Newdir, 0) p2, _ := b.inode(input.Newdir, 0)
if mops, ok := p1.ops.(MutableDirOperations); ok { if mops, ok := p1.ops.(Renamer); ok {
errno := mops.Rename(&fuse.Context{Caller: input.Caller, Cancel: cancel}, oldName, p2.ops, newName, input.Flags) errno := mops.Rename(&fuse.Context{Caller: input.Caller, Cancel: cancel}, oldName, p2.ops, newName, input.Flags)
if errno == 0 { if errno == 0 {
if input.Flags&RENAME_EXCHANGE != 0 { if input.Flags&RENAME_EXCHANGE != 0 {
...@@ -408,7 +435,7 @@ func (b *rawBridge) Link(cancel <-chan struct{}, input *fuse.LinkIn, name string ...@@ -408,7 +435,7 @@ func (b *rawBridge) Link(cancel <-chan struct{}, input *fuse.LinkIn, name string
parent, _ := b.inode(input.NodeId, 0) parent, _ := b.inode(input.NodeId, 0)
target, _ := b.inode(input.Oldnodeid, 0) target, _ := b.inode(input.Oldnodeid, 0)
if mops, ok := parent.ops.(MutableDirOperations); ok { if mops, ok := parent.ops.(Linker); ok {
child, errno := mops.Link(&fuse.Context{Caller: input.Caller, Cancel: cancel}, target.ops, name, out) child, errno := mops.Link(&fuse.Context{Caller: input.Caller, Cancel: cancel}, target.ops, name, out)
if errno != 0 { if errno != 0 {
return errnoToStatus(errno) return errnoToStatus(errno)
...@@ -424,7 +451,7 @@ func (b *rawBridge) Link(cancel <-chan struct{}, input *fuse.LinkIn, name string ...@@ -424,7 +451,7 @@ func (b *rawBridge) Link(cancel <-chan struct{}, input *fuse.LinkIn, name string
func (b *rawBridge) Symlink(cancel <-chan struct{}, header *fuse.InHeader, target string, name string, out *fuse.EntryOut) fuse.Status { func (b *rawBridge) Symlink(cancel <-chan struct{}, header *fuse.InHeader, target string, name string, out *fuse.EntryOut) fuse.Status {
parent, _ := b.inode(header.NodeId, 0) parent, _ := b.inode(header.NodeId, 0)
if mops, ok := parent.ops.(MutableDirOperations); ok { if mops, ok := parent.ops.(Symlinker); ok {
child, status := mops.Symlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, target, name, out) child, status := mops.Symlink(&fuse.Context{Caller: header.Caller, Cancel: cancel}, target, name, out)
if status != 0 { if status != 0 {
return errnoToStatus(status) return errnoToStatus(status)
...@@ -439,17 +466,45 @@ func (b *rawBridge) Symlink(cancel <-chan struct{}, header *fuse.InHeader, targe ...@@ -439,17 +466,45 @@ func (b *rawBridge) Symlink(cancel <-chan struct{}, header *fuse.InHeader, targe
func (b *rawBridge) Readlink(cancel <-chan struct{}, header *fuse.InHeader) (out []byte, status fuse.Status) { func (b *rawBridge) Readlink(cancel <-chan struct{}, header *fuse.InHeader) (out []byte, status fuse.Status) {
n, _ := b.inode(header.NodeId, 0) n, _ := b.inode(header.NodeId, 0)
result, errno := n.linkOps().Readlink(&fuse.Context{Caller: header.Caller, Cancel: cancel})
if errno != 0 { if linker, ok := n.ops.(Readlinker); ok {
return nil, errnoToStatus(errno) result, errno := linker.Readlink(&fuse.Context{Caller: header.Caller, Cancel: cancel})
if errno != 0 {
return nil, errnoToStatus(errno)
}
return result, fuse.OK
} }
return result, fuse.OK return nil, fuse.ENOTSUP
} }
func (b *rawBridge) Access(cancel <-chan struct{}, input *fuse.AccessIn) fuse.Status { func (b *rawBridge) Access(cancel <-chan struct{}, input *fuse.AccessIn) fuse.Status {
n, _ := b.inode(input.NodeId, 0) n, _ := b.inode(input.NodeId, 0)
return errnoToStatus(n.ops.Access(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Mask))
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
if a, ok := n.ops.(Accesser); ok {
return errnoToStatus(a.Access(ctx, input.Mask))
}
// default: check attributes.
caller := input.Caller
ga, ok := n.ops.(Getattrer)
if !ok {
return fuse.EACCES
}
var out fuse.AttrOut
if s := ga.Getattr(ctx, &out); s != 0 {
return errnoToStatus(s)
}
if !internal.HasAccess(caller.Uid, caller.Gid, out.Uid, out.Gid, out.Mode, input.Mask) {
return fuse.EACCES
}
return fuse.OK
} }
// Extended attributes. // Extended attributes.
...@@ -457,53 +512,58 @@ func (b *rawBridge) Access(cancel <-chan struct{}, input *fuse.AccessIn) fuse.St ...@@ -457,53 +512,58 @@ func (b *rawBridge) Access(cancel <-chan struct{}, input *fuse.AccessIn) fuse.St
func (b *rawBridge) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string, data []byte) (uint32, fuse.Status) { func (b *rawBridge) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string, data []byte) (uint32, fuse.Status) {
n, _ := b.inode(header.NodeId, 0) n, _ := b.inode(header.NodeId, 0)
if xops, ok := n.ops.(XAttrOperations); ok { if xops, ok := n.ops.(Getxattrer); ok {
nb, errno := xops.Getxattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr, data) nb, errno := xops.Getxattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr, data)
return nb, errnoToStatus(errno) return nb, errnoToStatus(errno)
} }
return 0, fuse.ENOTSUP return 0, fuse.ENOATTR
} }
func (b *rawBridge) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader, dest []byte) (sz uint32, status fuse.Status) { func (b *rawBridge) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader, dest []byte) (sz uint32, status fuse.Status) {
n, _ := b.inode(header.NodeId, 0) n, _ := b.inode(header.NodeId, 0)
if xops, ok := n.ops.(XAttrOperations); ok { if xops, ok := n.ops.(Listxattrer); ok {
sz, errno := xops.Listxattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, dest) sz, errno := xops.Listxattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, dest)
return sz, errnoToStatus(errno) return sz, errnoToStatus(errno)
} }
return 0, fuse.ENOTSUP return 0, fuse.OK
} }
func (b *rawBridge) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, attr string, data []byte) fuse.Status { func (b *rawBridge) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, attr string, data []byte) fuse.Status {
n, _ := b.inode(input.NodeId, 0) n, _ := b.inode(input.NodeId, 0)
if xops, ok := n.ops.(XAttrOperations); ok { if xops, ok := n.ops.(Setxattrer); ok {
return errnoToStatus(xops.Setxattr(&fuse.Context{Caller: input.Caller, Cancel: cancel}, attr, data, input.Flags)) return errnoToStatus(xops.Setxattr(&fuse.Context{Caller: input.Caller, Cancel: cancel}, attr, data, input.Flags))
} }
return fuse.ENOTSUP return fuse.ENOATTR
} }
func (b *rawBridge) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string) fuse.Status { func (b *rawBridge) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string) fuse.Status {
n, _ := b.inode(header.NodeId, 0) n, _ := b.inode(header.NodeId, 0)
if xops, ok := n.ops.(XAttrOperations); ok { if xops, ok := n.ops.(Removexattrer); ok {
return errnoToStatus(xops.Removexattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr)) return errnoToStatus(xops.Removexattr(&fuse.Context{Caller: header.Caller, Cancel: cancel}, attr))
} }
return fuse.ENOTSUP return fuse.ENOATTR
} }
func (b *rawBridge) Open(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) fuse.Status { func (b *rawBridge) Open(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) fuse.Status {
n, _ := b.inode(input.NodeId, 0) n, _ := b.inode(input.NodeId, 0)
f, flags, errno := n.fileOps().Open(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Flags)
if errno != 0 {
return errnoToStatus(errno)
}
if f != nil { if op, ok := n.ops.(Opener); ok {
b.mu.Lock() f, flags, errno := op.Open(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Flags)
defer b.mu.Unlock() if errno != 0 {
out.Fh = uint64(b.registerFile(n, f, input.Flags)) return errnoToStatus(errno)
}
if f != nil {
b.mu.Lock()
defer b.mu.Unlock()
out.Fh = uint64(b.registerFile(n, f, input.Flags))
}
out.OpenFlags = flags
return fuse.OK
} }
out.OpenFlags = flags
return fuse.OK return fuse.ENOTSUP
} }
// registerFile hands out a file handle. Must have bridge.mu // registerFile hands out a file handle. Must have bridge.mu
...@@ -528,14 +588,26 @@ func (b *rawBridge) registerFile(n *Inode, f FileHandle, flags uint32) uint32 { ...@@ -528,14 +588,26 @@ func (b *rawBridge) registerFile(n *Inode, f FileHandle, flags uint32) uint32 {
func (b *rawBridge) Read(cancel <-chan struct{}, input *fuse.ReadIn, buf []byte) (fuse.ReadResult, fuse.Status) { func (b *rawBridge) Read(cancel <-chan struct{}, input *fuse.ReadIn, buf []byte) (fuse.ReadResult, fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
res, errno := n.fileOps().Read(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, buf, int64(input.Offset))
return res, errnoToStatus(errno) if fr, ok := f.file.(FileReader); ok {
res, errno := fr.Read(&fuse.Context{Caller: input.Caller, Cancel: cancel}, buf, int64(input.Offset))
return res, errnoToStatus(errno)
}
if fops, ok := n.ops.(Reader); ok {
res, errno := fops.Read(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, buf, int64(input.Offset))
return res, errnoToStatus(errno)
}
return nil, fuse.ENOTSUP
} }
func (b *rawBridge) GetLk(cancel <-chan struct{}, input *fuse.LkIn, out *fuse.LkOut) fuse.Status { func (b *rawBridge) GetLk(cancel <-chan struct{}, input *fuse.LkIn, out *fuse.LkOut) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
if lops, ok := n.ops.(LockOperations); ok { if gl, ok := f.file.(FileGetlker); ok {
return errnoToStatus(gl.Getlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Owner, &input.Lk, input.LkFlags, &out.Lk))
}
if lops, ok := n.ops.(Getlker); ok {
return errnoToStatus(lops.Getlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags, &out.Lk)) return errnoToStatus(lops.Getlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags, &out.Lk))
} }
return fuse.ENOTSUP return fuse.ENOTSUP
...@@ -543,14 +615,20 @@ func (b *rawBridge) GetLk(cancel <-chan struct{}, input *fuse.LkIn, out *fuse.Lk ...@@ -543,14 +615,20 @@ func (b *rawBridge) GetLk(cancel <-chan struct{}, input *fuse.LkIn, out *fuse.Lk
func (b *rawBridge) SetLk(cancel <-chan struct{}, input *fuse.LkIn) fuse.Status { func (b *rawBridge) SetLk(cancel <-chan struct{}, input *fuse.LkIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
if lops, ok := n.ops.(LockOperations); ok { if sl, ok := n.ops.(FileSetlker); ok {
return errnoToStatus(sl.Setlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Owner, &input.Lk, input.LkFlags))
}
if lops, ok := n.ops.(Setlker); ok {
return errnoToStatus(lops.Setlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags)) return errnoToStatus(lops.Setlk(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags))
} }
return fuse.ENOTSUP return fuse.ENOTSUP
} }
func (b *rawBridge) SetLkw(cancel <-chan struct{}, input *fuse.LkIn) fuse.Status { func (b *rawBridge) SetLkw(cancel <-chan struct{}, input *fuse.LkIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
if lops, ok := n.ops.(LockOperations); ok { if sl, ok := n.ops.(FileSetlkwer); ok {
return errnoToStatus(sl.Setlkw(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Owner, &input.Lk, input.LkFlags))
}
if lops, ok := n.ops.(Setlkwer); ok {
return errnoToStatus(lops.Setlkw(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags)) return errnoToStatus(lops.Setlkw(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Owner, &input.Lk, input.LkFlags))
} }
return fuse.ENOTSUP return fuse.ENOTSUP
...@@ -563,7 +641,12 @@ func (b *rawBridge) Release(cancel <-chan struct{}, input *fuse.ReleaseIn) { ...@@ -563,7 +641,12 @@ func (b *rawBridge) Release(cancel <-chan struct{}, input *fuse.ReleaseIn) {
} }
f.wg.Wait() f.wg.Wait()
n.fileOps().Release(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file)
if r, ok := n.ops.(Releaser); ok {
r.Release(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file)
} else if r, ok := f.file.(FileReleaser); ok {
r.Release(&fuse.Context{Caller: input.Caller, Cancel: cancel})
}
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
...@@ -604,44 +687,75 @@ func (b *rawBridge) releaseFileEntry(nid uint64, fh uint64) (*Inode, *fileEntry) ...@@ -604,44 +687,75 @@ func (b *rawBridge) releaseFileEntry(nid uint64, fh uint64) (*Inode, *fileEntry)
func (b *rawBridge) Write(cancel <-chan struct{}, input *fuse.WriteIn, data []byte) (written uint32, status fuse.Status) { func (b *rawBridge) Write(cancel <-chan struct{}, input *fuse.WriteIn, data []byte) (written uint32, status fuse.Status) {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
w, errno := n.fileOps().Write(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, data, int64(input.Offset)) if fr, ok := f.file.(FileWriter); ok {
return w, errnoToStatus(errno) w, errno := fr.Write(&fuse.Context{Caller: input.Caller, Cancel: cancel}, data, int64(input.Offset))
return w, errnoToStatus(errno)
}
if wr, ok := n.ops.(Writer); ok {
w, errno := wr.Write(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, data, int64(input.Offset))
return w, errnoToStatus(errno)
}
return 0, fuse.ENOTSUP
} }
func (b *rawBridge) Flush(cancel <-chan struct{}, input *fuse.FlushIn) fuse.Status { func (b *rawBridge) Flush(cancel <-chan struct{}, input *fuse.FlushIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
return errnoToStatus(n.fileOps().Flush(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file)) if fl, ok := f.file.(FileFlusher); ok {
return errnoToStatus(fl.Flush(&fuse.Context{Caller: input.Caller, Cancel: cancel}))
}
if fl, ok := n.ops.(Flusher); ok {
return errnoToStatus(fl.Flush(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file))
}
return fuse.ENOTSUP
} }
func (b *rawBridge) Fsync(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.Status { func (b *rawBridge) Fsync(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
return errnoToStatus(n.fileOps().Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.FsyncFlags)) if fs, ok := f.file.(FileFsyncer); ok {
return errnoToStatus(fs.Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.FsyncFlags))
}
if fs, ok := n.ops.(Fsyncer); ok {
return errnoToStatus(fs.Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.FsyncFlags))
}
return fuse.ENOTSUP
} }
func (b *rawBridge) Fallocate(cancel <-chan struct{}, input *fuse.FallocateIn) fuse.Status { func (b *rawBridge) Fallocate(cancel <-chan struct{}, input *fuse.FallocateIn) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
return errnoToStatus(n.fileOps().Allocate(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Offset, input.Length, input.Mode)) if a, ok := n.ops.(FileAllocater); ok {
return errnoToStatus(a.Allocate(&fuse.Context{Caller: input.Caller, Cancel: cancel}, input.Offset, input.Length, input.Mode))
}
if a, ok := n.ops.(Allocater); ok {
return errnoToStatus(a.Allocate(&fuse.Context{Caller: input.Caller, Cancel: cancel}, f.file, input.Offset, input.Length, input.Mode))
}
return fuse.ENOTSUP
} }
func (b *rawBridge) OpenDir(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) fuse.Status { func (b *rawBridge) OpenDir(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.OpenOut) fuse.Status {
n, _ := b.inode(input.NodeId, 0) n, _ := b.inode(input.NodeId, 0)
errno := n.dirOps().Opendir(&fuse.Context{Caller: input.Caller, Cancel: cancel})
if errno != 0 { if od, ok := n.ops.(Opendirer); ok {
return errnoToStatus(errno) errno := od.Opendir(&fuse.Context{Caller: input.Caller, Cancel: cancel})
if errno != 0 {
return errnoToStatus(errno)
}
} }
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
out.Fh = uint64(b.registerFile(n, nil, 0)) out.Fh = uint64(b.registerFile(n, nil, 0))
return fuse.OK return fuse.OK
} }
func (b *rawBridge) getStream(cancel <-chan struct{}, input *fuse.ReadIn, inode *Inode, f *fileEntry) syscall.Errno { func (b *rawBridge) setStream(cancel <-chan struct{}, input *fuse.ReadIn, inode *Inode, f *fileEntry) syscall.Errno {
if f.dirStream == nil || input.Offset == 0 { if f.dirStream == nil || input.Offset == 0 {
if f.dirStream != nil { if f.dirStream != nil {
f.dirStream.Close() f.dirStream.Close()
f.dirStream = nil f.dirStream = nil
} }
str, errno := inode.dirOps().Readdir(&fuse.Context{Caller: input.Caller, Cancel: cancel}) str, errno := b.getStream(&fuse.Context{Caller: input.Caller, Cancel: cancel}, inode)
if errno != 0 { if errno != 0 {
return errno return errno
} }
...@@ -653,10 +767,25 @@ func (b *rawBridge) getStream(cancel <-chan struct{}, input *fuse.ReadIn, inode ...@@ -653,10 +767,25 @@ func (b *rawBridge) getStream(cancel <-chan struct{}, input *fuse.ReadIn, inode
return 0 return 0
} }
func (b *rawBridge) getStream(ctx context.Context, inode *Inode) (DirStream, syscall.Errno) {
rd, ok := inode.ops.(Readdirer)
if !ok {
r := []fuse.DirEntry{}
for k, ch := range inode.Children() {
r = append(r, fuse.DirEntry{Mode: ch.Mode(),
Name: k,
Ino: ch.NodeAttr().Ino})
}
return NewListDirStream(r), 0
}
return rd.Readdir(ctx)
}
func (b *rawBridge) ReadDir(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) fuse.Status { func (b *rawBridge) ReadDir(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
if errno := b.getStream(cancel, input, n, f); errno != 0 { if errno := b.setStream(cancel, input, n, f); errno != 0 {
return errnoToStatus(errno) return errnoToStatus(errno)
} }
...@@ -685,10 +814,10 @@ func (b *rawBridge) ReadDir(cancel <-chan struct{}, input *fuse.ReadIn, out *fus ...@@ -685,10 +814,10 @@ func (b *rawBridge) ReadDir(cancel <-chan struct{}, input *fuse.ReadIn, out *fus
func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) fuse.Status { func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out *fuse.DirEntryList) fuse.Status {
n, f := b.inode(input.NodeId, input.Fh) n, f := b.inode(input.NodeId, input.Fh)
if errno := b.getStream(cancel, input, n, f); errno != 0 { if errno := b.setStream(cancel, input, n, f); errno != 0 {
return errnoToStatus(errno) return errnoToStatus(errno)
} }
ctx := &fuse.Context{Caller: input.Caller, Cancel: cancel}
for f.dirStream.HasNext() { for f.dirStream.HasNext() {
var e fuse.DirEntry var e fuse.DirEntry
var errno syscall.Errno var errno syscall.Errno
...@@ -710,7 +839,7 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out ...@@ -710,7 +839,7 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out
return fuse.OK return fuse.OK
} }
child, errno := n.dirOps().Lookup(&fuse.Context{Caller: input.Caller, Cancel: cancel}, e.Name, entryOut) child, errno := b.lookup(ctx, n, e.Name, entryOut)
if errno != 0 { if errno != 0 {
if b.options.NegativeTimeout != nil { if b.options.NegativeTimeout != nil {
entryOut.SetEntryTimeout(*b.options.NegativeTimeout) entryOut.SetEntryTimeout(*b.options.NegativeTimeout)
...@@ -732,12 +861,21 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out ...@@ -732,12 +861,21 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out
func (b *rawBridge) FsyncDir(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.Status { func (b *rawBridge) FsyncDir(cancel <-chan struct{}, input *fuse.FsyncIn) fuse.Status {
n, _ := b.inode(input.NodeId, input.Fh) n, _ := b.inode(input.NodeId, input.Fh)
return errnoToStatus(n.fileOps().Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, nil, input.FsyncFlags)) if fs, ok := n.ops.(Fsyncer); ok {
return errnoToStatus(fs.Fsync(&fuse.Context{Caller: input.Caller, Cancel: cancel}, nil, input.FsyncFlags))
}
return fuse.ENOTSUP
} }
func (b *rawBridge) StatFs(cancel <-chan struct{}, input *fuse.InHeader, out *fuse.StatfsOut) fuse.Status { func (b *rawBridge) StatFs(cancel <-chan struct{}, input *fuse.InHeader, out *fuse.StatfsOut) fuse.Status {
n, _ := b.inode(input.NodeId, 0) n, _ := b.inode(input.NodeId, 0)
return errnoToStatus(n.ops.Statfs(&fuse.Context{Caller: input.Caller, Cancel: cancel}, out)) if sf, ok := n.ops.(Statfser); ok {
return errnoToStatus(sf.Statfs(&fuse.Context{Caller: input.Caller, Cancel: cancel}, out))
}
// leave zeroed out
return fuse.OK
} }
func (b *rawBridge) Init(s *fuse.Server) { func (b *rawBridge) Init(s *fuse.Server) {
...@@ -746,9 +884,14 @@ func (b *rawBridge) Init(s *fuse.Server) { ...@@ -746,9 +884,14 @@ func (b *rawBridge) Init(s *fuse.Server) {
func (b *rawBridge) CopyFileRange(cancel <-chan struct{}, in *fuse.CopyFileRangeIn) (size uint32, status fuse.Status) { func (b *rawBridge) CopyFileRange(cancel <-chan struct{}, in *fuse.CopyFileRangeIn) (size uint32, status fuse.Status) {
n1, f1 := b.inode(in.NodeId, in.FhIn) n1, f1 := b.inode(in.NodeId, in.FhIn)
cfr, ok := n1.ops.(CopyFileRanger)
if !ok {
return 0, fuse.ENOTSUP
}
n2, f2 := b.inode(in.NodeIdOut, in.FhOut) n2, f2 := b.inode(in.NodeIdOut, in.FhOut)
sz, errno := n1.fileOps().CopyFileRange(&fuse.Context{Caller: in.Caller, Cancel: cancel}, sz, errno := cfr.CopyFileRange(&fuse.Context{Caller: in.Caller, Cancel: cancel},
f1.file, in.OffIn, n2, f2.file, in.OffOut, in.Len, in.Flags) f1.file, in.OffIn, n2, f2.file, in.OffOut, in.Len, in.Flags)
return sz, errnoToStatus(errno) return sz, errnoToStatus(errno)
} }
...@@ -756,8 +899,24 @@ func (b *rawBridge) CopyFileRange(cancel <-chan struct{}, in *fuse.CopyFileRange ...@@ -756,8 +899,24 @@ func (b *rawBridge) CopyFileRange(cancel <-chan struct{}, in *fuse.CopyFileRange
func (b *rawBridge) Lseek(cancel <-chan struct{}, in *fuse.LseekIn, out *fuse.LseekOut) fuse.Status { func (b *rawBridge) Lseek(cancel <-chan struct{}, in *fuse.LseekIn, out *fuse.LseekOut) fuse.Status {
n, f := b.inode(in.NodeId, in.Fh) n, f := b.inode(in.NodeId, in.Fh)
off, errno := n.fileOps().Lseek(&fuse.Context{Caller: in.Caller, Cancel: cancel}, if fs, ok := f.file.(FileLseeker); ok {
f.file, in.Offset, in.Whence) off, errno := fs.Lseek(&fuse.Context{Caller: in.Caller, Cancel: cancel}, in.Offset, in.Whence)
out.Offset = off out.Offset = off
return errnoToStatus(errno) return errnoToStatus(errno)
}
ls, ok := n.ops.(Lseeker)
if ok {
off, errno := ls.Lseek(&fuse.Context{Caller: in.Caller, Cancel: cancel},
f.file, in.Offset, in.Whence)
out.Offset = off
return errnoToStatus(errno)
}
if in.Whence == _SEEK_DATA || in.Whence == _SEEK_HOLE {
out.Offset = in.Offset
return fuse.OK
}
return fuse.ENOTSUP
} }
...@@ -19,7 +19,7 @@ import ( ...@@ -19,7 +19,7 @@ import (
) )
type keepCacheFile struct { type keepCacheFile struct {
OperationStubs InodeEmbed
keepCache bool keepCache bool
mu sync.Mutex mu sync.Mutex
...@@ -61,12 +61,18 @@ func (f *keepCacheFile) Read(ctx context.Context, fh FileHandle, dest []byte, of ...@@ -61,12 +61,18 @@ func (f *keepCacheFile) Read(ctx context.Context, fh FileHandle, dest []byte, of
return fuse.ReadResultData(f.content[off:]), OK return fuse.ReadResultData(f.content[off:]), OK
} }
var _ = (Reader)((*keepCacheFile)(nil))
var _ = (Opener)((*keepCacheFile)(nil))
var _ = (Getattrer)((*keepCacheFile)(nil))
type keepCacheRoot struct { type keepCacheRoot struct {
OperationStubs InodeEmbed
keep, nokeep *keepCacheFile keep, nokeep *keepCacheFile
} }
var _ = (OnAdder)((*keepCacheRoot)(nil))
func (r *keepCacheRoot) OnAdd(ctx context.Context) { func (r *keepCacheRoot) OnAdd(ctx context.Context) {
i := r.Inode() i := r.Inode()
......
...@@ -5,11 +5,7 @@ ...@@ -5,11 +5,7 @@
package nodefs package nodefs
import ( import (
"context"
"syscall"
"github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal"
) )
// InodeEmbed embeds the Inode into a filesystem node. It is the only // InodeEmbed embeds the Inode into a filesystem node. It is the only
...@@ -25,7 +21,7 @@ func (n *InodeEmbed) inode() *Inode { ...@@ -25,7 +21,7 @@ func (n *InodeEmbed) inode() *Inode {
return &n.inode_ return &n.inode_
} }
func (n *InodeEmbed) init(ops Operations, attr NodeAttr, bridge *rawBridge, persistent bool) { func (n *InodeEmbed) init(ops InodeLink, attr NodeAttr, bridge *rawBridge, persistent bool) {
n.inode_ = Inode{ n.inode_ = Inode{
ops: ops, ops: ops,
nodeAttr: attr, nodeAttr: attr,
...@@ -42,332 +38,3 @@ func (n *InodeEmbed) init(ops Operations, attr NodeAttr, bridge *rawBridge, pers ...@@ -42,332 +38,3 @@ func (n *InodeEmbed) init(ops Operations, attr NodeAttr, bridge *rawBridge, pers
func (n *InodeEmbed) Inode() *Inode { func (n *InodeEmbed) Inode() *Inode {
return &n.inode_ return &n.inode_
} }
// OperationStubs provides no-operation default implementations for
// all the XxxOperations interfaces. The stubs provide useful defaults
// for implementing a read-only filesystem whose tree is constructed
// beforehand in the OnAdd method of the root. A example is in
// zip_test.go
//
// It is recommended to embed this in any Operations implementation,
// as it is the means by new operations are supported.
type OperationStubs struct {
InodeEmbed
}
// check that we have implemented all interface methods
var _ DirOperations = &OperationStubs{}
var _ FileOperations = &OperationStubs{}
var _ LockOperations = &OperationStubs{}
// StatFs zeroes the out argument and returns OK. This is because OSX
// filesystems must define this, or the mount will not work.
func (n *OperationStubs) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno {
// this should be defined on OSX, or the FS won't mount
*out = fuse.StatfsOut{}
return OK
}
// The default OnAdd does nothing.
func (n *OperationStubs) OnAdd(ctx context.Context) {
}
// GetAttr zeroes out argument and returns OK.
func (n *OperationStubs) Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno {
*out = fuse.AttrOut{}
return OK
}
func (n *OperationStubs) Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
return syscall.EROFS
}
// The Access default implementation checks traditional unix
// permissions of the GetAttr result agains the caller.
func (n *OperationStubs) Access(ctx context.Context, mask uint32) syscall.Errno {
caller, ok := fuse.FromContext(ctx)
if !ok {
return syscall.EINVAL
}
var out fuse.AttrOut
if s := n.inode().Operations().Getattr(ctx, &out); s != 0 {
return s
}
if !internal.HasAccess(caller.Uid, caller.Gid, out.Uid, out.Gid, out.Mode, mask) {
return syscall.EACCES
}
return OK
}
// FSetAttr delegates to the FileHandle's if f is not nil, or else to the
// Inode's SetAttr method.
func (n *OperationStubs) Fsetattr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
if f != nil {
return f.Setattr(ctx, in, out)
}
return n.inode_.Operations().Setattr(ctx, in, out)
}
// The Lookup method on the OperationStubs type looks for an
// existing child with the given name, or returns ENOENT.
func (n *OperationStubs) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
ch := n.inode().GetChild(name)
if ch == nil {
return nil, syscall.ENOENT
}
var a fuse.AttrOut
errno := ch.Operations().Getattr(ctx, &a)
out.Attr = a.Attr
return ch, errno
}
// Mkdir returns EROFS
func (n *OperationStubs) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, syscall.Errno) {
return nil, syscall.EROFS
}
// Mknod returns EROFS
func (n *OperationStubs) Mknod(ctx context.Context, name string, mode uint32, dev uint32, out *fuse.EntryOut) (*Inode, syscall.Errno) {
return nil, syscall.EROFS
}
// Rmdir returns EROFS
func (n *OperationStubs) Rmdir(ctx context.Context, name string) syscall.Errno {
return syscall.EROFS
}
// Unlink returns EROFS
func (n *OperationStubs) Unlink(ctx context.Context, name string) syscall.Errno {
return syscall.EROFS
}
// The default OpenDir always succeeds
func (n *OperationStubs) Opendir(ctx context.Context) syscall.Errno {
return OK
}
// The default ReadDir returns the list of children from the tree
func (n *OperationStubs) Readdir(ctx context.Context) (DirStream, syscall.Errno) {
r := []fuse.DirEntry{}
for k, ch := range n.inode().Children() {
r = append(r, fuse.DirEntry{Mode: ch.Mode(),
Name: k,
Ino: ch.NodeAttr().Ino})
}
return NewListDirStream(r), 0
}
// Rename returns EROFS
func (n *OperationStubs) Rename(ctx context.Context, name string, newParent Operations, newName string, flags uint32) syscall.Errno {
return syscall.EROFS
}
// Read delegates to the FileHandle argument.
func (n *OperationStubs) Read(ctx context.Context, f FileHandle, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
if f != nil {
return f.Read(ctx, dest, off)
}
return nil, syscall.ENOTSUP
}
// Symlink returns EROFS
func (n *OperationStubs) Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (node *Inode, errno syscall.Errno) {
return nil, syscall.EROFS
}
// Readlink return ENOTSUP
func (n *OperationStubs) Readlink(ctx context.Context) ([]byte, syscall.Errno) {
return nil, syscall.ENOTSUP
}
// Fsync delegates to the FileHandle
func (n *OperationStubs) Fsync(ctx context.Context, f FileHandle, flags uint32) syscall.Errno {
if f != nil {
return f.Fsync(ctx, flags)
}
return syscall.ENOTSUP
}
// Write delegates to the FileHandle
func (n *OperationStubs) Write(ctx context.Context, f FileHandle, data []byte, off int64) (written uint32, errno syscall.Errno) {
if f != nil {
return f.Write(ctx, data, off)
}
return 0, syscall.EROFS
}
func (n *OperationStubs) CopyFileRange(ctx context.Context, fhIn FileHandle,
offIn uint64, out *Inode, fhOut FileHandle, offOut uint64,
len uint64, flags uint64) (uint32, syscall.Errno) {
return 0, syscall.EROFS
}
// Lseek is called for seeking to and beyond holes. By default, it
// returns the input offset unchanged.
func (n *OperationStubs) Lseek(ctx context.Context, f FileHandle, off uint64, whence uint32) (uint64, syscall.Errno) {
if f != nil {
return f.Lseek(ctx, off, whence)
}
if whence == _SEEK_DATA || whence == _SEEK_HOLE {
return off, OK
}
return 0, syscall.ENOTSUP
}
// Getlk delegates to the FileHandlef
func (n *OperationStubs) Getlk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (errno syscall.Errno) {
if f != nil {
return f.Getlk(ctx, owner, lk, flags, out)
}
return syscall.ENOTSUP
}
// SetLk delegates to the FileHandle
func (n *OperationStubs) Setlk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) (errno syscall.Errno) {
if f != nil {
return f.Setlk(ctx, owner, lk, flags)
}
return syscall.ENOTSUP
}
// SetLkw delegates to the FileHandle
func (n *OperationStubs) Setlkw(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32) (errno syscall.Errno) {
if f != nil {
return f.Setlkw(ctx, owner, lk, flags)
}
return syscall.ENOTSUP
}
// Flush delegates to the FileHandle
func (n *OperationStubs) Flush(ctx context.Context, f FileHandle) syscall.Errno {
if f != nil {
return f.Flush(ctx)
}
return syscall.ENOTSUP
}
// Release delegates to the FileHandle
func (n *OperationStubs) Release(ctx context.Context, f FileHandle) syscall.Errno {
if f != nil {
return f.Release(ctx)
}
return OK
}
// Allocate delegates to the FileHandle
func (n *OperationStubs) Allocate(ctx context.Context, f FileHandle, off uint64, size uint64, mode uint32) (errno syscall.Errno) {
if f != nil {
return f.Allocate(ctx, off, size, mode)
}
return syscall.ENOTSUP
}
// Fgetattr delegates to the FileHandle's if f is not nil, or else to the
// Inode's GetAttr method.
func (n *OperationStubs) Fgetattr(ctx context.Context, f FileHandle, out *fuse.AttrOut) syscall.Errno {
if f != nil {
f.Getattr(ctx, out)
}
return n.inode_.ops.Getattr(ctx, out)
}
// Open returns ENOTSUP
func (n *OperationStubs) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno) {
return nil, 0, syscall.ENOTSUP
}
// Create returns ENOTSUP
func (n *OperationStubs) Create(ctx context.Context, name string, flags uint32, mode uint32) (node *Inode, fh FileHandle, fuseFlags uint32, errno syscall.Errno) {
return nil, nil, 0, syscall.EROFS
}
// Link returns ENOTSUP
func (n *OperationStubs) Link(ctx context.Context, target Operations, name string, out *fuse.EntryOut) (node *Inode, errno syscall.Errno) {
return nil, syscall.EROFS
}
// The default GetXAttr returns ENOATTR
func (n *OperationStubs) GetXAttr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) {
return 0, ENOATTR
}
// The default SetXAttr returns ENOATTR
func (n *OperationStubs) SetXAttr(ctx context.Context, attr string, data []byte, flags uint32) syscall.Errno {
return syscall.EROFS
}
// The default RemoveXAttr returns ENOATTR
func (n *OperationStubs) RemoveXAttr(ctx context.Context, attr string) syscall.Errno {
return ENOATTR
}
// The default RemoveXAttr returns an empty list
func (n *OperationStubs) ListXAttr(ctx context.Context, dest []byte) (uint32, syscall.Errno) {
return 0, OK
}
// FileHandleStubs satisfies the FileHandle interface, and provides
// stub methods that return ENOTSUP for all operations.
type FileHandleStubs struct {
}
var _ = FileHandle((*FileHandleStubs)(nil))
func (f *FileHandleStubs) Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) {
return nil, syscall.ENOTSUP
}
func (f *FileHandleStubs) Write(ctx context.Context, data []byte, off int64) (written uint32, errno syscall.Errno) {
return 0, syscall.ENOTSUP
}
func (f *FileHandleStubs) Getlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (errno syscall.Errno) {
return syscall.ENOTSUP
}
func (f *FileHandleStubs) Setlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) (errno syscall.Errno) {
return syscall.ENOTSUP
}
func (f *FileHandleStubs) Setlkw(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32) (errno syscall.Errno) {
return syscall.ENOTSUP
}
func (f *FileHandleStubs) Flush(ctx context.Context) syscall.Errno {
return syscall.ENOTSUP
}
func (f *FileHandleStubs) Release(ctx context.Context) syscall.Errno {
return syscall.ENOTSUP
}
func (f *FileHandleStubs) Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno {
return syscall.ENOTSUP
}
func (f *FileHandleStubs) Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno {
return syscall.ENOTSUP
}
func (f *FileHandleStubs) Allocate(ctx context.Context, off uint64, size uint64, mode uint32) (errno syscall.Errno) {
return syscall.ENOTSUP
}
func (f *FileHandleStubs) Fsync(ctx context.Context, flags uint32) (errno syscall.Errno) {
return syscall.ENOTSUP
}
func (f *FileHandleStubs) Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno) {
return 0, syscall.ENOTSUP
}
...@@ -17,7 +17,7 @@ import ( ...@@ -17,7 +17,7 @@ import (
) )
type dioRoot struct { type dioRoot struct {
OperationStubs InodeEmbed
} }
func (r *dioRoot) OnAdd(ctx context.Context) { func (r *dioRoot) OnAdd(ctx context.Context) {
...@@ -27,9 +27,11 @@ func (r *dioRoot) OnAdd(ctx context.Context) { ...@@ -27,9 +27,11 @@ func (r *dioRoot) OnAdd(ctx context.Context) {
// A file handle that pretends that every hole/data starts at // A file handle that pretends that every hole/data starts at
// multiples of 1024 // multiples of 1024
type dioFH struct { type dioFH struct {
FileHandleStubs
} }
var _ = (FileLseeker)((*dioFH)(nil))
var _ = (FileReader)((*dioFH)(nil))
func (f *dioFH) Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno) { func (f *dioFH) Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno) {
next := (off + 1023) & (^uint64(1023)) next := (off + 1023) & (^uint64(1023))
return next, OK return next, OK
...@@ -42,9 +44,11 @@ func (fh *dioFH) Read(ctx context.Context, data []byte, off int64) (fuse.ReadRes ...@@ -42,9 +44,11 @@ func (fh *dioFH) Read(ctx context.Context, data []byte, off int64) (fuse.ReadRes
// overrides Open so it can return a dioFH file handle // overrides Open so it can return a dioFH file handle
type dioFile struct { type dioFile struct {
OperationStubs InodeEmbed
} }
var _ = (Opener)((*dioFile)(nil))
func (f *dioFile) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno) { func (f *dioFile) Open(ctx context.Context, flags uint32) (fh FileHandle, fuseFlags uint32, errno syscall.Errno) {
return &dioFH{}, fuse.FOPEN_DIRECT_IO, OK return &dioFH{}, fuse.FOPEN_DIRECT_IO, OK
} }
......
...@@ -24,6 +24,20 @@ type loopbackFile struct { ...@@ -24,6 +24,20 @@ type loopbackFile struct {
fd int fd int
} }
var _ = (FileHandle)((*loopbackFile)(nil))
var _ = (FileReleaser)((*loopbackFile)(nil))
var _ = (FileGetattrer)((*loopbackFile)(nil))
var _ = (FileReader)((*loopbackFile)(nil))
var _ = (FileWriter)((*loopbackFile)(nil))
var _ = (FileGetlker)((*loopbackFile)(nil))
var _ = (FileSetlker)((*loopbackFile)(nil))
var _ = (FileSetlkwer)((*loopbackFile)(nil))
var _ = (FileLseeker)((*loopbackFile)(nil))
var _ = (FileFlusher)((*loopbackFile)(nil))
var _ = (FileFsyncer)((*loopbackFile)(nil))
var _ = (FileSetattrer)((*loopbackFile)(nil))
var _ = (FileAllocater)((*loopbackFile)(nil))
func (f *loopbackFile) Read(ctx context.Context, buf []byte, off int64) (res fuse.ReadResult, errno syscall.Errno) { func (f *loopbackFile) Read(ctx context.Context, buf []byte, off int64) (res fuse.ReadResult, errno syscall.Errno) {
r := fuse.ReadResultFd(uintptr(f.fd), off, len(buf)) r := fuse.ReadResultFd(uintptr(f.fd), off, len(buf))
return r, OK return r, OK
......
...@@ -53,7 +53,7 @@ func (i *NodeAttr) Reserved() bool { ...@@ -53,7 +53,7 @@ func (i *NodeAttr) Reserved() bool {
type Inode struct { type Inode struct {
nodeAttr NodeAttr nodeAttr NodeAttr
ops Operations ops InodeLink
bridge *rawBridge bridge *rawBridge
// Following data is mutable. // Following data is mutable.
...@@ -90,18 +90,6 @@ type Inode struct { ...@@ -90,18 +90,6 @@ type Inode struct {
parents map[parentData]struct{} parents map[parentData]struct{}
} }
func (n *Inode) dirOps() DirOperations {
return n.ops.(DirOperations)
}
func (n *Inode) fileOps() FileOperations {
return n.ops.(FileOperations)
}
func (n *Inode) linkOps() SymlinkOperations {
return n.ops.(SymlinkOperations)
}
// NodeAttr returns the (Ino, Gen) tuple for this node. // NodeAttr returns the (Ino, Gen) tuple for this node.
func (n *Inode) NodeAttr() NodeAttr { func (n *Inode) NodeAttr() NodeAttr {
return n.nodeAttr return n.nodeAttr
...@@ -214,7 +202,7 @@ func (n *Inode) Forgotten() bool { ...@@ -214,7 +202,7 @@ func (n *Inode) Forgotten() bool {
// Operations returns the object implementing the file system // Operations returns the object implementing the file system
// operations. // operations.
func (n *Inode) Operations() Operations { func (n *Inode) Operations() InodeLink {
return n.ops return n.ops
} }
...@@ -273,7 +261,7 @@ func (iparent *Inode) setEntry(name string, ichild *Inode) { ...@@ -273,7 +261,7 @@ func (iparent *Inode) setEntry(name string, ichild *Inode) {
// NewPersistentInode returns an Inode whose lifetime is not in // NewPersistentInode returns an Inode whose lifetime is not in
// control of the kernel. // control of the kernel.
func (n *Inode) NewPersistentInode(ctx context.Context, node Operations, id NodeAttr) *Inode { func (n *Inode) NewPersistentInode(ctx context.Context, node InodeLink, id NodeAttr) *Inode {
return n.newInode(ctx, node, id, true) return n.newInode(ctx, node, id, true)
} }
...@@ -284,16 +272,16 @@ func (n *Inode) ForgetPersistent() { ...@@ -284,16 +272,16 @@ func (n *Inode) ForgetPersistent() {
n.removeRef(0, true) n.removeRef(0, true)
} }
// NewInode returns an inode for the given Operations. The mode should // NewInode returns an inode for the given InodeLink. The mode should
// be standard mode argument (eg. S_IFDIR). The inode number in id.Ino // be standard mode argument (eg. S_IFDIR). The inode number in id.Ino
// argument is used to implement hard-links. If it is given, and // argument is used to implement hard-links. If it is given, and
// another node with the same ID is known, that will node will be // another node with the same ID is known, that will node will be
// returned, and the passed-in `node` is ignored. // returned, and the passed-in `node` is ignored.
func (n *Inode) NewInode(ctx context.Context, ops Operations, id NodeAttr) *Inode { func (n *Inode) NewInode(ctx context.Context, ops InodeLink, id NodeAttr) *Inode {
return n.newInode(ctx, ops, id, false) return n.newInode(ctx, ops, id, false)
} }
func (n *Inode) newInode(ctx context.Context, ops Operations, id NodeAttr, persistent bool) *Inode { func (n *Inode) newInode(ctx context.Context, ops InodeLink, id NodeAttr, persistent bool) *Inode {
return n.bridge.newInode(ctx, ops, id, persistent) return n.bridge.newInode(ctx, ops, id, persistent)
} }
......
...@@ -17,14 +17,11 @@ import ( ...@@ -17,14 +17,11 @@ import (
) )
type interruptRoot struct { type interruptRoot struct {
OperationStubs InodeEmbed
child interruptOps child interruptOps
} }
type interruptOps struct { var _ = (Lookuper)((*interruptRoot)(nil))
OperationStubs
interrupted bool
}
func (r *interruptRoot) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) { func (r *interruptRoot) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
if name != "file" { if name != "file" {
...@@ -37,6 +34,13 @@ func (r *interruptRoot) Lookup(ctx context.Context, name string, out *fuse.Entry ...@@ -37,6 +34,13 @@ func (r *interruptRoot) Lookup(ctx context.Context, name string, out *fuse.Entry
return ch, OK return ch, OK
} }
type interruptOps struct {
InodeEmbed
interrupted bool
}
var _ = (Opener)((*interruptOps)(nil))
func (o *interruptOps) Open(ctx context.Context, flags uint32) (FileHandle, uint32, syscall.Errno) { func (o *interruptOps) Open(ctx context.Context, flags uint32) (FileHandle, uint32, syscall.Errno) {
select { select {
case <-time.After(100 * time.Millisecond): case <-time.After(100 * time.Millisecond):
......
...@@ -6,7 +6,6 @@ package nodefs ...@@ -6,7 +6,6 @@ package nodefs
import ( import (
"context" "context"
"log"
"os" "os"
"path/filepath" "path/filepath"
"syscall" "syscall"
...@@ -21,6 +20,27 @@ type loopbackRoot struct { ...@@ -21,6 +20,27 @@ type loopbackRoot struct {
rootDev uint64 rootDev uint64
} }
var _ = (Statfser)((*loopbackNode)(nil))
var _ = (Getattrer)((*loopbackNode)(nil))
var _ = (Getxattrer)((*loopbackNode)(nil))
var _ = (Setxattrer)((*loopbackNode)(nil))
var _ = (Removexattrer)((*loopbackNode)(nil))
var _ = (Listxattrer)((*loopbackNode)(nil))
var _ = (Readlinker)((*loopbackNode)(nil))
var _ = (Opener)((*loopbackNode)(nil))
var _ = (CopyFileRanger)((*loopbackNode)(nil))
var _ = (Lookuper)((*loopbackNode)(nil))
var _ = (Opendirer)((*loopbackNode)(nil))
var _ = (Readdirer)((*loopbackNode)(nil))
var _ = (Mkdirer)((*loopbackNode)(nil))
var _ = (Mknoder)((*loopbackNode)(nil))
var _ = (Linker)((*loopbackNode)(nil))
var _ = (Symlinker)((*loopbackNode)(nil))
var _ = (Creater)((*loopbackNode)(nil))
var _ = (Unlinker)((*loopbackNode)(nil))
var _ = (Rmdirer)((*loopbackNode)(nil))
var _ = (Renamer)((*loopbackNode)(nil))
func (n *loopbackNode) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno { func (n *loopbackNode) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno {
s := syscall.Statfs_t{} s := syscall.Statfs_t{}
err := syscall.Statfs(n.path(), &s) err := syscall.Statfs(n.path(), &s)
...@@ -32,7 +52,6 @@ func (n *loopbackNode) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall. ...@@ -32,7 +52,6 @@ func (n *loopbackNode) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.
} }
func (n *loopbackRoot) Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno { func (n *loopbackRoot) Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno {
log.Println("getattr")
st := syscall.Stat_t{} st := syscall.Stat_t{}
err := syscall.Stat(n.rootPath, &st) err := syscall.Stat(n.rootPath, &st)
if err != nil { if err != nil {
...@@ -43,7 +62,7 @@ func (n *loopbackRoot) Getattr(ctx context.Context, out *fuse.AttrOut) syscall.E ...@@ -43,7 +62,7 @@ func (n *loopbackRoot) Getattr(ctx context.Context, out *fuse.AttrOut) syscall.E
} }
type loopbackNode struct { type loopbackNode struct {
OperationStubs InodeEmbed
} }
func (n *loopbackNode) root() *loopbackRoot { func (n *loopbackNode) root() *loopbackRoot {
...@@ -122,14 +141,14 @@ func (n *loopbackNode) Unlink(ctx context.Context, name string) syscall.Errno { ...@@ -122,14 +141,14 @@ func (n *loopbackNode) Unlink(ctx context.Context, name string) syscall.Errno {
return ToErrno(err) return ToErrno(err)
} }
func toLoopbackNode(op Operations) *loopbackNode { func toLoopbackNode(op InodeLink) *loopbackNode {
if r, ok := op.(*loopbackRoot); ok { if r, ok := op.(*loopbackRoot); ok {
return &r.loopbackNode return &r.loopbackNode
} }
return op.(*loopbackNode) return op.(*loopbackNode)
} }
func (n *loopbackNode) Rename(ctx context.Context, name string, newParent Operations, newName string, flags uint32) syscall.Errno { func (n *loopbackNode) Rename(ctx context.Context, name string, newParent InodeLink, newName string, flags uint32) syscall.Errno {
newParentLoopback := toLoopbackNode(newParent) newParentLoopback := toLoopbackNode(newParent)
if flags&RENAME_EXCHANGE != 0 { if flags&RENAME_EXCHANGE != 0 {
return n.renameExchange(name, newParentLoopback, newName) return n.renameExchange(name, newParentLoopback, newName)
...@@ -199,7 +218,7 @@ func (n *loopbackNode) Symlink(ctx context.Context, target, name string, out *fu ...@@ -199,7 +218,7 @@ func (n *loopbackNode) Symlink(ctx context.Context, target, name string, out *fu
return ch, 0 return ch, 0
} }
func (n *loopbackNode) Link(ctx context.Context, target Operations, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) { func (n *loopbackNode) Link(ctx context.Context, target InodeLink, name string, out *fuse.EntryOut) (*Inode, syscall.Errno) {
p := filepath.Join(n.path(), name) p := filepath.Join(n.path(), name)
targetNode := toLoopbackNode(target) targetNode := toLoopbackNode(target)
...@@ -258,11 +277,7 @@ func (n *loopbackNode) Readdir(ctx context.Context) (DirStream, syscall.Errno) { ...@@ -258,11 +277,7 @@ func (n *loopbackNode) Readdir(ctx context.Context) (DirStream, syscall.Errno) {
return NewLoopbackDirStream(n.path()) return NewLoopbackDirStream(n.path())
} }
func (n *loopbackNode) Fgetattr(ctx context.Context, f FileHandle, out *fuse.AttrOut) syscall.Errno { func (n *loopbackNode) Getattr(ctx context.Context, out *fuse.AttrOut) syscall.Errno {
if f != nil {
return f.Getattr(ctx, out)
}
p := n.path() p := n.path()
var err error = nil var err error = nil
...@@ -277,7 +292,7 @@ func (n *loopbackNode) Fgetattr(ctx context.Context, f FileHandle, out *fuse.Att ...@@ -277,7 +292,7 @@ func (n *loopbackNode) Fgetattr(ctx context.Context, f FileHandle, out *fuse.Att
// NewLoopback returns a root node for a loopback file system whose // NewLoopback returns a root node for a loopback file system whose
// root is at the given root. // root is at the given root.
func NewLoopbackRoot(root string) (DirOperations, error) { func NewLoopbackRoot(root string) (InodeLink, error) {
var st syscall.Stat_t var st syscall.Stat_t
err := syscall.Stat(root, &st) err := syscall.Stat(root, &st)
if err != nil { if err != nil {
......
...@@ -14,7 +14,7 @@ import ( ...@@ -14,7 +14,7 @@ import (
// requests. This is a convenience wrapper around NewNodeFS and // requests. This is a convenience wrapper around NewNodeFS and
// fuse.NewServer. If nil is given as options, default settings are // fuse.NewServer. If nil is given as options, default settings are
// applied, which are 1 second entry and attribute timeout. // applied, which are 1 second entry and attribute timeout.
func Mount(dir string, root DirOperations, options *Options) (*fuse.Server, error) { func Mount(dir string, root InodeLink, options *Options) (*fuse.Server, error) {
if options == nil { if options == nil {
oneSec := time.Second oneSec := time.Second
options = &Options{ options = &Options{
......
...@@ -28,7 +28,7 @@ type testCase struct { ...@@ -28,7 +28,7 @@ type testCase struct {
origDir string origDir string
mntDir string mntDir string
loopback DirOperations loopback InodeLink
rawFS fuse.RawFileSystem rawFS fuse.RawFileSystem
server *fuse.Server server *fuse.Server
} }
......
...@@ -106,14 +106,15 @@ func TestZipFS(t *testing.T) { ...@@ -106,14 +106,15 @@ func TestZipFS(t *testing.T) {
// zipFile is a file read from a zip archive. // zipFile is a file read from a zip archive.
type zipFile struct { type zipFile struct {
OperationStubs InodeEmbed
file *zip.File file *zip.File
mu sync.Mutex mu sync.Mutex
data []byte data []byte
} }
var _ = (FileOperations)((*zipFile)(nil)) var _ = (Opener)((*zipFile)(nil))
var _ = (Getattrer)((*zipFile)(nil))
// Getattr sets the minimum, which is the size. A more full-featured // Getattr sets the minimum, which is the size. A more full-featured
// FS would also set timestamps and permissions. // FS would also set timestamps and permissions.
...@@ -157,11 +158,13 @@ func (zf *zipFile) Read(ctx context.Context, f FileHandle, dest []byte, off int6 ...@@ -157,11 +158,13 @@ func (zf *zipFile) Read(ctx context.Context, f FileHandle, dest []byte, off int6
// zipRoot is the root of the Zip filesystem. Its only functionality // zipRoot is the root of the Zip filesystem. Its only functionality
// is populating the filesystem. // is populating the filesystem.
type zipRoot struct { type zipRoot struct {
OperationStubs InodeEmbed
r *zip.Reader r *zip.Reader
} }
var _ = (OnAdder)((*zipRoot)(nil))
func (zr *zipRoot) OnAdd(ctx context.Context) { func (zr *zipRoot) OnAdd(ctx context.Context) {
// OnAdd is called once we are attached to an Inode. We can // OnAdd is called once we are attached to an Inode. We can
// then construct a tree. We construct the entire tree, and // then construct a tree. We construct the entire tree, and
...@@ -177,7 +180,7 @@ func (zr *zipRoot) OnAdd(ctx context.Context) { ...@@ -177,7 +180,7 @@ func (zr *zipRoot) OnAdd(ctx context.Context) {
} }
ch := p.GetChild(component) ch := p.GetChild(component)
if ch == nil { if ch == nil {
ch = p.NewPersistentInode(ctx, &OperationStubs{}, ch = p.NewPersistentInode(ctx, &InodeEmbed{},
NodeAttr{Mode: fuse.S_IFDIR}) NodeAttr{Mode: fuse.S_IFDIR})
p.AddChild(component, ch, true) p.AddChild(component, ch, true)
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment