2022-09-06 20:30:45 +00:00
|
|
|
package walker
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-10-15 08:14:50 +00:00
|
|
|
"fmt"
|
2022-09-06 20:30:45 +00:00
|
|
|
"path"
|
|
|
|
|
|
|
|
"github.com/restic/restic/internal/debug"
|
|
|
|
"github.com/restic/restic/internal/restic"
|
|
|
|
)
|
|
|
|
|
2022-12-28 10:04:28 +00:00
|
|
|
type NodeRewriteFunc func(node *restic.Node, path string) *restic.Node
|
2022-09-06 20:30:45 +00:00
|
|
|
|
2022-12-28 10:04:28 +00:00
|
|
|
type RewriteOpts struct {
|
|
|
|
// return nil to remove the node
|
|
|
|
RewriteNode NodeRewriteFunc
|
|
|
|
}
|
|
|
|
|
|
|
|
type TreeRewriter struct {
|
|
|
|
opts RewriteOpts
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewTreeRewriter(opts RewriteOpts) *TreeRewriter {
|
|
|
|
rw := &TreeRewriter{
|
|
|
|
opts: opts,
|
|
|
|
}
|
|
|
|
// setup default implementations
|
|
|
|
if rw.opts.RewriteNode == nil {
|
|
|
|
rw.opts.RewriteNode = func(node *restic.Node, path string) *restic.Node {
|
|
|
|
return node
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rw
|
2022-09-06 20:30:45 +00:00
|
|
|
}
|
|
|
|
|
2022-10-14 21:26:13 +00:00
|
|
|
type BlobLoadSaver interface {
|
|
|
|
restic.BlobSaver
|
|
|
|
restic.BlobLoader
|
|
|
|
}
|
|
|
|
|
2022-12-28 10:04:28 +00:00
|
|
|
func (t *TreeRewriter) RewriteTree(ctx context.Context, repo BlobLoadSaver, nodepath string, nodeID restic.ID) (newNodeID restic.ID, err error) {
|
2022-09-06 20:30:45 +00:00
|
|
|
curTree, err := restic.LoadTree(ctx, repo, nodeID)
|
|
|
|
if err != nil {
|
|
|
|
return restic.ID{}, err
|
|
|
|
}
|
|
|
|
|
2022-10-15 08:14:50 +00:00
|
|
|
// 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 {
|
2023-04-23 09:38:06 +00:00
|
|
|
return restic.ID{}, fmt.Errorf("cannot encode tree at %q without losing information", nodepath)
|
2022-10-15 08:14:50 +00:00
|
|
|
}
|
|
|
|
|
2022-09-06 20:30:45 +00:00
|
|
|
debug.Log("filterTree: %s, nodeId: %s\n", nodepath, nodeID.Str())
|
|
|
|
|
|
|
|
tb := restic.NewTreeJSONBuilder()
|
|
|
|
for _, node := range curTree.Nodes {
|
|
|
|
path := path.Join(nodepath, node.Name)
|
2022-12-28 10:04:28 +00:00
|
|
|
node = t.opts.RewriteNode(node, path)
|
|
|
|
if node == nil {
|
2022-09-06 20:30:45 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-12-28 09:42:21 +00:00
|
|
|
if node.Type != "dir" {
|
2022-09-06 20:30:45 +00:00
|
|
|
err = tb.AddNode(node)
|
|
|
|
if err != nil {
|
|
|
|
return restic.ID{}, err
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
2022-12-28 10:04:28 +00:00
|
|
|
newID, err := t.RewriteTree(ctx, repo, path, *node.Subtree)
|
2022-09-06 20:30:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return restic.ID{}, err
|
|
|
|
}
|
|
|
|
node.Subtree = &newID
|
|
|
|
err = tb.AddNode(node)
|
|
|
|
if err != nil {
|
|
|
|
return restic.ID{}, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-28 09:38:40 +00:00
|
|
|
tree, err := tb.Finalize()
|
|
|
|
if err != nil {
|
|
|
|
return restic.ID{}, err
|
|
|
|
}
|
2022-09-06 20:30:45 +00:00
|
|
|
|
2022-12-28 09:38:40 +00:00
|
|
|
// Save new tree
|
|
|
|
newTreeID, _, _, err := repo.SaveBlob(ctx, restic.TreeBlob, tree, restic.ID{}, false)
|
|
|
|
if !newTreeID.Equal(nodeID) {
|
2022-09-06 20:30:45 +00:00
|
|
|
debug.Log("filterTree: save new tree for %s as %v\n", nodepath, newTreeID)
|
|
|
|
}
|
2022-12-28 09:38:40 +00:00
|
|
|
return newTreeID, err
|
2022-09-06 20:30:45 +00:00
|
|
|
}
|