Commit 6323032a authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Fix lookupCount handling errors:

* Introduce Inode.addLookupCount(), checking for underflow. 

* Increase lookupCount for mounts only once

* If FsNode.Link() returns existing node, increase its lookup count.

* Root node should have lookupCount = 1 at start.

* If we get a Lookup for an existing node, increase its lookup count.
parent ff93cf0e
......@@ -44,6 +44,10 @@ func NewFileSystemConnector(nodeFs NodeFileSystem, opts *FileSystemOptions) (me
me.inodeMap = NewHandleMap(!opts.SkipCheckHandles)
me.rootNode = me.newInode(true)
me.rootNode.nodeId = FUSE_ROOT_ID
// FUSE does not issue a LOOKUP for 1 (obviously), but it does
// issue a forget. This lookupCount is to make the counts match.
me.rootNode.addLookupCount(1)
me.verify()
me.MountRoot(nodeFs, opts)
return me
......@@ -89,32 +93,26 @@ func (me *FileSystemConnector) lookupUpdate(parent *Inode, name string, isDir bo
data.mount = parent.mount
data.treeLock = &data.mount.treeLock
}
data.lookupCount += lookupCount
data.addLookupCount(lookupCount)
return data
}
func (me *FileSystemConnector) lookupMount(parent *Inode, name string, lookupCount int) (mount *fileSystemMount) {
func (me *FileSystemConnector) findMount(parent *Inode, name string) (mount *fileSystemMount) {
parent.treeLock.RLock()
defer parent.treeLock.RUnlock()
if parent.mounts == nil {
return nil
}
mount, ok := parent.mounts[name]
if ok {
mount.treeLock.Lock()
defer mount.treeLock.Unlock()
mount.mountInode.lookupCount += lookupCount
return mount
}
return nil
return parent.mounts[name]
}
func (me *FileSystemConnector) getInodeData(nodeid uint64) *Inode {
if nodeid == FUSE_ROOT_ID {
return me.rootNode
}
return (*Inode)(unsafe.Pointer(DecodeHandle(nodeid)))
i := (*Inode)(unsafe.Pointer(DecodeHandle(nodeid)))
return i
}
func (me *FileSystemConnector) forgetUpdate(nodeId uint64, forgetCount int) {
......@@ -125,7 +123,7 @@ func (me *FileSystemConnector) forgetUpdate(nodeId uint64, forgetCount int) {
node.treeLock.Lock()
defer node.treeLock.Unlock()
node.lookupCount -= forgetCount
node.addLookupCount(-forgetCount)
me.considerDropInode(node)
}
......@@ -142,6 +140,7 @@ func (me *FileSystemConnector) considerDropInode(n *Inode) (drop bool) {
panic(fmt.Sprintf("trying to del child %q, but not present", k))
}
me.inodeMap.Forget(ch.nodeId)
ch.nodeId = 0xdeadbeef
}
if len(n.children) > 0 || n.lookupCount > 0 {
......
......@@ -21,7 +21,7 @@ func (me *FileSystemConnector) Lookup(header *InHeader, name string) (out *Entry
return out, status
}
func (me *FileSystemConnector) internalMountLookup(mount *fileSystemMount, lookupCount int) (out *EntryOut, status Status, node *Inode) {
func (me *FileSystemConnector) lookupMountUpdate(mount *fileSystemMount, lookupCount int) (out *EntryOut, status Status, node *Inode) {
fi, err := mount.fs.Root().GetAttr(nil, nil)
if err == ENOENT && mount.options.NegativeTimeout > 0.0 {
return NegativeEntry(mount.options.NegativeTimeout), OK, nil
......@@ -31,7 +31,9 @@ func (me *FileSystemConnector) internalMountLookup(mount *fileSystemMount, looku
}
mount.treeLock.Lock()
defer mount.treeLock.Unlock()
mount.mountInode.lookupCount += lookupCount
mount.mountInode.addLookupCount(lookupCount)
out = mount.fileInfoToEntry(fi)
out.NodeId = mount.mountInode.nodeId
......@@ -41,8 +43,8 @@ func (me *FileSystemConnector) internalMountLookup(mount *fileSystemMount, looku
}
func (me *FileSystemConnector) internalLookup(parent *Inode, name string, lookupCount int, context *Context) (out *EntryOut, code Status, node *Inode) {
if mount := me.lookupMount(parent, name, lookupCount); mount != nil {
return me.internalMountLookup(mount, lookupCount)
if mount := me.findMount(parent, name); mount != nil {
return me.lookupMountUpdate(mount, lookupCount)
}
var fi *os.FileInfo
......@@ -59,6 +61,9 @@ func (me *FileSystemConnector) internalLookup(parent *Inode, name string, lookup
}
if child != nil && code.Ok() {
mount.treeLock.Lock()
defer mount.treeLock.Unlock()
child.addLookupCount(1)
out = parent.mount.fileInfoToEntry(fi)
out.NodeId = child.nodeId
out.Generation = 1
......@@ -239,7 +244,7 @@ func (me *FileSystemConnector) Symlink(header *InHeader, pointedTo string, linkN
func (me *FileSystemConnector) Rename(header *InHeader, input *RenameIn, oldName string, newName string) (code Status) {
oldParent := me.getInodeData(header.NodeId)
isMountPoint := me.lookupMount(oldParent, oldName, 0) != nil
isMountPoint := me.findMount(oldParent, oldName) != nil
if isMountPoint {
return EBUSY
}
......@@ -272,6 +277,7 @@ func (me *FileSystemConnector) Link(header *InHeader, input *LinkIn, name string
if fsInode.Inode() == nil {
out, _ = me.createChild(parent, name, fi, fsInode)
} else {
fsInode.Inode().addLookupCount(1)
out = parent.mount.fileInfoToEntry(fi)
out.Ino = fsInode.Inode().nodeId
out.NodeId = out.Ino
......
......@@ -154,7 +154,7 @@ func (me *int64HandleMap) Forget(handle uint64) (val *Handled) {
me.mutex.Lock()
defer me.mutex.Unlock()
val.check = 0
val.check = 0xdeadbeef
me.handles[handle] = nil, false
return val
}
......
......@@ -34,6 +34,8 @@ type Inode struct {
// Contains directories that function as mounts. The entries
// are duplicated in children.
mounts map[string]*fileSystemMount
// Use addLookupCount() to manipulate.
lookupCount int
// Non-nil if this is a mountpoint.
......@@ -116,7 +118,7 @@ func (me *Inode) CreateChild(name string, isDir bool, fsi FsNode) *Inode {
fsi.SetInode(ch)
ch.mount = me.mount
ch.treeLock = me.treeLock
ch.lookupCount = 1
ch.addLookupCount(1)
ch.connector = me.connector
me.addChild(name, ch)
......@@ -130,6 +132,16 @@ func (me *Inode) GetChild(name string) (child *Inode) {
return me.children[name]
}
////////////////////////////////////////////////////////////////
// private
func (me *Inode) addLookupCount(delta int) {
me.lookupCount += delta
if me.lookupCount < 0 {
panic(fmt.Sprintf("lookupCount underflow: %d: %v", me.lookupCount, me))
}
}
// Must be called with treeLock for the mount held.
func (me *Inode) addChild(name string, child *Inode) {
if paranoia {
......@@ -203,6 +215,9 @@ func (me *Inode) getMountDirEntries() (out []DirEntry) {
const initDirSize = 20
func (me *Inode) verify(cur *fileSystemMount) {
if me.lookupCount < 0 {
panic("negative lookup count")
}
if me.mountPoint != nil {
if me != me.mountPoint.mountInode {
panic("mountpoint mismatch")
......
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