Commit 259481ac authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys Committed by Han-Wen Nienhuys

fuse/nodefs: Add TreeWatcher interface.

Use the TreeWatcher interface in pathfs to synchronize the parent
mapping in pathfs with the tree management in nodefs.

Tested:
  loopback -l $mount $orig
  fsstress -p 1 -z -f rmdir=10 -f link=10 -f creat=10 -f mkdir=10 \
    -f rename=30 -f stat=30 -f unlink=30 -f truncate=20 -m 8 -n 1000 \
    -d $mount -s 1456162204 ; sleep 1; echo ok ; rm -Rf $mount

Fixes #99.

Change-Id: I8668571c7dabf6da6ec708eec53de1887864b9ea
parent dfee4d35
...@@ -142,6 +142,15 @@ func (n *Inode) AddChild(name string, child *Inode) { ...@@ -142,6 +142,15 @@ func (n *Inode) AddChild(name string, child *Inode) {
n.mount.treeLock.Unlock() n.mount.treeLock.Unlock()
} }
// TreeWatcher is an additional interface that Nodes can implement.
// If they do, the OnAdd and OnRemove are called for operations on the
// file system tree. The functions run under a lock, so they should
// not do blocking operations.
type TreeWatcher interface {
OnAdd(parent *Inode, name string)
OnRemove(parent *Inode, name string)
}
// RmChild removes an inode by name, and returns it. It returns nil if // RmChild removes an inode by name, and returns it. It returns nil if
// child does not exist. // child does not exist.
func (n *Inode) RmChild(name string) (ch *Inode) { func (n *Inode) RmChild(name string) (ch *Inode) {
...@@ -163,13 +172,19 @@ func (n *Inode) addChild(name string, child *Inode) { ...@@ -163,13 +172,19 @@ func (n *Inode) addChild(name string, child *Inode) {
} }
} }
n.children[name] = child n.children[name] = child
if w, ok := child.Node().(TreeWatcher); ok && child.mountPoint == nil {
w.OnAdd(n, name)
}
} }
// Must be called with treeLock for the mount held. // Must be called with treeLock for the mount held.
func (n *Inode) rmChild(name string) (ch *Inode) { func (n *Inode) rmChild(name string) *Inode {
ch = n.children[name] ch := n.children[name]
if ch != nil { if ch != nil {
delete(n.children, name) delete(n.children, name)
if w, ok := ch.Node().(TreeWatcher); ok && ch.mountPoint == nil {
w.OnRemove(n, name)
}
} }
return ch return ch
} }
......
...@@ -32,7 +32,7 @@ type PathNodeFs struct { ...@@ -32,7 +32,7 @@ type PathNodeFs struct {
root *pathInode root *pathInode
connector *nodefs.FileSystemConnector connector *nodefs.FileSystemConnector
// protects clientInodeMap and pathInode.Parent pointers // protects pathInode.parents
pathLock sync.RWMutex pathLock sync.RWMutex
// This map lists all the parent links known for a given // This map lists all the parent links known for a given
...@@ -188,7 +188,9 @@ func (fs *PathNodeFs) AllFiles(name string, mask uint32) []nodefs.WithFlags { ...@@ -188,7 +188,9 @@ func (fs *PathNodeFs) AllFiles(name string, mask uint32) []nodefs.WithFlags {
// NewPathNodeFs returns a file system that translates from inodes to // NewPathNodeFs returns a file system that translates from inodes to
// path names. // path names.
func NewPathNodeFs(fs FileSystem, opts *PathNodeFsOptions) *PathNodeFs { func NewPathNodeFs(fs FileSystem, opts *PathNodeFsOptions) *PathNodeFs {
root := new(pathInode) root := &pathInode{
parents: map[parentData]struct{}{},
}
root.fs = fs root.fs = fs
if opts == nil { if opts == nil {
...@@ -210,16 +212,19 @@ func (fs *PathNodeFs) Root() nodefs.Node { ...@@ -210,16 +212,19 @@ func (fs *PathNodeFs) Root() nodefs.Node {
return fs.root return fs.root
} }
type parentData struct {
parent *pathInode
name string
}
// This is a combination of dentry (entry in the file/directory and // This is a combination of dentry (entry in the file/directory and
// the inode). This structure is used to implement glue for FSes where // the inode). This structure is used to implement glue for FSes where
// there is a one-to-one mapping of paths and inodes. // there is a one-to-one mapping of paths and inodes.
type pathInode struct { type pathInode struct {
pathFs *PathNodeFs pathFs *PathNodeFs
fs FileSystem fs FileSystem
Name string
// This is nil at the root of the mount. parents map[parentData]struct{}
Parent *pathInode
// This is to correctly resolve hardlinks of the underlying // This is to correctly resolve hardlinks of the underlying
// real filesystem. // real filesystem.
...@@ -251,6 +256,14 @@ func (n *pathInode) Inode() *nodefs.Inode { ...@@ -251,6 +256,14 @@ func (n *pathInode) Inode() *nodefs.Inode {
return n.inode return n.inode
} }
// TODO - return *parentData?
func (n *pathInode) parent() parentData {
for k := range n.parents {
return k
}
return parentData{}
}
func (n *pathInode) SetInode(node *nodefs.Inode) { func (n *pathInode) SetInode(node *nodefs.Inode) {
n.inode = node n.inode = node
} }
...@@ -271,58 +284,69 @@ func (n *pathInode) GetPath() string { ...@@ -271,58 +284,69 @@ func (n *pathInode) GetPath() string {
return "" return ""
} }
pathLen := 0 pathLen := 1
// The simple solution is to collect names, and reverse join // The simple solution is to collect names, and reverse join
// them, them, but since this is a hot path, we take some // them, them, but since this is a hot path, we take some
// effort to avoid allocations. // effort to avoid allocations.
n.pathFs.pathLock.RLock() n.pathFs.pathLock.RLock()
p := n walkUp := n
for ; p.Parent != nil; p = p.Parent {
pathLen += len(p.Name) + 1 // TODO - guess depth? use *parentData?
parents := make([]parentData, 0, 10)
for {
p := walkUp.parent()
if p.parent == nil {
break
}
parents = append(parents, p)
pathLen += len(p.name) + 1
walkUp = p.parent
} }
pathLen-- pathLen--
if p != p.pathFs.root { pathBytes := make([]byte, 0, pathLen)
n.pathFs.pathLock.RUnlock() for i := len(parents) - 1; i >= 0; i-- {
return ".deleted" pathBytes = append(pathBytes, parents[i].name...)
} if i > 0 {
pathBytes = append(pathBytes, '/')
pathBytes := make([]byte, pathLen)
end := len(pathBytes)
for p = n; p.Parent != nil; p = p.Parent {
l := len(p.Name)
copy(pathBytes[end-l:], p.Name)
end -= len(p.Name) + 1
if end > 0 {
pathBytes[end] = '/'
} }
} }
n.pathFs.pathLock.RUnlock() n.pathFs.pathLock.RUnlock()
path := string(pathBytes) path := string(pathBytes)
if n.pathFs.debug { if n.pathFs.debug {
// TODO: print node ID.
log.Printf("Inode = %q (%s)", path, n.fs.String()) log.Printf("Inode = %q (%s)", path, n.fs.String())
} }
if walkUp != n.pathFs.root {
// This might happen if the node has been removed from
// the tree using unlink, but we are forced to run
// some file system operation, because the file is
// still opened.
// TODO - add a deterministic disambiguating suffix.
return ".deleted"
}
return path return path
} }
func (n *pathInode) addChild(name string, child *pathInode) { func (n *pathInode) OnAdd(parent *nodefs.Inode, name string) {
child.Parent = n n.pathFs.pathLock.Lock()
child.Name = name defer n.pathFs.pathLock.Unlock()
pathParent := parent.Node().(*pathInode)
n.parents[parentData{pathParent, name}] = struct{}{}
if child.clientInode > 0 && n.pathFs.options.ClientInodes { if n.clientInode > 0 && n.pathFs.options.ClientInodes {
n.pathFs.pathLock.Lock() m := n.pathFs.clientInodeMap[n.clientInode]
defer n.pathFs.pathLock.Unlock()
m := n.pathFs.clientInodeMap[child.clientInode]
e := &clientInodePath{ e := &clientInodePath{
n, name, child, pathParent, name, n,
} }
m = append(m, e) m = append(m, e)
n.pathFs.clientInodeMap[child.clientInode] = m n.pathFs.clientInodeMap[n.clientInode] = m
} }
} }
...@@ -331,12 +355,20 @@ func (n *pathInode) rmChild(name string) *pathInode { ...@@ -331,12 +355,20 @@ func (n *pathInode) rmChild(name string) *pathInode {
if childInode == nil { if childInode == nil {
return nil return nil
} }
ch := childInode.Node().(*pathInode) return childInode.Node().(*pathInode)
}
func (n *pathInode) OnRemove(parent *nodefs.Inode, name string) {
n.pathFs.pathLock.Lock()
defer n.pathFs.pathLock.Unlock()
if ch.clientInode > 0 && n.pathFs.options.ClientInodes { // TODO - paranoia: what if the cast fails? Can this happen?
n.pathFs.pathLock.Lock() parentPI := parent.Node().(*pathInode)
defer n.pathFs.pathLock.Unlock()
m := n.pathFs.clientInodeMap[ch.clientInode] delete(n.parents, parentData{parentPI, name})
if n.clientInode > 0 && n.pathFs.options.ClientInodes {
m := n.pathFs.clientInodeMap[n.clientInode]
idx := -1 idx := -1
// Find the entry that has us as the parent // Find the entry that has us as the parent
...@@ -351,22 +383,12 @@ func (n *pathInode) rmChild(name string) *pathInode { ...@@ -351,22 +383,12 @@ func (n *pathInode) rmChild(name string) *pathInode {
// last element over it and truncating the slice // last element over it and truncating the slice
m[idx] = m[len(m)-1] m[idx] = m[len(m)-1]
m = m[:len(m)-1] m = m[:len(m)-1]
n.pathFs.clientInodeMap[ch.clientInode] = m n.pathFs.clientInodeMap[n.clientInode] = m
} }
if len(m) > 0 { if len(m) == 0 {
// Reparent to a random remaining entry delete(n.pathFs.clientInodeMap, n.clientInode)
ch.Parent = m[0].parent
ch.Name = m[0].name
return ch
} else {
delete(n.pathFs.clientInodeMap, ch.clientInode)
} }
} }
ch.Name = ".deleted"
ch.Parent = nil
return ch
} }
// Handle a change in clientInode number for an other wise unchanged // Handle a change in clientInode number for an other wise unchanged
...@@ -382,9 +404,9 @@ func (n *pathInode) setClientInode(ino uint64) { ...@@ -382,9 +404,9 @@ func (n *pathInode) setClientInode(ino uint64) {
} }
n.clientInode = ino n.clientInode = ino
if n.Parent != nil { if p := n.parent(); p.parent != nil {
e := &clientInodePath{ e := &clientInodePath{
n.Parent, n.Name, n, p.parent, p.name, n,
} }
n.pathFs.clientInodeMap[ino] = append(n.pathFs.clientInodeMap[ino], e) n.pathFs.clientInodeMap[ino] = append(n.pathFs.clientInodeMap[ino], e)
} }
...@@ -450,7 +472,6 @@ func (n *pathInode) Mknod(name string, mode uint32, dev uint32, context *fuse.Co ...@@ -450,7 +472,6 @@ func (n *pathInode) Mknod(name string, mode uint32, dev uint32, context *fuse.Co
if code.Ok() { if code.Ok() {
pNode := n.createChild(name, false) pNode := n.createChild(name, false)
child = pNode.Inode() child = pNode.Inode()
n.addChild(name, pNode)
} }
return child, code return child, code
} }
...@@ -462,7 +483,6 @@ func (n *pathInode) Mkdir(name string, mode uint32, context *fuse.Context) (*nod ...@@ -462,7 +483,6 @@ func (n *pathInode) Mkdir(name string, mode uint32, context *fuse.Context) (*nod
if code.Ok() { if code.Ok() {
pNode := n.createChild(name, true) pNode := n.createChild(name, true)
child = pNode.Inode() child = pNode.Inode()
n.addChild(name, pNode)
} }
return child, code return child, code
} }
...@@ -470,7 +490,7 @@ func (n *pathInode) Mkdir(name string, mode uint32, context *fuse.Context) (*nod ...@@ -470,7 +490,7 @@ func (n *pathInode) Mkdir(name string, mode uint32, context *fuse.Context) (*nod
func (n *pathInode) Unlink(name string, context *fuse.Context) (code fuse.Status) { func (n *pathInode) Unlink(name string, context *fuse.Context) (code fuse.Status) {
code = n.fs.Unlink(filepath.Join(n.GetPath(), name), context) code = n.fs.Unlink(filepath.Join(n.GetPath(), name), context)
if code.Ok() { if code.Ok() {
n.rmChild(name) n.Inode().RmChild(name)
} }
return code return code
} }
...@@ -478,7 +498,7 @@ func (n *pathInode) Unlink(name string, context *fuse.Context) (code fuse.Status ...@@ -478,7 +498,7 @@ func (n *pathInode) Unlink(name string, context *fuse.Context) (code fuse.Status
func (n *pathInode) Rmdir(name string, context *fuse.Context) (code fuse.Status) { func (n *pathInode) Rmdir(name string, context *fuse.Context) (code fuse.Status) {
code = n.fs.Rmdir(filepath.Join(n.GetPath(), name), context) code = n.fs.Rmdir(filepath.Join(n.GetPath(), name), context)
if code.Ok() { if code.Ok() {
n.rmChild(name) n.Inode().RmChild(name)
} }
return code return code
} }
...@@ -490,7 +510,6 @@ func (n *pathInode) Symlink(name string, content string, context *fuse.Context) ...@@ -490,7 +510,6 @@ func (n *pathInode) Symlink(name string, content string, context *fuse.Context)
if code.Ok() { if code.Ok() {
pNode := n.createChild(name, false) pNode := n.createChild(name, false)
child = pNode.Inode() child = pNode.Inode()
n.addChild(name, pNode)
} }
return child, code return child, code
} }
...@@ -501,10 +520,8 @@ func (n *pathInode) Rename(oldName string, newParent nodefs.Node, newName string ...@@ -501,10 +520,8 @@ func (n *pathInode) Rename(oldName string, newParent nodefs.Node, newName string
newPath := filepath.Join(p.GetPath(), newName) newPath := filepath.Join(p.GetPath(), newName)
code = n.fs.Rename(oldPath, newPath, context) code = n.fs.Rename(oldPath, newPath, context)
if code.Ok() { if code.Ok() {
ch := n.rmChild(oldName) ch := n.Inode().RmChild(oldName)
p.rmChild(newName) p.Inode().AddChild(newName, ch)
p.Inode().AddChild(newName, ch.Inode())
p.addChild(newName, ch)
} }
return code return code
} }
...@@ -529,12 +546,10 @@ func (n *pathInode) Link(name string, existingFsnode nodefs.Node, context *fuse. ...@@ -529,12 +546,10 @@ func (n *pathInode) Link(name string, existingFsnode nodefs.Node, context *fuse.
if existing.clientInode != 0 && existing.clientInode == a.Ino { if existing.clientInode != 0 && existing.clientInode == a.Ino {
child = existing.Inode() child = existing.Inode()
n.Inode().AddChild(name, existing.Inode()) n.Inode().AddChild(name, existing.Inode())
n.addChild(name, existing)
} else { } else {
pNode := n.createChild(name, false) pNode := n.createChild(name, false)
child = pNode.Inode() child = pNode.Inode()
pNode.clientInode = a.Ino pNode.clientInode = a.Ino
n.addChild(name, pNode)
} }
} }
return child, code return child, code
...@@ -547,13 +562,15 @@ func (n *pathInode) Create(name string, flags uint32, mode uint32, context *fuse ...@@ -547,13 +562,15 @@ func (n *pathInode) Create(name string, flags uint32, mode uint32, context *fuse
if code.Ok() { if code.Ok() {
pNode := n.createChild(name, false) pNode := n.createChild(name, false)
child = pNode.Inode() child = pNode.Inode()
n.addChild(name, pNode)
} }
return file, child, code return file, child, code
} }
func (n *pathInode) createChild(name string, isDir bool) *pathInode { func (n *pathInode) createChild(name string, isDir bool) *pathInode {
i := new(pathInode) i := &pathInode{
parents: map[parentData]struct{}{},
}
i.fs = n.fs i.fs = n.fs
i.pathFs = n.pathFs i.pathFs = n.pathFs
...@@ -562,7 +579,8 @@ func (n *pathInode) createChild(name string, isDir bool) *pathInode { ...@@ -562,7 +579,8 @@ func (n *pathInode) createChild(name string, isDir bool) *pathInode {
} }
func (n *pathInode) Open(flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) { func (n *pathInode) Open(flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
file, code = n.fs.Open(n.GetPath(), flags, context) p := n.GetPath()
file, code = n.fs.Open(p, flags, context)
if n.pathFs.debug { if n.pathFs.debug {
file = &nodefs.WithFlags{ file = &nodefs.WithFlags{
File: file, File: file,
...@@ -600,7 +618,6 @@ func (n *pathInode) findChild(fi *fuse.Attr, name string, fullPath string) (out ...@@ -600,7 +618,6 @@ func (n *pathInode) findChild(fi *fuse.Attr, name string, fullPath string) (out
if out == nil { if out == nil {
out = n.createChild(name, fi.IsDir()) out = n.createChild(name, fi.IsDir())
out.clientInode = fi.Ino out.clientInode = fi.Ino
n.addChild(name, out)
} else { } else {
// should add 'out' as a child to n ? // should add 'out' as a child to n ?
} }
......
...@@ -111,7 +111,12 @@ func TestRecursiveMount(t *testing.T) { ...@@ -111,7 +111,12 @@ func TestRecursiveMount(t *testing.T) {
t.Error("expect EBUSY") t.Error("expect EBUSY")
} }
f.Close() if err := f.Close(); err != nil {
t.Errorf("close: %v", err)
}
// We can't avoid a sleep here: the file handle release is not
// synchronized.
t.Log("Waiting for kernel to flush file-close to fuse...") t.Log("Waiting for kernel to flush file-close to fuse...")
time.Sleep(testTtl) time.Sleep(testTtl)
......
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