forked from TrueCloudLab/restic
fuse: cache fs.Node instances
A particular node should always be represented by a single instance. This is necessary to allow the fuse library to assign a stable nodeId to a node. macOS Sonoma trips over the previous, unstable behavior when using fuse-t.
This commit is contained in:
parent
d8e0384940
commit
75ec7d3269
3 changed files with 71 additions and 24 deletions
|
@ -29,6 +29,7 @@ type dir struct {
|
|||
parentInode uint64
|
||||
node *restic.Node
|
||||
m sync.Mutex
|
||||
cache treeCache
|
||||
}
|
||||
|
||||
func cleanupNodeName(name string) string {
|
||||
|
@ -43,6 +44,7 @@ func newDir(root *Root, inode, parentInode uint64, node *restic.Node) (*dir, err
|
|||
node: node,
|
||||
inode: inode,
|
||||
parentInode: parentInode,
|
||||
cache: *newTreeCache(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -87,6 +89,7 @@ func newDirFromSnapshot(root *Root, inode uint64, snapshot *restic.Snapshot) (*d
|
|||
Subtree: snapshot.Tree,
|
||||
},
|
||||
inode: inode,
|
||||
cache: *newTreeCache(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -208,25 +211,27 @@ func (d *dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
node, ok := d.items[name]
|
||||
if !ok {
|
||||
debug.Log(" Lookup(%v) -> not found", name)
|
||||
return nil, syscall.ENOENT
|
||||
}
|
||||
inode := inodeFromNode(d.inode, node)
|
||||
switch node.Type {
|
||||
case "dir":
|
||||
return newDir(d.root, inode, d.inode, node)
|
||||
case "file":
|
||||
return newFile(d.root, inode, node)
|
||||
case "symlink":
|
||||
return newLink(d.root, inode, node)
|
||||
case "dev", "chardev", "fifo", "socket":
|
||||
return newOther(d.root, inode, node)
|
||||
default:
|
||||
debug.Log(" node %v has unknown type %v", name, node.Type)
|
||||
return nil, syscall.ENOENT
|
||||
}
|
||||
return d.cache.lookupOrCreate(name, func() (fs.Node, error) {
|
||||
node, ok := d.items[name]
|
||||
if !ok {
|
||||
debug.Log(" Lookup(%v) -> not found", name)
|
||||
return nil, syscall.ENOENT
|
||||
}
|
||||
inode := inodeFromNode(d.inode, node)
|
||||
switch node.Type {
|
||||
case "dir":
|
||||
return newDir(d.root, inode, d.inode, node)
|
||||
case "file":
|
||||
return newFile(d.root, inode, node)
|
||||
case "symlink":
|
||||
return newLink(d.root, inode, node)
|
||||
case "dev", "chardev", "fifo", "socket":
|
||||
return newOther(d.root, inode, node)
|
||||
default:
|
||||
debug.Log(" node %v has unknown type %v", name, node.Type)
|
||||
return nil, syscall.ENOENT
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (d *dir) Listxattr(_ context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error {
|
||||
|
|
|
@ -23,6 +23,7 @@ type SnapshotsDir struct {
|
|||
parentInode uint64
|
||||
dirStruct *SnapshotsDirStructure
|
||||
prefix string
|
||||
cache treeCache
|
||||
}
|
||||
|
||||
// ensure that *SnapshotsDir implements these interfaces
|
||||
|
@ -38,6 +39,7 @@ func NewSnapshotsDir(root *Root, inode, parentInode uint64, dirStruct *Snapshots
|
|||
parentInode: parentInode,
|
||||
dirStruct: dirStruct,
|
||||
prefix: prefix,
|
||||
cache: *newTreeCache(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,8 +109,12 @@ func (d *SnapshotsDir) Lookup(ctx context.Context, name string) (fs.Node, error)
|
|||
return nil, syscall.ENOENT
|
||||
}
|
||||
|
||||
entry := meta.names[name]
|
||||
if entry != nil {
|
||||
return d.cache.lookupOrCreate(name, func() (fs.Node, error) {
|
||||
entry := meta.names[name]
|
||||
if entry == nil {
|
||||
return nil, syscall.ENOENT
|
||||
}
|
||||
|
||||
inode := inodeFromName(d.inode, name)
|
||||
if entry.linkTarget != "" {
|
||||
return newSnapshotLink(d.root, inode, entry.linkTarget, entry.snapshot)
|
||||
|
@ -116,9 +122,7 @@ func (d *SnapshotsDir) Lookup(ctx context.Context, name string) (fs.Node, error)
|
|||
return newDirFromSnapshot(d.root, inode, entry.snapshot)
|
||||
}
|
||||
return NewSnapshotsDir(d.root, inode, d.inode, d.dirStruct, d.prefix+"/"+name), nil
|
||||
}
|
||||
|
||||
return nil, syscall.ENOENT
|
||||
})
|
||||
}
|
||||
|
||||
// SnapshotLink
|
||||
|
|
38
internal/fuse/tree_cache.go
Normal file
38
internal/fuse/tree_cache.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
//go:build darwin || freebsd || linux
|
||||
// +build darwin freebsd linux
|
||||
|
||||
package fuse
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/anacrolix/fuse/fs"
|
||||
)
|
||||
|
||||
type treeCache struct {
|
||||
nodes map[string]fs.Node
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
func newTreeCache() *treeCache {
|
||||
return &treeCache{
|
||||
nodes: map[string]fs.Node{},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *treeCache) lookupOrCreate(name string, create func() (fs.Node, error)) (fs.Node, error) {
|
||||
t.m.Lock()
|
||||
defer t.m.Unlock()
|
||||
|
||||
if node, ok := t.nodes[name]; ok {
|
||||
return node, nil
|
||||
}
|
||||
|
||||
node, err := create()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t.nodes[name] = node
|
||||
return node, nil
|
||||
}
|
Loading…
Reference in a new issue