Commit bedd3b65 authored by Kirill Smelkov's avatar Kirill Smelkov

X incomplete review of newapi 2

many things from na1.patch still not yet integrated back.
parent 48be1a10
This diff is collapsed.
...@@ -36,6 +36,9 @@ ...@@ -36,6 +36,9 @@
// automatically builds new index node and links it in the filesystem tree. // automatically builds new index node and links it in the filesystem tree.
// InodeOf can be used to get particular Inode associated with a Node. // InodeOf can be used to get particular Inode associated with a Node.
// //
// XXX once can also create new subtrees via linking nodes explicitly, and
// return such subtree on lookup.
//
// XXX ^^^ inodes cleaned on cache clean (FORGET). // XXX ^^^ inodes cleaned on cache clean (FORGET).
// //
// XXX describe how to mount. // XXX describe how to mount.
...@@ -57,8 +60,11 @@ import ( ...@@ -57,8 +60,11 @@ import (
// //
// The identity of the Inode does not change over the lifetime of // The identity of the Inode does not change over the lifetime of
// the node object. // the node object.
//
// Returned Inode is always !nil - if node was not yet associated with inode, a
// new inode is atomically created and associated with the node.
func InodeOf(node Node) *Inode { func InodeOf(node Node) *Inode {
return node.inode() return inodeOf(node)
} }
/* /*
...@@ -81,21 +87,32 @@ type Node interface { ...@@ -81,21 +87,32 @@ type Node interface {
inode() *Inode inode() *Inode
setInode(*Inode) setInode(*Inode)
// NodeID() should return filesystem-wide ID of the node.
//
// If the node has such ID it will be used as the base for corresponding inode ID.
// If the node does not have such ID - 0 must be returned and
// automatically allocated inode ID will be used.
//
// The ID, if given, must be unique throughout filesystem.
//
// XXX range of allowed ID = ?
NodeID() uint64
// Lookup should find a direct child of the node by child name. // Lookup should find a direct child of the node by child name.
// //
// VFS makes sure to call Lookup only once for particular (node, name) // VFS makes sure to call Lookup only once for particular (node, name)
// pair. // pair.
Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, fuse.Status) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (Node, fuse.Status)
Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, fuse.Status) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (Node, fuse.Status)
Mknod(ctx context.Context, name string, mode uint32, dev uint32, out *fuse.EntryOut) (*Inode, fuse.Status) Mknod(ctx context.Context, name string, mode uint32, dev uint32, out *fuse.EntryOut) (Node, fuse.Status)
Rmdir(ctx context.Context, name string) fuse.Status Rmdir(ctx context.Context, name string) fuse.Status
Unlink(ctx context.Context, name string) fuse.Status Unlink(ctx context.Context, name string) fuse.Status
Rename(ctx context.Context, name string, newParent Node, newName string, flags uint32) fuse.Status Rename(ctx context.Context, name string, newParent Node, newName string, flags uint32) fuse.Status
Open(ctx context.Context, flags uint32) (fh File, fuseFlags uint32, code fuse.Status) Open(ctx context.Context, flags uint32) (fh File, fuseFlags uint32, code fuse.Status)
Create(ctx context.Context, name string, flags uint32, mode uint32) (node *Inode, fh File, fuseFlags uint32, code fuse.Status) Create(ctx context.Context, name string, flags uint32, mode uint32) (node Node, fh File, fuseFlags uint32, code fuse.Status)
Read(ctx context.Context, f File, dest []byte, off int64) (fuse.ReadResult, fuse.Status) Read(ctx context.Context, f File, dest []byte, off int64) (fuse.ReadResult, fuse.Status)
...@@ -137,6 +154,7 @@ type Node interface { ...@@ -137,6 +154,7 @@ type Node interface {
Allocate(ctx context.Context, f File, off uint64, size uint64, mode uint32) (code fuse.Status) Allocate(ctx context.Context, f File, off uint64, size uint64, mode uint32) (code fuse.Status)
} }
// XXX -> Handle? FileHandle? (better Handle as handle could be used not only for leaves)
type File interface { type File interface {
Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, fuse.Status) Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, fuse.Status)
Write(ctx context.Context, data []byte, off int64) (written uint32, code fuse.Status) Write(ctx context.Context, data []byte, off int64) (written uint32, code fuse.Status)
......
...@@ -20,6 +20,7 @@ type fileEntry struct { ...@@ -20,6 +20,7 @@ type fileEntry struct {
// space to hold directory stuff // space to hold directory stuff
} }
// rawBridge interconnects nodefs tree structure with raw FUSE exchange.
type rawBridge struct { type rawBridge struct {
fuse.RawFileSystem fuse.RawFileSystem
...@@ -36,7 +37,7 @@ type rawBridge struct { ...@@ -36,7 +37,7 @@ type rawBridge struct {
freeFiles []uint64 freeFiles []uint64
} }
// newInode creates creates new inode pointing to node. // newInode creates new inode pointing to node.
// XXX - should store the Ino number we expose in GetAttr too ? // XXX - should store the Ino number we expose in GetAttr too ?
func (b *rawBridge) newInode(node Node, mode uint32, id FileID, persistent bool) *Inode { func (b *rawBridge) newInode(node Node, mode uint32, id FileID, persistent bool) *Inode {
b.mu.Lock() b.mu.Lock()
...@@ -46,6 +47,8 @@ func (b *rawBridge) newInode(node Node, mode uint32, id FileID, persistent bool) ...@@ -46,6 +47,8 @@ func (b *rawBridge) newInode(node Node, mode uint32, id FileID, persistent bool)
log.Panicf("using reserved ID %d for inode number", id.Ino) log.Panicf("using reserved ID %d for inode number", id.Ino)
} }
// FIXME with automatic IDs this can create 2 inodes for 1 node if
// there are 2 concurrent lookups via different paths.
if id.Ino == 0 { if id.Ino == 0 {
id.Ino = b.automaticIno id.Ino = b.automaticIno
b.automaticIno++ b.automaticIno++
...@@ -153,7 +156,6 @@ func (b *rawBridge) Rmdir(header *fuse.InHeader, name string) fuse.Status { ...@@ -153,7 +156,6 @@ func (b *rawBridge) Rmdir(header *fuse.InHeader, name string) fuse.Status {
parent.RmChild(name) parent.RmChild(name)
} }
return code return code
} }
func (b *rawBridge) Unlink(header *fuse.InHeader, name string) fuse.Status { func (b *rawBridge) Unlink(header *fuse.InHeader, name string) fuse.Status {
...@@ -425,6 +427,9 @@ func (b *rawBridge) Open(input *fuse.OpenIn, out *fuse.OpenOut) (status fuse.Sta ...@@ -425,6 +427,9 @@ func (b *rawBridge) Open(input *fuse.OpenIn, out *fuse.OpenOut) (status fuse.Sta
// //
// XXX is it allowed to return the same Fh from two different Open // XXX is it allowed to return the same Fh from two different Open
// calls on the same inode? // calls on the same inode?
// XXX -> absolutely no, 2 different opens should result in 2 diffrerent Fh.
// an example when not doing so would break badly is e.g. socket-like file-handles,
// where IO on different handle is completey separate from the rest.
func (b *rawBridge) registerFile(f File) uint64 { func (b *rawBridge) registerFile(f File) uint64 {
var fh uint64 var fh uint64
if len(b.freeFiles) > 0 { if len(b.freeFiles) > 0 {
......
...@@ -18,6 +18,10 @@ type DefaultNode struct { ...@@ -18,6 +18,10 @@ type DefaultNode struct {
inode_ *Inode inode_ *Inode
} }
// XXX why we dropped atomics here?
// -> moved rawBridge.newInode() ?
// text: "set/retrieve inode" in na1.patch
func (dn *DefaultNode) setInode(inode *Inode) { func (dn *DefaultNode) setInode(inode *Inode) {
dn.inode_ = inode dn.inode_ = inode
} }
...@@ -26,6 +30,12 @@ func (dn *DefaultNode) inode() *Inode { ...@@ -26,6 +30,12 @@ func (dn *DefaultNode) inode() *Inode {
return dn.inode_ return dn.inode_
} }
func inodeOf(node Node) *Inode {
// XXX .inode = nil -> atomically create new
// XXX requires that we move atomic stuff back to setInode/inode/inodeOf
return node.inode()
}
func (n *DefaultNode) Read(ctx context.Context, f File, dest []byte, off int64) (fuse.ReadResult, fuse.Status) { func (n *DefaultNode) Read(ctx context.Context, f File, dest []byte, off int64) (fuse.ReadResult, fuse.Status) {
if f != nil { if f != nil {
return f.Read(ctx, dest, off) return f.Read(ctx, dest, off)
......
...@@ -37,7 +37,7 @@ type FileID struct { ...@@ -37,7 +37,7 @@ type FileID struct {
Gen uint64 Gen uint64
} }
// Zero returns if the FileID is zeroed out // Reserved returns if the FileID is reserved and should not be used ... XXX
func (i *FileID) Reserved() bool { func (i *FileID) Reserved() bool {
return i.Ino == 0 || i.Ino == 1 || i.Ino == ^uint64(0) return i.Ino == 0 || i.Ino == 1 || i.Ino == ^uint64(0)
} }
...@@ -117,11 +117,12 @@ func nodeLess(a, b *Inode) bool { ...@@ -117,11 +117,12 @@ func nodeLess(a, b *Inode) bool {
// It also avoids locking an inode more than once, if it was specified multiple times. // It also avoids locking an inode more than once, if it was specified multiple times.
// An example when an inode might be given multiple times is if dir/a and dir/b // An example when an inode might be given multiple times is if dir/a and dir/b
// are hardlinked to the same inode and the caller needs to take locks on dir children. // are hardlinked to the same inode and the caller needs to take locks on dir children.
//
// It is valid to give nil nodes - those are simply ignored.
func lockNodes(ns ...*Inode) { func lockNodes(ns ...*Inode) {
sortNodes(ns) sortNodes(ns)
// The default value nil prevents trying to lock nil nodes. var nprev *Inode = nil // initial nil + sort/dedup makes us ignore nil nodes
var nprev *Inode
for _, n := range ns { for _, n := range ns {
if n != nprev { if n != nprev {
n.mu.Lock() n.mu.Lock()
...@@ -133,22 +134,30 @@ func lockNodes(ns ...*Inode) { ...@@ -133,22 +134,30 @@ func lockNodes(ns ...*Inode) {
// lockNode2 locks a and b in order consistent with lockNodes. // lockNode2 locks a and b in order consistent with lockNodes.
func lockNode2(a, b *Inode) { func lockNode2(a, b *Inode) {
if a == b { if a == b {
b = nil
}
if !nodeLess(a, b) {
b, a = a, b
}
if a != nil {
a.mu.Lock() a.mu.Lock()
} else if nodeLess(a, b) { }
a.mu.Lock() if b != nil {
b.mu.Lock()
} else {
b.mu.Lock() b.mu.Lock()
a.mu.Lock()
} }
} }
// unlockNode2 unlocks a and b // unlockNode2 unlocks a and b loced by lockNode2.
func unlockNode2(a, b *Inode) { func unlockNode2(a, b *Inode) {
if a == b { if a == b {
b = nil
}
// there is no deadlock if we unlock nodes in different compared to
// order they were locked.
if a != nil {
a.mu.Unlock() a.mu.Unlock()
} else { }
a.mu.Unlock() if b != nil {
b.mu.Unlock() b.mu.Unlock()
} }
} }
...@@ -159,7 +168,7 @@ func unlockNodes(ns ...*Inode) { ...@@ -159,7 +168,7 @@ func unlockNodes(ns ...*Inode) {
// however it still helps to have nodes sorted to avoid duplicates. // however it still helps to have nodes sorted to avoid duplicates.
sortNodes(ns) sortNodes(ns)
var nprev *Inode var nprev *Inode = nil // initial nil + sort/dedup makes us ignore nil nodes
for _, n := range ns { for _, n := range ns {
if n != nprev { if n != nprev {
n.mu.Unlock() n.mu.Unlock()
...@@ -351,7 +360,7 @@ retry: ...@@ -351,7 +360,7 @@ retry:
// RmChild removes multiple children. Returns whether the removal // RmChild removes multiple children. Returns whether the removal
// succeeded and whether the node is still live afterward. The removal // succeeded and whether the node is still live afterward. The removal
// is transactional: it only succeeds if all names are children, and // is transactional: it only succeeds if all names are in children, and
// if they all were removed successfully. If the removal was // if they all were removed successfully. If the removal was
// successful, and there are no children left, the node may be removed // successful, and there are no children left, the node may be removed
// from the FS tree. In that case, RmChild returns live==false. // from the FS tree. In that case, RmChild returns live==false.
......
...@@ -62,7 +62,7 @@ func (n *loopbackNode) path() string { ...@@ -62,7 +62,7 @@ func (n *loopbackNode) path() string {
return filepath.Join(n.rootNode.root, path) return filepath.Join(n.rootNode.root, path)
} }
func (n *loopbackNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (*Inode, fuse.Status) { func (n *loopbackNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (Node, fuse.Status) {
p := filepath.Join(n.path(), name) p := filepath.Join(n.path(), name)
st := syscall.Stat_t{} st := syscall.Stat_t{}
...@@ -73,11 +73,12 @@ func (n *loopbackNode) Lookup(ctx context.Context, name string, out *fuse.EntryO ...@@ -73,11 +73,12 @@ func (n *loopbackNode) Lookup(ctx context.Context, name string, out *fuse.EntryO
out.Attr.FromStat(&st) out.Attr.FromStat(&st)
node := n.rootNode.newLoopbackNode() node := n.rootNode.newLoopbackNode()
ch := n.inode().NewInode(node, out.Attr.Mode, idFromStat(&st)) return node, fuse.OK
return ch, fuse.OK //ch := n.inode().NewInode(node, out.Attr.Mode, idFromStat(&st))
//return ch, fuse.OK
} }
func (n *loopbackNode) Mknod(ctx context.Context, name string, mode, rdev uint32, out *fuse.EntryOut) (*Inode, fuse.Status) { func (n *loopbackNode) Mknod(ctx context.Context, name string, mode, rdev uint32, out *fuse.EntryOut) (Node, fuse.Status) {
p := filepath.Join(n.path(), name) p := filepath.Join(n.path(), name)
err := syscall.Mknod(p, mode, int(rdev)) err := syscall.Mknod(p, mode, int(rdev))
if err != nil { if err != nil {
...@@ -92,9 +93,10 @@ func (n *loopbackNode) Mknod(ctx context.Context, name string, mode, rdev uint32 ...@@ -92,9 +93,10 @@ func (n *loopbackNode) Mknod(ctx context.Context, name string, mode, rdev uint32
out.Attr.FromStat(&st) out.Attr.FromStat(&st)
node := n.rootNode.newLoopbackNode() node := n.rootNode.newLoopbackNode()
ch := n.inode().NewInode(node, out.Attr.Mode, idFromStat(&st)) return node, fuse.OK
return ch, fuse.OK //ch := n.inode().NewInode(node, out.Attr.Mode, idFromStat(&st))
//return ch, fuse.OK
} }
func (n *loopbackNode) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, fuse.Status) { func (n *loopbackNode) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*Inode, fuse.Status) {
......
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