Merge pull request #5408 from MichaelEischer/fix-walker-crash

walker: fix error handling if tree cannot be loaded
This commit is contained in:
Michael Eischer 2025-07-21 21:46:59 +02:00 committed by GitHub
commit 3433c5abac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 55 additions and 11 deletions

View File

@ -91,6 +91,8 @@ func walk(ctx context.Context, repo restic.BlobLoader, prefix string, parentTree
if err == ErrSkipNode { if err == ErrSkipNode {
continue continue
} }
return err
} }
err = walk(ctx, repo, p, *node.Subtree, subtree, visitor) err = walk(ctx, repo, p, *node.Subtree, subtree, visitor)

View File

@ -8,6 +8,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
) )
// TestTree is used to construct a list of trees for testing the walker. // TestTree is used to construct a list of trees for testing the walker.
@ -93,12 +94,12 @@ func (t TreeMap) Connections() uint {
// checkFunc returns a function suitable for walking the tree to check // checkFunc returns a function suitable for walking the tree to check
// something, and a function which will check the final result. // something, and a function which will check the final result.
type checkFunc func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB)) type checkFunc func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB, error))
// checkItemOrder ensures that the order of the 'path' arguments is the one passed in as 'want'. // checkItemOrder ensures that the order of the 'path' arguments is the one passed in as 'want'.
func checkItemOrder(want []string) checkFunc { func checkItemOrder(want []string) checkFunc {
pos := 0 pos := 0
return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB)) { return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB, error)) {
walker = func(treeID restic.ID, path string, node *restic.Node, err error) error { walker = func(treeID restic.ID, path string, node *restic.Node, err error) error {
if err != nil { if err != nil {
t.Errorf("error walking %v: %v", path, err) t.Errorf("error walking %v: %v", path, err)
@ -121,7 +122,8 @@ func checkItemOrder(want []string) checkFunc {
return walker(restic.ID{}, "leave: "+path, nil, nil) return walker(restic.ID{}, "leave: "+path, nil, nil)
} }
final = func(t testing.TB) { final = func(t testing.TB, err error) {
rtest.OK(t, err)
if pos != len(want) { if pos != len(want) {
t.Errorf("not enough items returned, want %d, got %d", len(want), pos) t.Errorf("not enough items returned, want %d, got %d", len(want), pos)
} }
@ -134,7 +136,7 @@ func checkItemOrder(want []string) checkFunc {
// checkParentTreeOrder ensures that the order of the 'parentID' arguments is the one passed in as 'want'. // checkParentTreeOrder ensures that the order of the 'parentID' arguments is the one passed in as 'want'.
func checkParentTreeOrder(want []string) checkFunc { func checkParentTreeOrder(want []string) checkFunc {
pos := 0 pos := 0
return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB)) { return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB, error)) {
walker = func(treeID restic.ID, path string, node *restic.Node, err error) error { walker = func(treeID restic.ID, path string, node *restic.Node, err error) error {
if err != nil { if err != nil {
t.Errorf("error walking %v: %v", path, err) t.Errorf("error walking %v: %v", path, err)
@ -153,7 +155,8 @@ func checkParentTreeOrder(want []string) checkFunc {
return nil return nil
} }
final = func(t testing.TB) { final = func(t testing.TB, err error) {
rtest.OK(t, err)
if pos != len(want) { if pos != len(want) {
t.Errorf("not enough items returned, want %d, got %d", len(want), pos) t.Errorf("not enough items returned, want %d, got %d", len(want), pos)
} }
@ -168,7 +171,7 @@ func checkParentTreeOrder(want []string) checkFunc {
func checkSkipFor(skipFor map[string]struct{}, wantPaths []string) checkFunc { func checkSkipFor(skipFor map[string]struct{}, wantPaths []string) checkFunc {
var pos int var pos int
return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB)) { return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB, error)) {
walker = func(treeID restic.ID, path string, node *restic.Node, err error) error { walker = func(treeID restic.ID, path string, node *restic.Node, err error) error {
if err != nil { if err != nil {
t.Errorf("error walking %v: %v", path, err) t.Errorf("error walking %v: %v", path, err)
@ -196,7 +199,8 @@ func checkSkipFor(skipFor map[string]struct{}, wantPaths []string) checkFunc {
return walker(restic.ID{}, "leave: "+path, nil, nil) return walker(restic.ID{}, "leave: "+path, nil, nil)
} }
final = func(t testing.TB) { final = func(t testing.TB, err error) {
rtest.OK(t, err)
if pos != len(wantPaths) { if pos != len(wantPaths) {
t.Errorf("wrong number of paths returned, want %d, got %d", len(wantPaths), pos) t.Errorf("wrong number of paths returned, want %d, got %d", len(wantPaths), pos)
} }
@ -206,6 +210,32 @@ func checkSkipFor(skipFor map[string]struct{}, wantPaths []string) checkFunc {
} }
} }
func checkErrorReturned(errForPath string) checkFunc {
expectedErr := fmt.Errorf("error for %v", errForPath)
return func(t testing.TB) (walker WalkFunc, leaveDir func(path string) error, final func(testing.TB, error)) {
walker = func(treeID restic.ID, path string, node *restic.Node, err error) error {
if path == errForPath {
return expectedErr
}
return nil
}
leaveDir = func(path string) error {
return walker(restic.ID{}, "leave: "+path, nil, nil)
}
final = func(t testing.TB, err error) {
if err == nil {
t.Errorf("expected error for %v, got nil", errForPath)
}
rtest.Assert(t, err == expectedErr, "expected error for %v, got %v", errForPath, err)
}
return walker, leaveDir, final
}
}
func TestWalker(t *testing.T) { func TestWalker(t *testing.T) {
var tests = []struct { var tests = []struct {
tree TestTree tree TestTree
@ -427,6 +457,21 @@ func TestWalker(t *testing.T) {
}), }),
}, },
}, },
{
tree: TestTree{
"subdir1": TestTree{
"file": TestFile{},
},
"subdir2": TestTree{},
},
checks: []checkFunc{
checkErrorReturned("/subdir1"),
checkErrorReturned("/subdir2"),
checkErrorReturned("/subdir1/file"),
checkErrorReturned("leave: /subdir1"),
checkErrorReturned("leave: /subdir2"),
},
},
} }
for _, test := range tests { for _, test := range tests {
@ -442,10 +487,7 @@ func TestWalker(t *testing.T) {
ProcessNode: fn, ProcessNode: fn,
LeaveDir: leaveDir, LeaveDir: leaveDir,
}) })
if err != nil { last(t, err)
t.Error(err)
}
last(t)
}) })
} }
}) })