Commit 74afa852 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

nodefs: use {Gen, Ino} as file ID

This lets filesystems control Generation produced. It also unifies
OpaqueID (used for implementors) NodeID (used for kernel comms) and
the Ino field (reported in Stat/Lstat calls).

For loopback, there is a minor inconvenience, which is that the file
is determined by (Dev, Ino). Get around this by xoring Dev and Ino.
parent 0ba4db2f
......@@ -14,11 +14,6 @@ import (
"golang.org/x/sys/unix"
)
type mapEntry struct {
generation uint64
inode *Inode
}
type fileEntry struct {
file File
......@@ -33,14 +28,12 @@ type rawBridge struct {
// mu protects the following data. Locks for inodes must be
// taken before rawBridge.mu
mu sync.Mutex
nodes []mapEntry
free []uint64
mu sync.Mutex
nodes map[uint64]*Inode
automaticIno uint64
files []fileEntry
freeFiles []uint64
byFileID map[FileID]*Inode
}
// newInode creates creates new inode pointing to node.
......@@ -49,27 +42,35 @@ func (b *rawBridge) newInode(node Node, mode uint32, id FileID, persistent bool)
b.mu.Lock()
defer b.mu.Unlock()
if !id.Zero() {
// the same node can be looked up through 2 paths in parallel, eg.
//
// root
// / \
// dir1 dir2
// \ /
// file
//
// dir1.Lookup("file") and dir2.Lookup("file") are executed
// simultaneously. The matching FileIDs ensure that we return the
// same node.
old := b.byFileID[id]
if old != nil {
return old
}
if id.Reserved() {
log.Panicf("using reserved ID %d for inode number", id.Ino)
}
if id.Ino == 0 {
id.Ino = b.automaticIno
b.automaticIno++
}
// the same node can be looked up through 2 paths in parallel, eg.
//
// root
// / \
// dir1 dir2
// \ /
// file
//
// dir1.Lookup("file") and dir2.Lookup("file") are executed
// simultaneously. The matching FileIDs ensure that we return the
// same node.
old := b.nodes[id.Ino]
if old != nil {
return old
}
inode := &Inode{
mode: mode ^ 07777,
node: node,
nodeID: id,
bridge: b,
persistent: persistent,
parents: make(map[parentData]struct{}),
......@@ -78,10 +79,7 @@ func (b *rawBridge) newInode(node Node, mode uint32, id FileID, persistent bool)
inode.children = make(map[string]*Inode)
}
if !id.Zero() {
b.byFileID[id] = inode
}
b.nodes[id.Ino] = inode
node.setInode(inode)
return inode
}
......@@ -89,7 +87,7 @@ func (b *rawBridge) newInode(node Node, mode uint32, id FileID, persistent bool)
func NewNodeFS(root Node, opts *Options) fuse.RawFileSystem {
bridge := &rawBridge{
RawFileSystem: fuse.NewDefaultRawFileSystem(),
byFileID: make(map[FileID]*Inode),
automaticIno: 1 << 63,
}
if opts != nil {
......@@ -101,7 +99,6 @@ func NewNodeFS(root Node, opts *Options) fuse.RawFileSystem {
}
bridge.root = &Inode{
nodeID: 1,
lookupCount: 1,
mode: fuse.S_IFDIR,
children: make(map[string]*Inode),
......@@ -109,11 +106,11 @@ func NewNodeFS(root Node, opts *Options) fuse.RawFileSystem {
node: root,
bridge: bridge,
}
bridge.root.nodeID.Ino = 1
root.setInode(bridge.root)
bridge.nodes = append(bridge.nodes,
mapEntry{},
// ID 1 is always the root.
mapEntry{inode: bridge.root})
bridge.nodes = map[uint64]*Inode{
1: bridge.root,
}
// Fh 0 means no file handle.
bridge.files = []fileEntry{{}}
......@@ -123,7 +120,7 @@ func NewNodeFS(root Node, opts *Options) fuse.RawFileSystem {
func (b *rawBridge) inode(id uint64, fh uint64) (*Inode, fileEntry) {
b.mu.Lock()
defer b.mu.Unlock()
n, f := b.nodes[id].inode, b.files[fh]
n, f := b.nodes[id], b.files[fh]
if n == nil {
log.Panicf("unknown node %d", id)
}
......@@ -203,18 +200,18 @@ func (b *rawBridge) addNewChild(parent *Inode, name string, child *Inode, file F
lockNodes(parent, child)
parent.setEntry(name, child)
b.mu.Lock()
child.lookupCount++
if child.nodeID == 0 {
b.registerInode(child)
}
var fh uint64
if file != nil {
fh = b.registerFile(file)
}
out.NodeId = child.nodeID
out.Generation = b.nodes[out.NodeId].generation
out.NodeId = child.nodeID.Ino
out.Generation = child.nodeID.Gen
out.Attr.Ino = child.nodeID.Ino
b.mu.Unlock()
unlockNodes(parent, child)
return fh
......@@ -229,25 +226,6 @@ func (b *rawBridge) setEntryOutTimeout(out *fuse.EntryOut) {
}
}
// registerInode sets an nodeID in the child. Must have bridge.mu and
// child.mu
func (b *rawBridge) registerInode(child *Inode) {
if l := len(b.free); l > 0 {
last := b.free[l-1]
b.free = b.free[:l-1]
child.nodeID = last
b.nodes[last].inode = child
b.nodes[last].generation++
} else {
last := len(b.nodes)
b.nodes = append(b.nodes, mapEntry{
inode: child,
})
child.nodeID = uint64(last)
}
}
func (b *rawBridge) Create(input *fuse.CreateIn, name string, out *fuse.CreateOut) (code fuse.Status) {
ctx := context.TODO()
parent, _ := b.inode(input.NodeId, 0)
......@@ -269,6 +247,9 @@ func (b *rawBridge) Create(input *fuse.CreateIn, name string, out *fuse.CreateOu
out.Attr = temp.Attr
out.AttrValid = temp.AttrValid
out.AttrValidNsec = temp.AttrValidNsec
out.Attr.Ino = child.nodeID.Ino
out.Generation = child.nodeID.Gen
out.NodeId = child.nodeID.Ino
b.setEntryOutTimeout(&out.EntryOut)
if out.Attr.Mode&^07777 != fuse.S_IFREG {
......@@ -282,11 +263,6 @@ func (b *rawBridge) Forget(nodeid, nlookup uint64) {
n.removeRef(nlookup, false)
}
func (b *rawBridge) unregisterNode(nodeid uint64) {
b.free = append(b.free, nodeid)
b.nodes[nodeid].inode = nil
}
func (b *rawBridge) SetDebug(debug bool) {}
func (b *rawBridge) GetAttr(input *fuse.GetAttrIn, out *fuse.AttrOut) fuse.Status {
......@@ -299,6 +275,7 @@ func (b *rawBridge) GetAttr(input *fuse.GetAttrIn, out *fuse.AttrOut) fuse.Statu
code := n.node.GetAttr(context.TODO(), f, out)
b.setAttrTimeout(out)
out.Ino = input.NodeId
return code
}
......@@ -372,6 +349,7 @@ func (b *rawBridge) SetAttr(input *fuse.SetAttrIn, out *fuse.AttrOut) (code fuse
// the changes we effect here.
code = n.node.GetAttr(ctx, f, out)
b.setAttrTimeout(out)
out.Ino = n.nodeID.Ino
return code
}
......
......@@ -21,20 +21,25 @@ type parentData struct {
}
// FileID provides a identifier for file objects defined by FUSE
// filesystems. The identifier is divided into a (Device, Inode) pair,
// so files in underlying file systems can easily be represented, but
// FUSE filesystems are free to use any other information as 128-bit
// key.
//
// XXX name: PersistentID ? NodeID ?
// filesystems.
type FileID struct {
Dev uint64
// The inode number must be unique among the currently live
// objects in the file system. It is used to communicate to
// the kernel about this file object. The values uint64(-1),
// and 1 are reserved. When using Ino==0, a unique, sequential
// number is assigned (starting at 2^63)
Ino uint64
// When reusing a previously used inode number for a new
// object, the new object must have a different Gen
// number. This is irrelevant if the FS is not exported over
// NFS
Gen uint64
}
// Zero returns if the FileID is zeroed out
func (i *FileID) Zero() bool {
return i.Dev == 0 && i.Ino == 0
func (i *FileID) Reserved() bool {
return i.Ino == 0 || i.Ino == 1 || i.Ino == ^uint64(0)
}
// Inode is a node in VFS tree. Inodes are one-to-one mapped to Node
......@@ -45,7 +50,7 @@ type Inode struct {
// The filetype bits from the mode.
mode uint32
opaqueID FileID
nodeID FileID
node Node
bridge *rawBridge
......@@ -57,10 +62,6 @@ type Inode struct {
// lockNodes/unlockNodes
mu sync.Mutex
// ID of the inode for talking to the kernel, 0 if the kernel
// does not know this inode.
nodeID uint64
// persistent indicates that this node should not be removed
// from the tree, even if there are no live references. This
// must be set on creation, and can only be changed to false
......@@ -86,7 +87,7 @@ type Inode struct {
func (n *Inode) debugString() string {
var ss []string
for nm, ch := range n.children {
ss = append(ss, fmt.Sprintf("%q=%d(%d)", nm, ch.nodeID, ch.opaqueID))
ss = append(ss, fmt.Sprintf("%q=%d", nm, ch.nodeID.Ino))
}
return fmt.Sprintf("%d: %s", n.nodeID, strings.Join(ss, ","))
......@@ -172,9 +173,9 @@ func unlockNodes(ns ...*Inode) {
// kernel has no way of reviving forgotten nodes by its own
// initiative.
func (n *Inode) Forgotten() bool {
n.bridge.mu.Lock()
defer n.bridge.mu.Unlock()
return n.nodeID == 0
n.mu.Lock()
defer n.mu.Unlock()
return n.lookupCount == 0 && len(n.parents) == 0 && !n.persistent
}
// Node returns the Node object implementing the file system operations.
......@@ -333,13 +334,7 @@ retry:
}
n.bridge.mu.Lock()
if n.nodeID != 0 {
n.bridge.unregisterNode(n.nodeID)
n.nodeID = 0
}
if !n.opaqueID.Zero() {
delete(n.bridge.byFileID, n.opaqueID)
}
delete(n.bridge.nodes, n.nodeID.Ino)
n.bridge.mu.Unlock()
unlockNodes(lockme...)
......
......@@ -72,14 +72,8 @@ func (n *loopbackNode) Lookup(ctx context.Context, name string, out *fuse.EntryO
}
out.Attr.FromStat(&st)
opaque := FileID{
Dev: uint64(out.Attr.Rdev),
Ino: out.Attr.Ino,
}
node := n.rootNode.newLoopbackNode()
ch := n.inode().NewInode(node, out.Attr.Mode, opaque)
ch := n.inode().NewInode(node, out.Attr.Mode, idFromStat(&st))
return ch, fuse.OK
}
......@@ -98,11 +92,7 @@ func (n *loopbackNode) Mknod(ctx context.Context, name string, mode, rdev uint32
out.Attr.FromStat(&st)
node := n.rootNode.newLoopbackNode()
opaque := FileID{
Dev: uint64(out.Attr.Rdev),
Ino: out.Attr.Ino,
}
ch := n.inode().NewInode(node, out.Attr.Mode, opaque)
ch := n.inode().NewInode(node, out.Attr.Mode, idFromStat(&st))
return ch, fuse.OK
}
......@@ -123,11 +113,7 @@ func (n *loopbackNode) Mkdir(ctx context.Context, name string, mode uint32, out
out.Attr.FromStat(&st)
node := n.rootNode.newLoopbackNode()
opaque := FileID{
Dev: uint64(out.Attr.Rdev),
Ino: out.Attr.Ino,
}
ch := n.inode().NewInode(node, out.Attr.Mode, opaque)
ch := n.inode().NewInode(node, out.Attr.Mode, idFromStat(&st))
return ch, fuse.OK
}
......@@ -163,6 +149,15 @@ func (n *loopbackNode) Rename(ctx context.Context, name string, newParent Node,
return fuse.ToStatus(err)
}
func idFromStat(st *syscall.Stat_t) FileID {
return FileID{
Gen: 1,
// This should work well for traditional backing FSes,
// not so much for other go-fuse FS-es
Ino: uint64(st.Dev)<<32 ^ st.Ino,
}
}
func (n *loopbackNode) Create(ctx context.Context, name string, flags uint32, mode uint32) (inode *Inode, fh File, fuseFlags uint32, code fuse.Status) {
p := filepath.Join(n.path(), name)
......@@ -178,12 +173,7 @@ func (n *loopbackNode) Create(ctx context.Context, name string, flags uint32, mo
}
node := n.rootNode.newLoopbackNode()
opaque := FileID{
Dev: st.Rdev,
Ino: st.Ino,
}
ch := n.inode().NewInode(node, st.Mode, opaque)
ch := n.inode().NewInode(node, st.Mode, idFromStat(&st))
lf := newLoopbackFile(f)
n.mu.Lock()
defer n.mu.Unlock()
......
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