Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
go-fuse
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
go-fuse
Commits
fed41d2f
Commit
fed41d2f
authored
Mar 29, 2019
by
Han-Wen Nienhuys
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
nodefs: split out Operations interface in single-method interfaces
This is the prevalent style for FUSE libraries in Go.
parent
cc423d1b
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
586 additions
and
637 deletions
+586
-637
nodefs/api.go
nodefs/api.go
+239
-150
nodefs/bridge.go
nodefs/bridge.go
+266
-107
nodefs/cache_test.go
nodefs/cache_test.go
+8
-2
nodefs/default.go
nodefs/default.go
+1
-334
nodefs/directio_test.go
nodefs/directio_test.go
+7
-3
nodefs/files.go
nodefs/files.go
+14
-0
nodefs/inode.go
nodefs/inode.go
+6
-18
nodefs/interrupt_test.go
nodefs/interrupt_test.go
+9
-5
nodefs/loopback.go
nodefs/loopback.go
+27
-12
nodefs/mount.go
nodefs/mount.go
+1
-1
nodefs/simple_test.go
nodefs/simple_test.go
+1
-1
nodefs/zip_test.go
nodefs/zip_test.go
+7
-4
No files found.
nodefs/api.go
View file @
fed41d2f
...
...
@@ -28,32 +28,34 @@
// expressed through index-nodes (also known as "inode", see Inode) which
// describe parent/child relation in between nodes and node-ID association.
//
// A particular filesystem should provide nodes with filesystem
// operations implemented as defined by Operations interface. When
// filesystem is mounted, its root Operations is associated with root
// of the tree, and the tree is further build lazily when nodefs
// infrastructure needs to lookup children of nodes to process client
// requests. For every new Operations, the filesystem infrastructure
// automatically builds new index node and links it in the filesystem
// tree. Operations.Inode() can be used to get particular Inode
// associated with a Operations.
// All error reporting must use the syscall.Errno type. The value 0
// (`OK`) should be used to indicate success. The method names are
// inspired on the system call names, so we have Listxattr rather than
// ListXAttr.
//
// A filesystem should provide nodes that embed InodeEmbed, so the
// node comply with the InodeLink interface. When filesystem is
// mounted, its root InodeEmbed is associated with root of the tree,
// 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
// issuing FORGET calls. When a node has no children, and no kernel
// references, it is removed from the file system trees.
//
// File system trees can also be constructed in advance. This is done
// by instantiating "persistent" inodes from the Operations.OnAdd
// method. Persistent inodes remain in memory even if the kernel has
// forgotten them. See zip_test.go for an example of how to do this.
// by instantiating "persistent" inodes from the OnAdder
// implementation. Persistent inodes remain in memory even if the
// 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
// discover the file system tree on-demand, and if the kernel is tight
// on memory, parts of the tree are forgotten again. These file
// systems should implement
Operations.Lookup instead. The loopback
//
file
system created by `NewLoopbackRoot` provides a straightforward
// systems should implement
Lookuper instead. The loopback file
// system created by `NewLoopbackRoot` provides a straightforward
// example.
//
package
nodefs
import
(
...
...
@@ -64,15 +66,20 @@ import (
"github.com/hanwen/go-fuse/fuse"
)
// InodeLink provides the machinery to connect Operations (user
// defined methods) to Inode (a node in the filesystem tree).
// InodeLink provides the machinery to connect user defined methods to
// 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
{
// populateInode and inode are used by nodefs internally to
// link Inode to a Node.
//
// See Inode() for the public API to retrieve an inode from Node.
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
// instance. The identity of the Inode does not change over
...
...
@@ -81,143 +88,162 @@ type InodeLink interface {
Inode
()
*
Inode
}
// Operations is the interface that implements the filesystem inode.
// Each Operations instance must embed OperationStubs. All error
// reporting must use the syscall.Errno type. The value 0 (`OK`)
// should be used to indicate success. The method names are inspired
// on the system call names, so we have Listxattr rather than
// ListXAttr.
type
Operations
interface
{
InodeLink
// Statfs implements statistics for the filesystem that holds
// this Inode.
// Statfs implements statistics for the filesystem that holds this
// Inode. If not defined, the `out` argument will zeroed with an OK
// result. This is because OSX filesystems must Statfs, or the mount
// will not work.
type
Statfser
interface
{
Statfs
(
ctx
context
.
Context
,
out
*
fuse
.
StatfsOut
)
syscall
.
Errno
}
// Access should return if the caller can access the file with
// the given mode. In this case, the context has data about
// the real UID. For example a root-SUID binary called by user
// susan gets the UID and GID for susan here.
// Access should return if the caller can access the file with
// the given mode. In this case, the context has data about
// the real UID. For example a root-SUID binary called by user
// 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
}
// GetAttr reads attributes for an Inode. The library will
// ensure that Mode and Ino are set correctly. For regular
// files, Size should be set so it can be read correctly.
// GetAttr reads attributes for an Inode. The library will
// ensure that Mode and Ino are set correctly. For regular
// files, Size should be set so it can be read correctly.
type
Getattrer
interface
{
Getattr
(
ctx
context
.
Context
,
out
*
fuse
.
AttrOut
)
syscall
.
Errno
}
// SetAttr sets attributes for an Inode.
Setattr
(
ctx
context
.
Context
,
in
*
fuse
.
SetAttrIn
,
out
*
fuse
.
AttrOut
)
syscall
.
Errno
// SetAttr sets attributes for an Inode.
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
// an Inode.
// OnAdd is called once this Operations object is attached to
// an Inode.
type
OnAdder
interface
{
OnAdd
(
ctx
context
.
Context
)
}
// XAttrOperations is a collection of methods used to implement extended attributes.
type
XAttrOperations
interface
{
Operations
// GetXAttr should read data for the given attribute into
// `dest` and return the number of bytes. If `dest` is too
// small, it should return ERANGE and the size of the attribute.
// Getxattr should read data for the given attribute into
// `dest` and return the number of bytes. If `dest` is too
// small, it should return ERANGE and the size of the attribute.
// If not defined, Getxattr will return ENOATTR.
type
Getxattrer
interface
{
Getxattr
(
ctx
context
.
Context
,
attr
string
,
dest
[]
byte
)
(
uint32
,
syscall
.
Errno
)
}
// SetXAttr should store data for the given attribute. See
// setxattr(2) for information about flags.
// Setxattr should store data for the given attribute. See
// 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
}
// 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
}
// ListXAttr should read all attributes (null terminated) into
// `dest`. If the `dest` buffer is too small, it should return
// ERANGE and the correct size.
// Listxattr should read all attributes (null terminated) into
// `dest`. If the `dest` buffer is too small, it should return ERANGE
// 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
)
}
// SymlinkOperations holds operations specific to symlinks.
type
SymlinkOperations
interface
{
Operations
// Readlink reads the content of a symlink.
// Readlink reads the content of a symlink.
type
Readlinker
interface
{
Readlink
(
ctx
context
.
Context
)
([]
byte
,
syscall
.
Errno
)
}
// FileOperations holds operations that apply to regular files.
type
FileOperations
interface
{
Operations
// Open opens an Inode (of regular file type) for reading. It
// is optional but recommended to return a FileHandle.
// Open opens an Inode (of regular file type) for reading. It
// is optional but recommended to return a FileHandle.
type
Opener
interface
{
Open
(
ctx
context
.
Context
,
flags
uint32
)
(
fh
FileHandle
,
fuseFlags
uint32
,
errno
syscall
.
Errno
)
}
// Reads data from a file. The data should be returned as
// ReadResult, which may be constructed from the incoming
// `dest` buffer. If the file was opened without FileHandle,
// the FileHandle argument here is nil. The default
// implementation forwards to the FileHandle.
// Reads data from a file. The data should be returned as
// ReadResult, which may be constructed from the incoming
// `dest` buffer. If the file was opened without FileHandle,
// the FileHandle argument here is nil. The default
// implementation forwards to the FileHandle.
type
Reader
interface
{
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
// returning, the data will be reused and may not referenced.
// The default implementation forwards to the FileHandle.
// Writes the data into the file handle at given offset. After
// returning, the data will be reused and may not referenced.
// 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
)
}
// Fsync is a signal to ensure writes to the Inode are flushed
// to stable storage. The default implementation forwards to the
// FileHandle.
// Fsync is a signal to ensure writes to the Inode are flushed
// to stable storage.
type
Fsyncer
interface
{
Fsync
(
ctx
context
.
Context
,
f
FileHandle
,
flags
uint32
)
syscall
.
Errno
}
// Flush is called for close() call on a file descriptor. In
// case of duplicated descriptor, it may be called more than
// once for a file. The default implementation forwards to the
// FileHandle.
// Flush is called for close() call on a file descriptor. In
// case of duplicated descriptor, it may be called more than
// once for a file. The default implementation forwards to the
// FileHandle.
type
Flusher
interface
{
Flush
(
ctx
context
.
Context
,
f
FileHandle
)
syscall
.
Errno
}
// This is called to before the file handle is forgotten. The
// kernel ingores the return value of this method,
// so any cleanup that requires specific synchronization or
// could fail with I/O errors should happen in Flush instead.
// The default implementation forwards to the FileHandle.
// This is called to before the file handle is forgotten. The
// kernel ingores the return value of this method,
// so any cleanup that requires specific synchronization or
// could fail with I/O errors should happen in Flush instead.
// The default implementation forwards to the FileHandle.
type
Releaser
interface
{
Release
(
ctx
context
.
Context
,
f
FileHandle
)
syscall
.
Errno
}
// Allocate preallocates space for future writes, so they will
// never encounter ESPACE.
// Allocate preallocates space for future writes, so they will
// never encounter ESPACE.
type
Allocater
interface
{
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.
Fgetattr
(
ctx
context
.
Context
,
f
FileHandle
,
out
*
fuse
.
AttrOut
)
syscall
.
Errno
// 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 copies data between sections of two files,
// without the data having to pass through the calling process.
type
CopyFileRanger
interface
{
CopyFileRange
(
ctx
context
.
Context
,
fhIn
FileHandle
,
offIn
uint64
,
out
*
Inode
,
fhOut
FileHandle
,
offOut
uint64
,
len
uint64
,
flags
uint64
)
(
uint32
,
syscall
.
Errno
)
}
// Lseek is used to implement holes: it should return the
// first offset beyond `off` where there is data (SEEK_DATA)
// or where there is a hole (SEEK_HOLE).
// Lseek is used to implement holes: it should return the
// first offset beyond `off` where there is data (SEEK_DATA)
// or where there is a hole (SEEK_HOLE).
type
Lseeker
interface
{
Lseek
(
ctx
context
.
Context
,
f
FileHandle
,
Off
uint64
,
whence
uint32
)
(
uint64
,
syscall
.
Errno
)
}
// LockOperations are operations for locking regions of regular files.
type
LockOperations
interface
{
FileOperations
// Getlk returns locks that would conflict with the given
// input 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 input
// lock. If no locks conflict, the output has type L_UNLCK. See
// fcntl(2) for more information.
// If not defined, returns ENOTSUP
type
Getlker
interface
{
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
// obtained. See fcntl(2) for more information.
// Setlk obtains a lock on a file, or fail if the lock could not
// 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
}
// Setlkw obtains a lock on a file, waiting if necessary. See fcntl(2)
// for more information.
// Setlkw obtains a lock on a file, waiting if necessary. See fcntl(2)
// 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
}
...
...
@@ -237,63 +263,87 @@ type DirStream interface {
Close
()
}
//
DirOperations are operations for directory nodes in the filesystem.
type
DirOperations
interface
{
Operation
s
// Lookup should find a direct child of the node by child
// name. If the entry does not exist, it should return ENOENT
// and optionally set a NegativeTimeout in `out`. If it does
// exist, it should return attribute data in `out` and return
// the Inode for the child. A new inode can be created using
// `Inode.NewInode`. The new Inode will be added to the FS
// tree automatically if the return status is OK.
//
Lookup should find a direct child of the node by child
// name. If the entry does not exist, it should return ENOENT
// and optionally set a NegativeTimeout in `out`. If it doe
s
// exist, it should return attribute data in `out` and return
// the Inode for the child. A new inode can be created using
// `Inode.NewInode`. The new Inode will be added to the FS
// tree automatically if the return status is OK.
//
// If not defined, we look for an existing child with the given name,
// or returns ENOENT.
type
Lookuper
interface
{
Lookup
(
ctx
context
.
Context
,
name
string
,
out
*
fuse
.
EntryOut
)
(
*
Inode
,
syscall
.
Errno
)
}
// OpenDir opens a directory Inode for reading its
// contents. The actual reading is driven from ReadDir, so
// this method is just for performing sanity/permission
// checks.
// OpenDir opens a directory Inode for reading its
// contents. The actual reading is driven from ReadDir, so
// this method is just for performing sanity/permission
// checks. The default is to return success.
type
Opendirer
interface
{
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
)
}
// MutableDirOperations are operations for directories that can add or
// remove entries.
type
MutableDirOperations
interface
{
DirOperations
// Mkdir is similar to Lookup, but must create a directory entry and Inode.
// Mkdir is similar to Lookup, but must create a directory entry and Inode.
// Default is to return EROFS.
type
Mkdirer
interface
{
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
)
}
// 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
)
// Link is similar to Lookup, but must create a new link to an existing Inode.
// 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
)
}
// Create is similar to Lookup, but should create a new
// child. It typically also returns a FileHandle as a
// reference for future reads/writes
// Create is similar to Lookup, but should create a new
// child. It typically also returns a FileHandle as a
// 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
)
}
// Unlink should remove a child from this directory. If the
// return status is OK, the Inode is removed as child in the
// FS tree automatically.
// Unlink should remove a child from this directory. If the
// return status is OK, the Inode is removed as child in the
// FS tree automatically. Default is to return EROFS.
type
Unlinker
interface
{
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
}
// Rename should move a child from one directory to a
// different one. The changes is effected in the FS tree if
// the return status is OK
Rename
(
ctx
context
.
Context
,
name
string
,
newParent
Operations
,
newName
string
,
flags
uint32
)
syscall
.
Errno
// Rename should move a child from one directory to a different
// one. The change is effected in the FS tree if the return status is
// OK.
// 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
...
...
@@ -307,28 +357,67 @@ type MutableDirOperations interface {
// FileHandle. Files that have such dynamic content should return the
// FOPEN_DIRECT_IO flag from their `Open` method. See directio_test.go
// for an example.
//
// For a description of individual operations, see the equivalent
// operations in FileOperations.
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
)
}
// 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
)
}
// 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
}
// 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
}
// 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
}
// See Lseek. Default is to call Lseek on the Inode
type
FileLseeker
interface
{
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
}
// See Fsync. Default is to call Fsync on the Inode
type
FileFsyncer
interface
{
Fsync
(
ctx
context
.
Context
,
flags
uint32
)
syscall
.
Errno
}
Release
(
ctx
context
.
Context
)
syscall
.
Errno
Getattr
(
ctx
context
.
Context
,
out
*
fuse
.
AttrOut
)
syscall
.
Errno
// See Fsync. Default is to call Setattr on the Inode
type
FileSetattrer
interface
{
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
}
...
...
nodefs/bridge.go
View file @
fed41d2f
...
...
@@ -12,6 +12,7 @@ import (
"time"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal"
)
func
errnoToStatus
(
errno
syscall
.
Errno
)
fuse
.
Status
{
...
...
@@ -48,7 +49,7 @@ type rawBridge struct {
}
// 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
()
defer
b
.
mu
.
Unlock
()
...
...
@@ -93,26 +94,11 @@ func (b *rawBridge) newInode(ctx context.Context, ops Operations, id NodeAttr, p
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
()
ops
.
init
(
ops
,
id
,
b
,
persistent
)
ops
.
OnAdd
(
ctx
)
if
oa
,
ok
:=
ops
.
(
OnAdder
);
ok
{
oa
.
OnAdd
(
ctx
)
}
return
ops
.
inode
()
}
...
...
@@ -155,7 +141,7 @@ func (b *rawBridge) setAttrTimeout(out *fuse.AttrOut) {
// NewNodeFS creates a node based filesystem based on an Operations
// instance for the root.
func
NewNodeFS
(
root
DirOperations
,
opts
*
Options
)
fuse
.
RawFileSystem
{
func
NewNodeFS
(
root
InodeLink
,
opts
*
Options
)
fuse
.
RawFileSystem
{
bridge
:=
&
rawBridge
{
automaticIno
:
opts
.
FirstAutomaticIno
,
}
...
...
@@ -192,7 +178,9 @@ func NewNodeFS(root DirOperations, opts *Options) fuse.RawFileSystem {
// Fh 0 means no file handle.
bridge
.
files
=
[]
*
fileEntry
{{}}
root
.
OnAdd
(
context
.
Background
())
if
oa
,
ok
:=
root
.
(
OnAdder
);
ok
{
oa
.
OnAdd
(
context
.
Background
())
}
return
bridge
}
...
...
@@ -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
{
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
b
.
options
.
NegativeTimeout
!=
nil
&&
out
.
EntryTimeout
()
==
0
{
out
.
SetEntryTimeout
(
*
b
.
options
.
NegativeTimeout
)
...
...
@@ -226,13 +215,35 @@ func (b *rawBridge) Lookup(cancel <-chan struct{}, header *fuse.InHeader, name s
b
.
setEntryOutTimeout
(
out
)
out
.
Mode
=
child
.
nodeAttr
.
Mode
|
(
out
.
Mode
&
07777
)
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
{
parent
,
_
:=
b
.
inode
(
header
.
NodeId
,
0
)
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
)
}
...
...
@@ -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
{
parent
,
_
:=
b
.
inode
(
header
.
NodeId
,
0
)
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
)
}
...
...
@@ -260,7 +271,7 @@ func (b *rawBridge) Mkdir(cancel <-chan struct{}, input *fuse.MkdirIn, name stri
var
child
*
Inode
var
errno
syscall
.
Errno
if
mops
,
ok
:=
parent
.
ops
.
(
M
utableDirOperations
);
ok
{
if
mops
,
ok
:=
parent
.
ops
.
(
M
kdirer
);
ok
{
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
var
child
*
Inode
var
errno
syscall
.
Errno
if
mops
,
ok
:=
parent
.
ops
.
(
M
utableDirOperations
);
ok
{
if
mops
,
ok
:=
parent
.
ops
.
(
M
knoder
);
ok
{
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
var
errno
syscall
.
Errno
var
f
FileHandle
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
)
}
...
...
@@ -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
))
b
.
setEntryOutTimeout
(
&
out
.
EntryOut
)
out
.
OpenFlags
=
flags
var
temp
fuse
.
AttrOut
f
.
Getattr
(
ctx
,
&
temp
)
b
.
getattr
(
ctx
,
child
,
f
,
&
temp
)
out
.
Attr
=
temp
.
Attr
out
.
AttrValid
=
temp
.
AttrValid
out
.
AttrValidNsec
=
temp
.
AttrValidNsec
out
.
Attr
.
Ino
=
child
.
nodeAttr
.
Ino
out
.
Generation
=
child
.
nodeAttr
.
Gen
out
.
NodeId
=
child
.
nodeAttr
.
Ino
b
.
setEntryOutTimeout
(
&
out
.
EntryOut
)
out
.
NodeId
=
child
.
nodeAttr
.
Ino
out
.
Mode
=
(
out
.
Attr
.
Mode
&
07777
)
|
child
.
nodeAttr
.
Mode
return
fuse
.
OK
}
...
...
@@ -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
{
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
}
if
fops
,
ok
:=
n
.
ops
.
(
FileOperations
);
ok
{
return
errnoToStatus
(
b
.
getattr
(
ctx
,
n
,
f
,
out
))
}
f
:=
fEntry
.
file
if
input
.
Flags
()
&
fuse
.
FUSE_GETATTR_FH
==
0
{
// 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
()
}
func
(
b
*
rawBridge
)
getattr
(
ctx
context
.
Context
,
n
*
Inode
,
f
FileHandle
,
out
*
fuse
.
AttrOut
)
syscall
.
Errno
{
var
errno
syscall
.
Errno
var
fg
FileGetattrer
if
f
!=
nil
{
fg
,
_
=
f
.
(
FileGetattrer
)
}
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
)
out
.
Ino
=
input
.
NodeId
out
.
Ino
=
n
.
nodeAttr
.
Ino
out
.
Mode
=
(
out
.
Attr
.
Mode
&
07777
)
|
n
.
nodeAttr
.
Mode
return
errnoToStatus
(
errno
)
}
return
errno
ToStatus
(
n
.
ops
.
Getattr
(
ctx
,
out
))
return
errno
}
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
f
=
nil
}
if
fops
,
ok
:=
n
.
ops
.
(
FileOperations
);
ok
{
return
errnoToStatus
(
fops
.
Fsetattr
(
ctx
,
f
,
in
,
out
))
if
fops
,
ok
:=
f
.
(
FileSetattrer
);
ok
{
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
{
p1
,
_
:=
b
.
inode
(
input
.
NodeId
,
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
)
if
errno
==
0
{
if
input
.
Flags
&
RENAME_EXCHANGE
!=
0
{
...
...
@@ -408,7 +435,7 @@ func (b *rawBridge) Link(cancel <-chan struct{}, input *fuse.LinkIn, name string
parent
,
_
:=
b
.
inode
(
input
.
NodeId
,
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
)
if
errno
!=
0
{
return
errnoToStatus
(
errno
)
...
...
@@ -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
{
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
)
if
status
!=
0
{
return
errnoToStatus
(
status
)
...
...
@@ -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
)
{
n
,
_
:=
b
.
inode
(
header
.
NodeId
,
0
)
result
,
errno
:=
n
.
linkOps
()
.
Readlink
(
&
fuse
.
Context
{
Caller
:
header
.
Caller
,
Cancel
:
cancel
})
if
errno
!=
0
{
return
nil
,
errnoToStatus
(
errno
)
if
linker
,
ok
:=
n
.
ops
.
(
Readlinker
);
ok
{
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
{
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.
...
...
@@ -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
)
{
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
)
return
nb
,
errnoToStatus
(
errno
)
}
return
0
,
fuse
.
ENO
TSUP
return
0
,
fuse
.
ENO
ATTR
}
func
(
b
*
rawBridge
)
ListXAttr
(
cancel
<-
chan
struct
{},
header
*
fuse
.
InHeader
,
dest
[]
byte
)
(
sz
uint32
,
status
fuse
.
Status
)
{
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
)
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
{
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
fuse
.
ENO
TSUP
return
fuse
.
ENO
ATTR
}
func
(
b
*
rawBridge
)
RemoveXAttr
(
cancel
<-
chan
struct
{},
header
*
fuse
.
InHeader
,
attr
string
)
fuse
.
Status
{
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
fuse
.
ENO
TSUP
return
fuse
.
ENO
ATTR
}
func
(
b
*
rawBridge
)
Open
(
cancel
<-
chan
struct
{},
input
*
fuse
.
OpenIn
,
out
*
fuse
.
OpenOut
)
fuse
.
Status
{
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
{
b
.
mu
.
Lock
()
defer
b
.
mu
.
Unlock
()
out
.
Fh
=
uint64
(
b
.
registerFile
(
n
,
f
,
input
.
Flags
))
if
op
,
ok
:=
n
.
ops
.
(
Opener
);
ok
{
f
,
flags
,
errno
:=
op
.
Open
(
&
fuse
.
Context
{
Caller
:
input
.
Caller
,
Cancel
:
cancel
},
input
.
Flags
)
if
errno
!=
0
{
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
...
...
@@ -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
)
{
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
{
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
fuse
.
ENOTSUP
...
...
@@ -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
{
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
fuse
.
ENOTSUP
}
func
(
b
*
rawBridge
)
SetLkw
(
cancel
<-
chan
struct
{},
input
*
fuse
.
LkIn
)
fuse
.
Status
{
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
fuse
.
ENOTSUP
...
...
@@ -563,7 +641,12 @@ func (b *rawBridge) Release(cancel <-chan struct{}, input *fuse.ReleaseIn) {
}
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
()
defer
b
.
mu
.
Unlock
()
...
...
@@ -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
)
{
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
))
return
w
,
errnoToStatus
(
errno
)
if
fr
,
ok
:=
f
.
file
.
(
FileWriter
);
ok
{
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
{
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
{
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
{
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
{
n
,
_
:=
b
.
inode
(
input
.
NodeId
,
0
)
errno
:=
n
.
dirOps
()
.
Opendir
(
&
fuse
.
Context
{
Caller
:
input
.
Caller
,
Cancel
:
cancel
})
if
errno
!=
0
{
return
errnoToStatus
(
errno
)
if
od
,
ok
:=
n
.
ops
.
(
Opendirer
);
ok
{
errno
:=
od
.
Opendir
(
&
fuse
.
Context
{
Caller
:
input
.
Caller
,
Cancel
:
cancel
})
if
errno
!=
0
{
return
errnoToStatus
(
errno
)
}
}
b
.
mu
.
Lock
()
defer
b
.
mu
.
Unlock
()
out
.
Fh
=
uint64
(
b
.
registerFile
(
n
,
nil
,
0
))
return
fuse
.
OK
}
func
(
b
*
rawBridge
)
g
etStream
(
cancel
<-
chan
struct
{},
input
*
fuse
.
ReadIn
,
inode
*
Inode
,
f
*
fileEntry
)
syscall
.
Errno
{
func
(
b
*
rawBridge
)
s
etStream
(
cancel
<-
chan
struct
{},
input
*
fuse
.
ReadIn
,
inode
*
Inode
,
f
*
fileEntry
)
syscall
.
Errno
{
if
f
.
dirStream
==
nil
||
input
.
Offset
==
0
{
if
f
.
dirStream
!=
nil
{
f
.
dirStream
.
Close
()
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
{
return
errno
}
...
...
@@ -653,10 +767,25 @@ func (b *rawBridge) getStream(cancel <-chan struct{}, input *fuse.ReadIn, inode
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
{
n
,
f
:=
b
.
inode
(
input
.
NodeId
,
input
.
Fh
)
if
errno
:=
b
.
g
etStream
(
cancel
,
input
,
n
,
f
);
errno
!=
0
{
if
errno
:=
b
.
s
etStream
(
cancel
,
input
,
n
,
f
);
errno
!=
0
{
return
errnoToStatus
(
errno
)
}
...
...
@@ -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
{
n
,
f
:=
b
.
inode
(
input
.
NodeId
,
input
.
Fh
)
if
errno
:=
b
.
g
etStream
(
cancel
,
input
,
n
,
f
);
errno
!=
0
{
if
errno
:=
b
.
s
etStream
(
cancel
,
input
,
n
,
f
);
errno
!=
0
{
return
errnoToStatus
(
errno
)
}
ctx
:=
&
fuse
.
Context
{
Caller
:
input
.
Caller
,
Cancel
:
cancel
}
for
f
.
dirStream
.
HasNext
()
{
var
e
fuse
.
DirEntry
var
errno
syscall
.
Errno
...
...
@@ -710,7 +839,7 @@ func (b *rawBridge) ReadDirPlus(cancel <-chan struct{}, input *fuse.ReadIn, out
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
b
.
options
.
NegativeTimeout
!=
nil
{
entryOut
.
SetEntryTimeout
(
*
b
.
options
.
NegativeTimeout
)
...
...
@@ -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
{
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
{
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
)
{
...
...
@@ -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
)
{
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
)
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
)
return
sz
,
errnoToStatus
(
errno
)
}
...
...
@@ -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
{
n
,
f
:=
b
.
inode
(
in
.
NodeId
,
in
.
Fh
)
off
,
errno
:=
n
.
fileOps
()
.
Lseek
(
&
fuse
.
Context
{
Caller
:
in
.
Caller
,
Cancel
:
cancel
},
f
.
file
,
in
.
Offset
,
in
.
Whence
)
out
.
Offset
=
off
return
errnoToStatus
(
errno
)
if
fs
,
ok
:=
f
.
file
.
(
FileLseeker
);
ok
{
off
,
errno
:=
fs
.
Lseek
(
&
fuse
.
Context
{
Caller
:
in
.
Caller
,
Cancel
:
cancel
},
in
.
Offset
,
in
.
Whence
)
out
.
Offset
=
off
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
}
nodefs/cache_test.go
View file @
fed41d2f
...
...
@@ -19,7 +19,7 @@ import (
)
type
keepCacheFile
struct
{
OperationStubs
InodeEmbed
keepCache
bool
mu
sync
.
Mutex
...
...
@@ -61,12 +61,18 @@ func (f *keepCacheFile) Read(ctx context.Context, fh FileHandle, dest []byte, of
return
fuse
.
ReadResultData
(
f
.
content
[
off
:
]),
OK
}
var
_
=
(
Reader
)((
*
keepCacheFile
)(
nil
))
var
_
=
(
Opener
)((
*
keepCacheFile
)(
nil
))
var
_
=
(
Getattrer
)((
*
keepCacheFile
)(
nil
))
type
keepCacheRoot
struct
{
OperationStubs
InodeEmbed
keep
,
nokeep
*
keepCacheFile
}
var
_
=
(
OnAdder
)((
*
keepCacheRoot
)(
nil
))
func
(
r
*
keepCacheRoot
)
OnAdd
(
ctx
context
.
Context
)
{
i
:=
r
.
Inode
()
...
...
nodefs/default.go
View file @
fed41d2f
...
...
@@ -5,11 +5,7 @@
package
nodefs
import
(
"context"
"syscall"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/internal"
)
// InodeEmbed embeds the Inode into a filesystem node. It is the only
...
...
@@ -25,7 +21,7 @@ func (n *InodeEmbed) inode() *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
{
ops
:
ops
,
nodeAttr
:
attr
,
...
...
@@ -42,332 +38,3 @@ func (n *InodeEmbed) init(ops Operations, attr NodeAttr, bridge *rawBridge, pers
func
(
n
*
InodeEmbed
)
Inode
()
*
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
}
nodefs/directio_test.go
View file @
fed41d2f
...
...
@@ -17,7 +17,7 @@ import (
)
type
dioRoot
struct
{
OperationStubs
InodeEmbed
}
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
// multiples of 1024
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
)
{
next
:=
(
off
+
1023
)
&
(
^
uint64
(
1023
))
return
next
,
OK
...
...
@@ -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
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
)
{
return
&
dioFH
{},
fuse
.
FOPEN_DIRECT_IO
,
OK
}
...
...
nodefs/files.go
View file @
fed41d2f
...
...
@@ -24,6 +24,20 @@ type loopbackFile struct {
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
)
{
r
:=
fuse
.
ReadResultFd
(
uintptr
(
f
.
fd
),
off
,
len
(
buf
))
return
r
,
OK
...
...
nodefs/inode.go
View file @
fed41d2f
...
...
@@ -53,7 +53,7 @@ func (i *NodeAttr) Reserved() bool {
type
Inode
struct
{
nodeAttr
NodeAttr
ops
Operations
ops
InodeLink
bridge
*
rawBridge
// Following data is mutable.
...
...
@@ -90,18 +90,6 @@ type Inode 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.
func
(
n
*
Inode
)
NodeAttr
()
NodeAttr
{
return
n
.
nodeAttr
...
...
@@ -214,7 +202,7 @@ func (n *Inode) Forgotten() bool {
// Operations returns the object implementing the file system
// operations.
func
(
n
*
Inode
)
Operations
()
Operations
{
func
(
n
*
Inode
)
Operations
()
InodeLink
{
return
n
.
ops
}
...
...
@@ -273,7 +261,7 @@ func (iparent *Inode) setEntry(name string, ichild *Inode) {
// NewPersistentInode returns an Inode whose lifetime is not in
// 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
)
}
...
...
@@ -284,16 +272,16 @@ func (n *Inode) ForgetPersistent() {
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
// 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
// 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
)
}
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
)
}
...
...
nodefs/interrupt_test.go
View file @
fed41d2f
...
...
@@ -17,14 +17,11 @@ import (
)
type
interruptRoot
struct
{
OperationStubs
InodeEmbed
child
interruptOps
}
type
interruptOps
struct
{
OperationStubs
interrupted
bool
}
var
_
=
(
Lookuper
)((
*
interruptRoot
)(
nil
))
func
(
r
*
interruptRoot
)
Lookup
(
ctx
context
.
Context
,
name
string
,
out
*
fuse
.
EntryOut
)
(
*
Inode
,
syscall
.
Errno
)
{
if
name
!=
"file"
{
...
...
@@ -37,6 +34,13 @@ func (r *interruptRoot) Lookup(ctx context.Context, name string, out *fuse.Entry
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
)
{
select
{
case
<-
time
.
After
(
100
*
time
.
Millisecond
)
:
...
...
nodefs/loopback.go
View file @
fed41d2f
...
...
@@ -6,7 +6,6 @@ package nodefs
import
(
"context"
"log"
"os"
"path/filepath"
"syscall"
...
...
@@ -21,6 +20,27 @@ type loopbackRoot struct {
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
{
s
:=
syscall
.
Statfs_t
{}
err
:=
syscall
.
Statfs
(
n
.
path
(),
&
s
)
...
...
@@ -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
{
log
.
Println
(
"getattr"
)
st
:=
syscall
.
Stat_t
{}
err
:=
syscall
.
Stat
(
n
.
rootPath
,
&
st
)
if
err
!=
nil
{
...
...
@@ -43,7 +62,7 @@ func (n *loopbackRoot) Getattr(ctx context.Context, out *fuse.AttrOut) syscall.E
}
type
loopbackNode
struct
{
OperationStubs
InodeEmbed
}
func
(
n
*
loopbackNode
)
root
()
*
loopbackRoot
{
...
...
@@ -122,14 +141,14 @@ func (n *loopbackNode) Unlink(ctx context.Context, name string) syscall.Errno {
return
ToErrno
(
err
)
}
func
toLoopbackNode
(
op
Operations
)
*
loopbackNode
{
func
toLoopbackNode
(
op
InodeLink
)
*
loopbackNode
{
if
r
,
ok
:=
op
.
(
*
loopbackRoot
);
ok
{
return
&
r
.
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
)
if
flags
&
RENAME_EXCHANGE
!=
0
{
return
n
.
renameExchange
(
name
,
newParentLoopback
,
newName
)
...
...
@@ -199,7 +218,7 @@ func (n *loopbackNode) Symlink(ctx context.Context, target, name string, out *fu
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
)
targetNode
:=
toLoopbackNode
(
target
)
...
...
@@ -258,11 +277,7 @@ func (n *loopbackNode) Readdir(ctx context.Context) (DirStream, syscall.Errno) {
return
NewLoopbackDirStream
(
n
.
path
())
}
func
(
n
*
loopbackNode
)
Fgetattr
(
ctx
context
.
Context
,
f
FileHandle
,
out
*
fuse
.
AttrOut
)
syscall
.
Errno
{
if
f
!=
nil
{
return
f
.
Getattr
(
ctx
,
out
)
}
func
(
n
*
loopbackNode
)
Getattr
(
ctx
context
.
Context
,
out
*
fuse
.
AttrOut
)
syscall
.
Errno
{
p
:=
n
.
path
()
var
err
error
=
nil
...
...
@@ -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
// root is at the given root.
func
NewLoopbackRoot
(
root
string
)
(
DirOperations
,
error
)
{
func
NewLoopbackRoot
(
root
string
)
(
InodeLink
,
error
)
{
var
st
syscall
.
Stat_t
err
:=
syscall
.
Stat
(
root
,
&
st
)
if
err
!=
nil
{
...
...
nodefs/mount.go
View file @
fed41d2f
...
...
@@ -14,7 +14,7 @@ import (
// requests. This is a convenience wrapper around NewNodeFS and
// fuse.NewServer. If nil is given as options, default settings are
// 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
{
oneSec
:=
time
.
Second
options
=
&
Options
{
...
...
nodefs/simple_test.go
View file @
fed41d2f
...
...
@@ -28,7 +28,7 @@ type testCase struct {
origDir
string
mntDir
string
loopback
DirOperations
loopback
InodeLink
rawFS
fuse
.
RawFileSystem
server
*
fuse
.
Server
}
...
...
nodefs/zip_test.go
View file @
fed41d2f
...
...
@@ -106,14 +106,15 @@ func TestZipFS(t *testing.T) {
// zipFile is a file read from a zip archive.
type
zipFile
struct
{
OperationStubs
InodeEmbed
file
*
zip
.
File
mu
sync
.
Mutex
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
// FS would also set timestamps and permissions.
...
...
@@ -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
// is populating the filesystem.
type
zipRoot
struct
{
OperationStubs
InodeEmbed
r
*
zip
.
Reader
}
var
_
=
(
OnAdder
)((
*
zipRoot
)(
nil
))
func
(
zr
*
zipRoot
)
OnAdd
(
ctx
context
.
Context
)
{
// OnAdd is called once we are attached to an Inode. We can
// then construct a tree. We construct the entire tree, and
...
...
@@ -177,7 +180,7 @@ func (zr *zipRoot) OnAdd(ctx context.Context) {
}
ch
:=
p
.
GetChild
(
component
)
if
ch
==
nil
{
ch
=
p
.
NewPersistentInode
(
ctx
,
&
OperationStubs
{},
ch
=
p
.
NewPersistentInode
(
ctx
,
&
InodeEmbed
{},
NodeAttr
{
Mode
:
fuse
.
S_IFDIR
})
p
.
AddChild
(
component
,
ch
,
true
)
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment