forked from TrueCloudLab/restic
0b39940fdb
It was only used in two places: - stats: apparently as a minor performance optimization, which is unlikely to be important - find: filtered directories would be ignored. However, this optimization missed that it is possible that two directories have the exact same content. Such directories would be incorrectly ignored too. Example: ``` mkdir test test/a test/b restic backup test restic find latest test/b -> incorrectly does not return anything ``` Thus, remove the functionality as it's apparently too complex to use correctly.
91 lines
2.5 KiB
Go
91 lines
2.5 KiB
Go
package walker
|
|
|
|
import (
|
|
"context"
|
|
"path"
|
|
"sort"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/restic/restic/internal/restic"
|
|
)
|
|
|
|
// ErrSkipNode is returned by WalkFunc when a dir node should not be walked.
|
|
var ErrSkipNode = errors.New("skip this node")
|
|
|
|
// WalkFunc is the type of the function called for each node visited by Walk.
|
|
// Path is the slash-separated path from the root node. If there was a problem
|
|
// loading a node, err is set to a non-nil error. WalkFunc can chose to ignore
|
|
// it by returning nil.
|
|
//
|
|
// When the special value ErrSkipNode is returned and node is a dir node, it is
|
|
// not walked. When the node is not a dir node, the remaining items in this
|
|
// tree are skipped.
|
|
type WalkFunc func(parentTreeID restic.ID, path string, node *restic.Node, nodeErr error) (err error)
|
|
|
|
// Walk calls walkFn recursively for each node in root. If walkFn returns an
|
|
// error, it is passed up the call stack. The trees in ignoreTrees are not
|
|
// walked. If walkFn ignores trees, these are added to the set.
|
|
func Walk(ctx context.Context, repo restic.BlobLoader, root restic.ID, walkFn WalkFunc) error {
|
|
tree, err := restic.LoadTree(ctx, repo, root)
|
|
err = walkFn(root, "/", nil, err)
|
|
|
|
if err != nil {
|
|
if err == ErrSkipNode {
|
|
err = nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
return walk(ctx, repo, "/", root, tree, walkFn)
|
|
}
|
|
|
|
// walk recursively traverses the tree, ignoring subtrees when the ID of the
|
|
// subtree is in ignoreTrees. If err is nil and ignore is true, the subtree ID
|
|
// will be added to ignoreTrees by walk.
|
|
func walk(ctx context.Context, repo restic.BlobLoader, prefix string, parentTreeID restic.ID, tree *restic.Tree, walkFn WalkFunc) (err error) {
|
|
sort.Slice(tree.Nodes, func(i, j int) bool {
|
|
return tree.Nodes[i].Name < tree.Nodes[j].Name
|
|
})
|
|
|
|
for _, node := range tree.Nodes {
|
|
p := path.Join(prefix, node.Name)
|
|
|
|
if node.Type == "" {
|
|
return errors.Errorf("node type is empty for node %q", node.Name)
|
|
}
|
|
|
|
if node.Type != "dir" {
|
|
err := walkFn(parentTreeID, p, node, nil)
|
|
if err != nil {
|
|
if err == ErrSkipNode {
|
|
// skip the remaining entries in this tree
|
|
return nil
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
if node.Subtree == nil {
|
|
return errors.Errorf("subtree for node %v in tree %v is nil", node.Name, p)
|
|
}
|
|
|
|
subtree, err := restic.LoadTree(ctx, repo, *node.Subtree)
|
|
err = walkFn(parentTreeID, p, node, err)
|
|
if err != nil {
|
|
if err == ErrSkipNode {
|
|
continue
|
|
}
|
|
}
|
|
|
|
err = walk(ctx, repo, p, *node.Subtree, subtree, walkFn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|