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