Commit 32016f39 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

nodefs: documentation

parent 6d9ae31b
......@@ -84,5 +84,7 @@ or
* Should bridge.Lookup() add the child, bridge.Unlink remove the child, etc.?
* Merge Fsync/FsyncDir?
* Merge Release/ReleaseDir?
......@@ -104,8 +104,15 @@ type Operations interface {
inode() *Inode
setInode(*Inode) bool
// StatFs implements statistics for the filesystem that holds
// this Inode. DefaultNode implements this, because OSX
// filesystem must have a valid StatFs implementation.
StatFs(ctx context.Context, out *fuse.StatfsOut) fuse.Status
// 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(ctx context.Context, mask uint32) fuse.Status
// File locking
......@@ -115,34 +122,77 @@ type Operations interface {
// Extended attributes
// 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(ctx context.Context, attr string, dest []byte) (uint32, fuse.Status)
// SetXAttr should store data for the given attribute.
// XXX flags.
SetXAttr(ctx context.Context, attr string, data []byte, flags uint32) fuse.Status
// RemoveXAttr should delete the given attribute.
RemoveXAttr(ctx context.Context, attr string) fuse.Status
// 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(ctx context.Context, dest []byte) (uint32, fuse.Status)
// The methods below may be called on closed files, due to
// concurrency.
GetAttr(ctx context.Context, f FileHandle, out *fuse.AttrOut) fuse.Status
// Lookup should find a direct child of the node by child name.
//
// VFS makes sure to call Lookup only once for particular
// (node, name) pair concurrently.
// 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(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, fuse.Status)
// 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, fuse.Status)
// Mknod is similar to Lookup, but must create a device entry and Inode.
Mknod(ctx context.Context, name string, mode uint32, dev uint32, out *fuse.EntryOut) (*Inode, fuse.Status)
Rmdir(ctx context.Context, name string) fuse.Status
Unlink(ctx context.Context, name string) fuse.Status
Rename(ctx context.Context, name string, newParent Operations, newName string, flags uint32) fuse.Status
Create(ctx context.Context, name string, flags uint32, mode uint32) (node *Inode, fh FileHandle, fuseFlags uint32, status fuse.Status)
// 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, status fuse.Status)
// Symlink is similar to Lookup, but must create a new symbolic link.
Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (node *Inode, status fuse.Status)
// 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(ctx context.Context, name string, flags uint32, mode uint32) (node *Inode, fh FileHandle, fuseFlags uint32, status fuse.Status)
// 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(ctx context.Context, name string) fuse.Status
// Rmdir is like Unlink but for directories.
Rmdir(ctx context.Context, name string) fuse.Status
// 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) fuse.Status
// Readlink reads the content of a symlink.
Readlink(ctx context.Context) (string, fuse.Status)
// 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, status fuse.Status)
// OpenDir is called for sanity/permission checks on opening a
// directory.
// 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(ctx context.Context) fuse.Status
// ReadDir opens a stream of directory entries.
......@@ -150,26 +200,33 @@ type Operations interface {
// Reads data from a file. The data should be returned as
// ReadResult, which may be constructed from the incoming
// `dest` buffer.
// `dest` buffer. If the file was opened without FileHandle,
// the FileHandle argument here is nil. The default
// implementation forwards to the FileHandle.
Read(ctx context.Context, f FileHandle, dest []byte, off int64) (fuse.ReadResult, fuse.Status)
// 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.
Write(ctx context.Context, f FileHandle, data []byte, off int64) (written uint32, status fuse.Status)
// Fsync is a signal to ensure writes to the Inode are flushed
// to stable storage. The default implementation forwards to the
// FileHandle.
Fsync(ctx context.Context, f FileHandle, flags uint32) (status fuse.Status)
// 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.
// once for a file. The default implementation forwards to the
// FileHandle.
Flush(ctx context.Context, f FileHandle) fuse.Status
// This is called to before the file handle is forgotten. This
// method has no return value, so nothing can synchronize on
// the call, and it cannot be canceled. Any cleanup that
// requires specific synchronization or could fail with I/O
// errors should happen in Flush instead.
Release(f FileHandle)
// This is called to before the file handle is forgotten. The
// kernel ingores the return value of this method is ignored,
// 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.
Release(f FileHandle) fuse.Status
/*
NOSUBMIT - fold into a setattr method, or expand methods?
......@@ -178,13 +235,29 @@ type Operations interface {
types as args, we can't take apart SetAttr for the caller
*/
// Truncate sets the file length to the given size. The
// default implementation forwards to the FileHandle.
Truncate(ctx context.Context, f FileHandle, size uint64) fuse.Status
// Chown changes the file owner . The default implementation
// forwards to the FileHandle.
Chown(ctx context.Context, f FileHandle, uid uint32, gid uint32) fuse.Status
// Chmod changes the file permissions. The default
// implementation forwards to the FileHandle.
Chmod(ctx context.Context, f FileHandle, perms uint32) fuse.Status
// Utimens changes the files timestamps. The default
// implementation forwards to the FileHandle.
Utimens(ctx context.Context, f FileHandle, atime *time.Time, mtime *time.Time) fuse.Status
// Allocate preallocates space for future writes, so they will
// never encounter ESPACE. The default implementation
// forwards to the FileHandle.
Allocate(ctx context.Context, f FileHandle, off uint64, size uint64, mode uint32) (status fuse.Status)
}
// FileHandle is a resource identifier for opened files.
type FileHandle interface {
Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, fuse.Status)
......@@ -209,7 +282,7 @@ type FileHandle interface {
// the call, and it cannot be canceled. Any cleanup that
// requires specific synchronization or could fail with I/O
// errors should happen in Flush instead.
Release()
Release() fuse.Status
// The methods below may be called on closed files, due to
// concurrency.
......
This diff is collapsed.
......@@ -150,10 +150,11 @@ func (n *DefaultOperations) Flush(ctx context.Context, f FileHandle) fuse.Status
return fuse.ENOSYS
}
func (n *DefaultOperations) Release(f FileHandle) {
func (n *DefaultOperations) Release(f FileHandle) fuse.Status {
if f != nil {
f.Release()
return f.Release()
}
return fuse.ENOSYS
}
func (n *DefaultOperations) Allocate(ctx context.Context, f FileHandle, off uint64, size uint64, mode uint32) (status fuse.Status) {
......@@ -264,8 +265,8 @@ func (f *DefaultFile) Flush(ctx context.Context) fuse.Status {
return fuse.ENOSYS
}
func (f *DefaultFile) Release() {
func (f *DefaultFile) Release() fuse.Status {
return fuse.ENOSYS
}
func (f *DefaultFile) GetAttr(ctx context.Context, out *fuse.AttrOut) fuse.Status {
......
......@@ -46,10 +46,11 @@ func (f *loopbackFile) Write(ctx context.Context, data []byte, off int64) (uint3
return uint32(n), fuse.ToStatus(err)
}
func (f *loopbackFile) Release() {
func (f *loopbackFile) Release() fuse.Status {
f.mu.Lock()
syscall.Close(f.fd)
err := syscall.Close(f.fd)
f.mu.Unlock()
return fuse.ToStatus(err)
}
func (f *loopbackFile) Flush(ctx context.Context) fuse.Status {
......
......@@ -15,8 +15,6 @@ import (
"github.com/hanwen/go-fuse/fuse"
)
var _ = log.Println
type parentData struct {
name string
parent *Inode
......@@ -54,7 +52,7 @@ type Inode struct {
nodeID FileID
node Operations
ops Operations
bridge *rawBridge
// Following data is mutable.
......@@ -90,10 +88,12 @@ type Inode struct {
parents map[parentData]struct{}
}
// FileID returns the (Ino, Gen) tuple for this node.
func (n *Inode) FileID() FileID {
return n.nodeID
}
// IsRoot returns true if this is the root of the FUSE mount.
func (n *Inode) IsRoot() bool {
return n.nodeID.Ino == fuse.FUSE_ROOT_ID
}
......@@ -195,7 +195,7 @@ func (n *Inode) Forgotten() bool {
// Node returns the Node object implementing the file system operations.
func (n *Inode) Operations() Operations {
return n.node
return n.ops
}
// Path returns a path string to the inode relative to the root.
......@@ -238,23 +238,6 @@ func (n *Inode) Path(root *Inode) string {
return path
}
// Finds a child with the given name and filetype. Returns nil if not
// found.
func (n *Inode) FindChildByMode(name string, mode uint32) *Inode {
mode ^= 07777
n.mu.Lock()
defer n.mu.Unlock()
ch := n.children[name]
if ch != nil && ch.mode == mode {
return ch
}
return nil
}
// setEntry does `iparent[name] = ichild` linking.
//
// setEntry must not be called simultaneously for any of iparent or ichild.
......@@ -420,7 +403,7 @@ retry:
}
// MvChild executes a rename. If overwrite is set, a child at the
// destination will be overwritten.
// destination will be overwritten, should it exist.
func (n *Inode) MvChild(old string, newParent *Inode, newName string, overwrite bool) bool {
retry:
for {
......@@ -475,6 +458,8 @@ retry:
}
}
// ExchangeChild swaps the entries at (n, oldName) and (newParent,
// newName).
func (n *Inode) ExchangeChild(oldName string, newParent *Inode, newName string) {
oldParent := n
retry:
......@@ -487,9 +472,6 @@ retry:
destChild := newParent.children[newName]
unlockNode2(oldParent, newParent)
if destChild == nil && oldChild == nil {
return
}
if destChild == oldChild {
return
}
......@@ -536,26 +518,36 @@ retry:
}
}
// NotifyEntry notifies the kernel that data for a (directory, name)
// tuple should be invalidated. On next access, a LOOKUP operation
// will be started.
func (n *Inode) NotifyEntry(name string) fuse.Status {
return n.bridge.server.EntryNotify(n.nodeID.Ino, name)
}
// XXX naming: DeleteNotify ?
// NotifyDelete notifies the kernel that the given inode was removed
// from this directory as entry under the given name. It is equivalent
// to NotifyEntry, but also sends an event to inotify watchers.
func (n *Inode) NotifyDelete(name string, child *Inode) fuse.Status {
// XXX arg ordering?
return n.bridge.server.DeleteNotify(n.nodeID.Ino, child.nodeID.Ino, name)
}
// NotifyContent notifies the kernel that content under the given
// inode should be flushed from buffers.
func (n *Inode) NotifyContent(off, sz int64) fuse.Status {
// XXX how does this work for directories?
return n.bridge.server.InodeNotify(n.nodeID.Ino, off, sz)
}
// WriteCache stores data in the kernel cache.
func (n *Inode) WriteCache(offset int64, data []byte) fuse.Status {
return n.bridge.server.InodeNotifyStoreCache(n.nodeID.Ino, offset, data)
}
// ReadCache reads data from the kernel cache.
func (n *Inode) ReadCache(offset int64, dest []byte) (count int, status fuse.Status) {
return n.bridge.server.InodeRetrieveCache(n.nodeID.Ino, offset, dest)
}
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