restic/internal/walker/rewriter.go

98 lines
2.3 KiB
Go
Raw Normal View History

2022-09-06 20:30:45 +00:00
package walker
import (
"context"
"fmt"
2022-09-06 20:30:45 +00:00
"path"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/restic"
)
type NodeRewriteFunc func(node *restic.Node, path string) *restic.Node
2022-09-06 20:30:45 +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
}
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
}
// 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-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)
node = t.opts.RewriteNode(node, path)
if node == nil {
2022-09-06 20:30:45 +00:00
continue
}
if node.Type != "dir" {
2022-09-06 20:30:45 +00:00
err = tb.AddNode(node)
if err != nil {
return restic.ID{}, err
}
continue
}
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
}
}
tree, err := tb.Finalize()
if err != nil {
return restic.ID{}, err
}
2022-09-06 20:30:45 +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)
}
return newTreeID, err
2022-09-06 20:30:45 +00:00
}