Commit 3e1c2619 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

nodefs: add OnAdd

Allow nil FileHandles.

Change DefaultOperations to be for a ReadOnly in-memory filesystem.
parent 9d9bc730
......@@ -44,9 +44,8 @@
//
// File system trees can also be constructed in advance. This is done
// by instantiating "persistent" inodes. Persistent inodes remain in
// memory even if the kernel has forgotten them.
//
// XXX describe how to mount.
// memory even if the kernel has forgotten them. See zip_test.go for
// an example of how to do this.
//
// XXX node example with Lookup.
//
......@@ -90,14 +89,20 @@ type Operations interface {
// susan gets the UID and GID for susan here.
Access(ctx context.Context, mask uint32) fuse.Status
// GetAttr reads attributes for an Inode
// 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(ctx context.Context, out *fuse.AttrOut) fuse.Status
// SetAttr sets attributes for an Inode.
SetAttr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status
// OnAdd is called once this Operations object is attached to
// an Inode.
OnAdd()
}
// XAttrOperations is as collection of methods used to implement extended attributes.
// XAttrOperations is a collection of methods used to implement extended attributes.
type XAttrOperations interface {
Operations
......@@ -309,4 +314,8 @@ type Options struct {
// for failed lookups (fuse.ENOENT). See fuse.EntryOut for
// more information.
NegativeTimeout *time.Duration
// Automatic inode numbers are handed out sequentially
// starting from this number. If unset, use 2^63.
FirstAutomaticIno uint64
}
......@@ -98,7 +98,13 @@ func (b *rawBridge) newInode(ops Operations, mode uint32, id FileID, persistent
b.nodes[id.Ino] = inode
ops.setInode(inode)
return ops.inode()
newIno := ops.inode()
if newIno == inode {
newIno.ops.OnAdd()
}
return newIno
}
// addNewChild inserts the child into the tree. Returns file handle if file != nil.
......@@ -142,7 +148,14 @@ func (b *rawBridge) setAttrTimeout(out *fuse.AttrOut) {
// instance for the root.
func NewNodeFS(root DirOperations, opts *Options) fuse.RawFileSystem {
bridge := &rawBridge{
automaticIno: 1 << 63,
automaticIno: opts.FirstAutomaticIno,
}
if bridge.automaticIno == 1 {
bridge.automaticIno++
}
if bridge.automaticIno == 0 {
bridge.automaticIno = 1 << 63
}
if opts != nil {
......@@ -169,6 +182,9 @@ func NewNodeFS(root DirOperations, opts *Options) fuse.RawFileSystem {
// Fh 0 means no file handle.
bridge.files = []*fileEntry{{}}
root.OnAdd()
return bridge
}
......@@ -472,9 +488,11 @@ func (b *rawBridge) Open(cancel <-chan struct{}, input *fuse.OpenIn, out *fuse.O
return status
}
b.mu.Lock()
defer b.mu.Unlock()
out.Fh = uint64(b.registerFile(n, f, input.Flags))
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
}
......@@ -531,6 +549,10 @@ func (b *rawBridge) SetLkw(cancel <-chan struct{}, input *fuse.LkIn) (status fus
func (b *rawBridge) Release(input *fuse.ReleaseIn) {
// XXX should have cancel channel too.
n, f := b.releaseFileEntry(input.NodeId, input.Fh)
if f == nil {
return
}
f.wg.Wait()
n.fileOps().Release(&fuse.Context{Caller: input.Caller, Cancel: nil}, f.file)
......
......@@ -6,15 +6,17 @@ package nodefs
import (
"context"
"log"
"sync/atomic"
"unsafe"
"github.com/hanwen/go-fuse/fuse"
)
// DefaultOperations provides stubs that return ENOENT for almost all
// functions.
// DefaultOperations 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 must be embedded in any Operations implementation.
type DefaultOperations struct {
......@@ -60,12 +62,18 @@ func (n *DefaultOperations) StatFs(ctx context.Context, out *fuse.StatfsOut) fus
return fuse.OK
}
func (n *DefaultOperations) OnAdd() {
// XXX context?
}
// GetAttr zeroes out argument and returns OK.
func (n *DefaultOperations) GetAttr(ctx context.Context, out *fuse.AttrOut) fuse.Status {
return fuse.ENOENT
*out = fuse.AttrOut{}
return fuse.OK
}
func (n *DefaultOperations) SetAttr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status {
return fuse.ENOENT
return fuse.EROFS
}
func (n *DefaultOperations) Access(ctx context.Context, mask uint32) fuse.Status {
......@@ -82,34 +90,59 @@ func (n *DefaultOperations) FSetAttr(ctx context.Context, f FileHandle, in *fuse
return n.inode_.Operations().SetAttr(ctx, in, out)
}
// The Lookup method on the DefaultOperations type looks for an
// existing child with the given name, or returns ENOENT.
func (n *DefaultOperations) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, fuse.Status) {
return nil, fuse.ENOENT
ch := InodeOf(n).GetChild(name)
if ch == nil {
return nil, fuse.ENOENT
}
var a fuse.AttrOut
status := ch.Operations().GetAttr(ctx, &a)
out.Attr = a.Attr
return ch, status
}
// Mkdir returns EROFS
func (n *DefaultOperations) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, fuse.Status) {
return nil, fuse.ENOENT
return nil, fuse.EROFS
}
// Mknod returns EROFS
func (n *DefaultOperations) Mknod(ctx context.Context, name string, mode uint32, dev uint32, out *fuse.EntryOut) (*Inode, fuse.Status) {
return nil, fuse.ENOENT
return nil, fuse.EROFS
}
// Rmdir returns EROFS
func (n *DefaultOperations) Rmdir(ctx context.Context, name string) fuse.Status {
return fuse.ENOENT
return fuse.EROFS
}
// Unlink returns EROFS
func (n *DefaultOperations) Unlink(ctx context.Context, name string) fuse.Status {
return fuse.ENOENT
return fuse.EROFS
}
// The default OpenDir always succeeds
func (n *DefaultOperations) OpenDir(ctx context.Context) fuse.Status {
return fuse.ENOENT
return fuse.OK
}
// The default ReadDir returns the list of children from the tree
func (n *DefaultOperations) ReadDir(ctx context.Context) (DirStream, fuse.Status) {
return nil, fuse.ENOENT
r := []fuse.DirEntry{}
for k, ch := range InodeOf(n).Children() {
r = append(r, fuse.DirEntry{Mode: ch.Mode(),
Name: k,
Ino: ch.FileID().Ino})
}
return NewListDirStream(r), fuse.OK
}
// Rename returns EROFS
func (n *DefaultOperations) Rename(ctx context.Context, name string, newParent Operations, newName string, flags uint32) fuse.Status {
return fuse.ENOENT
return fuse.EROFS
}
func (n *DefaultOperations) Read(ctx context.Context, f FileHandle, dest []byte, off int64) (fuse.ReadResult, fuse.Status) {
......@@ -119,9 +152,9 @@ func (n *DefaultOperations) Read(ctx context.Context, f FileHandle, dest []byte,
return nil, fuse.ENOENT
}
// Symlink returns EROFS
func (n *DefaultOperations) Symlink(ctx context.Context, target, name string, out *fuse.EntryOut) (node *Inode, status fuse.Status) {
log.Println("defsyml")
return nil, fuse.ENOENT
return nil, fuse.EROFS
}
func (n *DefaultOperations) Readlink(ctx context.Context) (string, fuse.Status) {
......@@ -140,7 +173,7 @@ func (n *DefaultOperations) Write(ctx context.Context, f FileHandle, data []byte
return f.Write(ctx, data, off)
}
return 0, fuse.ENOENT
return 0, fuse.EROFS
}
func (n *DefaultOperations) GetLk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (status fuse.Status) {
......@@ -178,7 +211,7 @@ func (n *DefaultOperations) Release(ctx context.Context, f FileHandle) fuse.Stat
if f != nil {
return f.Release(ctx)
}
return fuse.ENOENT
return fuse.OK
}
func (n *DefaultOperations) Allocate(ctx context.Context, f FileHandle, off uint64, size uint64, mode uint32) (status fuse.Status) {
......@@ -203,24 +236,29 @@ func (n *DefaultOperations) Open(ctx context.Context, flags uint32) (fh FileHand
}
func (n *DefaultOperations) Create(ctx context.Context, name string, flags uint32, mode uint32) (node *Inode, fh FileHandle, fuseFlags uint32, status fuse.Status) {
return nil, nil, 0, fuse.ENOENT
return nil, nil, 0, fuse.EROFS
}
func (n *DefaultOperations) Link(ctx context.Context, target Operations, name string, out *fuse.EntryOut) (node *Inode, status fuse.Status) {
return nil, fuse.ENOENT
return nil, fuse.EROFS
}
// The default GetXAttr returns ENOATTR
func (n *DefaultOperations) GetXAttr(ctx context.Context, attr string, dest []byte) (uint32, fuse.Status) {
return 0, fuse.ENOENT
return 0, fuse.ENOATTR
}
// The default SetXAttr returns ENOATTR
func (n *DefaultOperations) SetXAttr(ctx context.Context, attr string, data []byte, flags uint32) fuse.Status {
return fuse.ENOENT
return fuse.EROFS
}
// The default RemoveXAttr returns ENOATTR
func (n *DefaultOperations) RemoveXAttr(ctx context.Context, attr string) fuse.Status {
return fuse.ENOENT
return fuse.ENOATTR
}
// The default RemoveXAttr returns an empty list
func (n *DefaultOperations) ListXAttr(ctx context.Context, dest []byte) (uint32, fuse.Status) {
return 0, fuse.OK
}
......
......@@ -105,6 +105,11 @@ func (n *Inode) FileID() FileID {
return n.nodeID
}
// Mode returns the filetype
func (n *Inode) Mode() uint32 {
return n.mode
}
// 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
......@@ -361,6 +366,69 @@ retry:
return forgotten, false
}
// GetChild returns a child node with the given name, or nil if the
// directory has no child by that name.
func (n *Inode) GetChild(name string) *Inode {
n.mu.Lock()
defer n.mu.Unlock()
return n.children[name]
}
// AddChild adds a child to this node. If overwrite is false, fail if
// the destination already exists.
func (n *Inode) AddChild(name string, ch *Inode, overwrite bool) (success bool) {
if len(name) == 0 {
log.Panic("empty name for inode")
}
retry:
for {
lockNode2(n, ch)
prev, ok := n.children[name]
parentCounter := n.changeCounter
if !ok {
n.children[name] = ch
ch.parents[parentData{name, n}] = struct{}{}
n.changeCounter++
ch.changeCounter++
unlockNode2(n, ch)
return true
}
unlockNode2(n, ch)
if !overwrite {
return false
}
lockme := [3]*Inode{n, ch, prev}
lockNodes(lockme[:]...)
if parentCounter != n.changeCounter {
unlockNodes(lockme[:]...)
continue retry
}
delete(prev.parents, parentData{name, n})
n.children[name] = ch
ch.parents[parentData{name, n}] = struct{}{}
n.changeCounter++
ch.changeCounter++
prev.changeCounter++
unlockNodes(lockme[:]...)
return true
}
}
// Children returns the list of children of this directory Inode.
func (n *Inode) Children() map[string]*Inode {
n.mu.Lock()
defer n.mu.Unlock()
r := make(map[string]*Inode, len(n.children))
for k, v := range n.children {
r[k] = v
}
return r
}
// RmChild removes multiple children. Returns whether the removal
// succeeded and whether the node is still live afterward. The removal
// is transactional: it only succeeds if all names are children, and
......@@ -418,6 +486,10 @@ retry:
// MvChild executes a rename. If overwrite is set, a child at the
// destination will be overwritten, should it exist.
func (n *Inode) MvChild(old string, newParent *Inode, newName string, overwrite bool) bool {
if len(newName) == 0 {
log.Panicf("empty newName for MvChild")
}
retry:
for {
lockNode2(n, newParent)
......
......@@ -281,7 +281,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 NewLoopback(root string) (DirOperations, error) {
func NewLoopbackRoot(root string) (DirOperations, error) {
var st syscall.Stat_t
err := syscall.Stat(root, &st)
if err != nil {
......
......@@ -69,7 +69,7 @@ func newTestCase(t *testing.T, entryCache bool, attrCache bool) *testCase {
}
var err error
tc.loopback, err = NewLoopback(tc.origDir)
tc.loopback, err = NewLoopbackRoot(tc.origDir)
if err != nil {
t.Fatalf("NewLoopback: %v", err)
}
......
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