forked from TrueCloudLab/restic
1a9705fc95
Now the rewritten tree is always serialized which makes sure that we don't accidentally miss any relevant changes.
84 lines
2.2 KiB
Go
84 lines
2.2 KiB
Go
package walker
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"path"
|
|
|
|
"github.com/restic/restic/internal/debug"
|
|
"github.com/restic/restic/internal/restic"
|
|
)
|
|
|
|
// SelectByNameFunc returns true for all items that should be included (files and
|
|
// dirs). If false is returned, files are ignored and dirs are not even walked.
|
|
type SelectByNameFunc func(item string) bool
|
|
|
|
type TreeFilterVisitor struct {
|
|
SelectByName SelectByNameFunc
|
|
PrintExclude func(string)
|
|
}
|
|
|
|
type BlobLoadSaver interface {
|
|
restic.BlobSaver
|
|
restic.BlobLoader
|
|
}
|
|
|
|
func FilterTree(ctx context.Context, repo BlobLoadSaver, nodepath string, nodeID restic.ID, visitor *TreeFilterVisitor) (newNodeID restic.ID, err error) {
|
|
curTree, err := restic.LoadTree(ctx, repo, nodeID)
|
|
if err != nil {
|
|
return restic.ID{}, err
|
|
}
|
|
|
|
// check that we can properly encode this tree without losing information
|
|
// The alternative of using json/Decoder.DisallowUnknownFields() doesn't work as we use
|
|
// a custom UnmarshalJSON to decode trees, see also https://github.com/golang/go/issues/41144
|
|
testID, err := restic.SaveTree(ctx, repo, curTree)
|
|
if err != nil {
|
|
return restic.ID{}, err
|
|
}
|
|
if nodeID != testID {
|
|
return restic.ID{}, fmt.Errorf("cannot encode tree at %q without losing information", nodepath)
|
|
}
|
|
|
|
debug.Log("filterTree: %s, nodeId: %s\n", nodepath, nodeID.Str())
|
|
|
|
tb := restic.NewTreeJSONBuilder()
|
|
for _, node := range curTree.Nodes {
|
|
path := path.Join(nodepath, node.Name)
|
|
if !visitor.SelectByName(path) {
|
|
if visitor.PrintExclude != nil {
|
|
visitor.PrintExclude(path)
|
|
}
|
|
continue
|
|
}
|
|
|
|
if node.Subtree == nil {
|
|
err = tb.AddNode(node)
|
|
if err != nil {
|
|
return restic.ID{}, err
|
|
}
|
|
continue
|
|
}
|
|
newID, err := FilterTree(ctx, repo, path, *node.Subtree, visitor)
|
|
if err != nil {
|
|
return restic.ID{}, err
|
|
}
|
|
node.Subtree = &newID
|
|
err = tb.AddNode(node)
|
|
if err != nil {
|
|
return restic.ID{}, err
|
|
}
|
|
}
|
|
|
|
tree, err := tb.Finalize()
|
|
if err != nil {
|
|
return restic.ID{}, err
|
|
}
|
|
|
|
// Save new tree
|
|
newTreeID, _, _, err := repo.SaveBlob(ctx, restic.TreeBlob, tree, restic.ID{}, false)
|
|
if !newTreeID.Equal(nodeID) {
|
|
debug.Log("filterTree: save new tree for %s as %v\n", nodepath, newTreeID)
|
|
}
|
|
return newTreeID, err
|
|
}
|