fuse: reset treeCache on snapshot reload to fix stale latest symlink (#21873)

The treeCache in SnapshotsDir was never cleared when snapshots were
reloaded. This caused the "latest" symlink to keep pointing to the
previous snapshot even after new snapshots were added.

Add a generation counter to SnapshotsDirStructure that is incremented
whenever the directory structure is rebuilt (in makeDirs). The
treeCache checks this generation on each lookup and resets itself
when the generation changes, ensuring cached nodes (including symlinks)
are refreshed after a snapshot reload.
This commit is contained in:
Ricardo Sawir
2026-06-23 01:31:51 +07:00
committed by Michael Eischer
parent add4fa1efb
commit 32be2e559b
7 changed files with 153 additions and 11 deletions
+16 -4
View File
@@ -5,12 +5,15 @@ package fuse
import (
"sync"
"github.com/restic/restic/internal/debug"
"github.com/anacrolix/fuse/fs"
)
type treeCache struct {
nodes map[string]fs.Node
m sync.Mutex
nodes map[string]fs.Node
m sync.Mutex
generation int64
}
type forgetFn func()
@@ -21,19 +24,28 @@ func newTreeCache() *treeCache {
}
}
func (t *treeCache) lookupOrCreate(name string, create func(forget forgetFn) (fs.Node, error)) (fs.Node, error) {
func (t *treeCache) lookupOrCreate(name string, generation int64, create func(forget forgetFn) (fs.Node, error)) (fs.Node, error) {
t.m.Lock()
defer t.m.Unlock()
if generation >= 0 && generation != t.generation {
debug.Log("treeCache generation changed %d -> %d, resetting cache", t.generation, generation)
t.nodes = make(map[string]fs.Node)
t.generation = generation
}
if node, ok := t.nodes[name]; ok {
return node, nil
}
cacheGeneration := t.generation
node, err := create(func() {
t.m.Lock()
defer t.m.Unlock()
delete(t.nodes, name)
if t.generation == cacheGeneration {
delete(t.nodes, name)
}
})
if err != nil {
return nil, err