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
|
parentInode uint64
|
||||||
node *restic.Node
|
node *restic.Node
|
||||||
m sync.Mutex
|
m sync.Mutex
|
||||||
|
cache treeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func cleanupNodeName(name string) string {
|
func cleanupNodeName(name string) string {
|
||||||
|
@ -43,6 +44,7 @@ func newDir(root *Root, inode, parentInode uint64, node *restic.Node) (*dir, err
|
||||||
node: node,
|
node: node,
|
||||||
inode: inode,
|
inode: inode,
|
||||||
parentInode: parentInode,
|
parentInode: parentInode,
|
||||||
|
cache: *newTreeCache(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +89,7 @@ func newDirFromSnapshot(root *Root, inode uint64, snapshot *restic.Snapshot) (*d
|
||||||
Subtree: snapshot.Tree,
|
Subtree: snapshot.Tree,
|
||||||
},
|
},
|
||||||
inode: inode,
|
inode: inode,
|
||||||
|
cache: *newTreeCache(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,6 +211,7 @@ func (d *dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return d.cache.lookupOrCreate(name, func() (fs.Node, error) {
|
||||||
node, ok := d.items[name]
|
node, ok := d.items[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
debug.Log(" Lookup(%v) -> not found", name)
|
debug.Log(" Lookup(%v) -> not found", name)
|
||||||
|
@ -227,6 +231,7 @@ func (d *dir) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
||||||
debug.Log(" node %v has unknown type %v", name, node.Type)
|
debug.Log(" node %v has unknown type %v", name, node.Type)
|
||||||
return nil, syscall.ENOENT
|
return nil, syscall.ENOENT
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dir) Listxattr(_ context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error {
|
func (d *dir) Listxattr(_ context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error {
|
||||||
|
|
|
@ -23,6 +23,7 @@ type SnapshotsDir struct {
|
||||||
parentInode uint64
|
parentInode uint64
|
||||||
dirStruct *SnapshotsDirStructure
|
dirStruct *SnapshotsDirStructure
|
||||||
prefix string
|
prefix string
|
||||||
|
cache treeCache
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure that *SnapshotsDir implements these interfaces
|
// ensure that *SnapshotsDir implements these interfaces
|
||||||
|
@ -38,6 +39,7 @@ func NewSnapshotsDir(root *Root, inode, parentInode uint64, dirStruct *Snapshots
|
||||||
parentInode: parentInode,
|
parentInode: parentInode,
|
||||||
dirStruct: dirStruct,
|
dirStruct: dirStruct,
|
||||||
prefix: prefix,
|
prefix: prefix,
|
||||||
|
cache: *newTreeCache(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,8 +109,12 @@ func (d *SnapshotsDir) Lookup(ctx context.Context, name string) (fs.Node, error)
|
||||||
return nil, syscall.ENOENT
|
return nil, syscall.ENOENT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return d.cache.lookupOrCreate(name, func() (fs.Node, error) {
|
||||||
entry := meta.names[name]
|
entry := meta.names[name]
|
||||||
if entry != nil {
|
if entry == nil {
|
||||||
|
return nil, syscall.ENOENT
|
||||||
|
}
|
||||||
|
|
||||||
inode := inodeFromName(d.inode, name)
|
inode := inodeFromName(d.inode, name)
|
||||||
if entry.linkTarget != "" {
|
if entry.linkTarget != "" {
|
||||||
return newSnapshotLink(d.root, inode, entry.linkTarget, entry.snapshot)
|
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 newDirFromSnapshot(d.root, inode, entry.snapshot)
|
||||||
}
|
}
|
||||||
return NewSnapshotsDir(d.root, inode, d.inode, d.dirStruct, d.prefix+"/"+name), nil
|
return NewSnapshotsDir(d.root, inode, d.inode, d.dirStruct, d.prefix+"/"+name), nil
|
||||||
}
|
})
|
||||||
|
|
||||||
return nil, syscall.ENOENT
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SnapshotLink
|
// 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