func printTrees(repo *repository.Repository, wr io.Writer) error { done := make(chan struct{}) defer close(done) trees := []backend.ID{} for _, idx := range repo.Index().All() { for blob := range idx.Each(nil) { if blob.Type != pack.Tree { continue } trees = append(trees, blob.ID) } } for _, id := range trees { tree, err := restic.LoadTree(repo, id) if err != nil { fmt.Fprintf(os.Stderr, "LoadTree(%v): %v", id.Str(), err) continue } fmt.Fprintf(wr, "tree_id: %v\n", id) prettyPrintJSON(wr, tree) } return nil }
func newDirFromSnapshot(repo *repository.Repository, snapshot SnapshotWithId, ownerIsRoot bool) (*dir, error) { tree, err := restic.LoadTree(repo, *snapshot.Tree) if err != nil { return nil, err } items := make(map[string]*restic.Node) for _, node := range tree.Nodes { items[node.Name] = node } return &dir{ repo: repo, node: &restic.Node{ UID: uint32(os.Getuid()), GID: uint32(os.Getgid()), AccessTime: snapshot.Time, ModTime: snapshot.Time, ChangeTime: snapshot.Time, Mode: os.ModeDir | 0555, }, items: items, inode: inodeFromBackendId(snapshot.ID), ownerIsRoot: ownerIsRoot, }, nil }
func newDirFromSnapshot(repo *repository.Repository, snapshot SnapshotWithId) (*dir, error) { tree, err := restic.LoadTree(repo, snapshot.Tree) if err != nil { return nil, err } items := make(map[string]*restic.Node) for _, node := range tree.Nodes { items[node.Name] = node } return &dir{ repo: repo, items: items, inode: inodeFromBackendId(snapshot.ID), }, nil }
func newDir(repo *repository.Repository, node *restic.Node) (*dir, error) { tree, err := restic.LoadTree(repo, node.Subtree) if err != nil { return nil, err } items := make(map[string]*restic.Node) for _, node := range tree.Nodes { items[node.Name] = node } return &dir{ repo: repo, items: items, inode: node.Inode, }, nil }
// replaceSpecialNodes replaces nodes with name "." and "/" by their contents. // Otherwise, the node is returned. func replaceSpecialNodes(repo *repository.Repository, node *restic.Node) ([]*restic.Node, error) { if node.Type != "dir" || node.Subtree == nil { return []*restic.Node{node}, nil } if node.Name != "." && node.Name != "/" { return []*restic.Node{node}, nil } tree, err := restic.LoadTree(repo, *node.Subtree) if err != nil { return nil, err } return tree.Nodes, nil }
func (c CmdFind) findInTree(repo *repository.Repository, id backend.ID, path string) ([]findResult, error) { debug.Log("restic.find", "checking tree %v\n", id) tree, err := restic.LoadTree(repo, id) if err != nil { return nil, err } results := []findResult{} for _, node := range tree.Nodes { debug.Log("restic.find", " testing entry %q\n", node.Name) m, err := filepath.Match(c.pattern, node.Name) if err != nil { return nil, err } if m { debug.Log("restic.find", " pattern matches\n") if !c.oldest.IsZero() && node.ModTime.Before(c.oldest) { debug.Log("restic.find", " ModTime is older than %s\n", c.oldest) continue } if !c.newest.IsZero() && node.ModTime.After(c.newest) { debug.Log("restic.find", " ModTime is newer than %s\n", c.newest) continue } results = append(results, findResult{node: node, path: path}) } else { debug.Log("restic.find", " pattern does not match\n") } if node.Type == "dir" { subdirResults, err := c.findInTree(repo, *node.Subtree, filepath.Join(path, node.Name)) if err != nil { return nil, err } results = append(results, subdirResults...) } } return results, nil }
func (cmd CmdLs) printTree(prefix string, repo *repository.Repository, id backend.ID) error { tree, err := restic.LoadTree(repo, id) if err != nil { return err } for _, entry := range tree.Nodes { cmd.global.Printf(cmd.printNode(prefix, entry) + "\n") if entry.Type == "dir" && entry.Subtree != nil { err = cmd.printTree(filepath.Join(prefix, entry.Name), repo, entry.Subtree) if err != nil { return err } } } return nil }
func printTree(prefix string, repo *repository.Repository, id backend.ID) error { tree, err := restic.LoadTree(repo, id) if err != nil { return err } for _, entry := range tree.Nodes { fmt.Println(printNode(prefix, entry)) if entry.Type == "dir" && entry.Subtree != nil { err = printTree(filepath.Join(prefix, entry.Name), repo, entry.Subtree) if err != nil { return err } } } return nil }
func TestLoadTree(t *testing.T) { repo := SetupRepo() defer TeardownRepo(repo) // save tree tree := restic.NewTree() id, err := repo.SaveJSON(pack.Tree, tree) OK(t, err) // save packs OK(t, repo.Flush()) // load tree again tree2, err := restic.LoadTree(repo, id) OK(t, err) Assert(t, tree.Equals(tree2), "trees are not equal: want %v, got %v", tree, tree2) }
func BenchmarkLoadTree(t *testing.B) { repo := SetupRepo() defer TeardownRepo(repo) if BenchArchiveDirectory == "" { t.Skip("benchdir not set, skipping TestArchiverDedup") } // archive a few files arch := restic.NewArchiver(repo) sn, _, err := arch.Snapshot(nil, []string{BenchArchiveDirectory}, nil) OK(t, err) t.Logf("archived snapshot %v", sn.ID()) list := make([]backend.ID, 0, 10) done := make(chan struct{}) for _, idx := range repo.Index().All() { for blob := range idx.Each(done) { if blob.Type != pack.Tree { continue } list = append(list, blob.ID) if len(list) == cap(list) { close(done) break } } } // start benchmark t.ResetTimer() for i := 0; i < t.N; i++ { for _, id := range list { _, err := restic.LoadTree(repo, id) OK(t, err) } } }
// loadTreeWorker loads trees from repo and sends them to out. func loadTreeWorker(repo *repository.Repository, in <-chan backend.ID, out chan<- treeJob, done <-chan struct{}, wg *sync.WaitGroup) { defer func() { debug.Log("checker.loadTreeWorker", "exiting") wg.Done() }() var ( inCh = in outCh = out job treeJob ) outCh = nil for { select { case <-done: return case treeID, ok := <-inCh: if !ok { return } debug.Log("checker.loadTreeWorker", "load tree %v", treeID.Str()) tree, err := restic.LoadTree(repo, treeID) debug.Log("checker.loadTreeWorker", "load tree %v (%v) returned err %v", tree, treeID.Str(), err) job = treeJob{ID: treeID, error: err, Tree: tree} outCh = out inCh = nil case outCh <- job: debug.Log("checker.loadTreeWorker", "sent tree %v", job.ID.Str()) outCh = nil inCh = in } } }
func fsckTree(global CmdFsck, repo *repository.Repository, id backend.ID) error { debug.Log("restic.fsckTree", "checking tree %v", id.Str()) tree, err := restic.LoadTree(repo, id) if err != nil { return err } // if orphan check is active, record storage id if global.o_trees != nil { // add ID to list global.o_trees.Insert(id) } var firstErr error seenIDs := backend.NewIDSet() for i, node := range tree.Nodes { if node.Name == "" { return fmt.Errorf("node %v of tree %v has no name", i, id.Str()) } if node.Type == "" { return fmt.Errorf("node %q of tree %v has no type", node.Name, id.Str()) } switch node.Type { case "file": if node.Content == nil { debug.Log("restic.fsckTree", "file node %q of tree %v has no content: %v", node.Name, id, node) return fmt.Errorf("file node %q of tree %v has no content: %v", node.Name, id, node) } if node.Content == nil && node.Error == "" { debug.Log("restic.fsckTree", "file node %q of tree %v has no content", node.Name, id) return fmt.Errorf("file node %q of tree %v has no content", node.Name, id) } // record ids for _, id := range node.Content { seenIDs.Insert(id) } debug.Log("restic.fsckTree", "check file %v (%v)", node.Name, id.Str()) bytes, err := fsckFile(global, repo, node.Content) if err != nil { return err } if bytes != node.Size { debug.Log("restic.fsckTree", "file node %q of tree %v has size %d, but only %d bytes could be found", node.Name, id, node.Size, bytes) return fmt.Errorf("file node %q of tree %v has size %d, but only %d bytes could be found", node.Name, id, node.Size, bytes) } case "dir": if node.Subtree == nil { return fmt.Errorf("dir node %q of tree %v has no subtree", node.Name, id) } // record id seenIDs.Insert(node.Subtree) err = fsckTree(global, repo, node.Subtree) if err != nil { firstErr = err fmt.Fprintf(os.Stderr, "%v\n", err) } } } // check map for unused ids // for _, id := range tree.Map.IDs() { // if seenIDs.Find(id) != nil { // return fmt.Errorf("tree %v: map contains unused ID %v", id, id) // } // } return firstErr }