diff --git a/src/cmds/restic/cmd_find.go b/src/cmds/restic/cmd_find.go index 24258d510..683adaa87 100644 --- a/src/cmds/restic/cmd_find.go +++ b/src/cmds/restic/cmd_find.go @@ -61,7 +61,7 @@ func parseTime(str string) (time.Time, error) { func (c CmdFind) findInTree(repo *repository.Repository, id restic.ID, path string) ([]findResult, error) { debug.Log("restic.find", "checking tree %v\n", id) - tree, err := restic.LoadTree(repo, id) + tree, err := repo.LoadTree(id) if err != nil { return nil, err } diff --git a/src/cmds/restic/cmd_ls.go b/src/cmds/restic/cmd_ls.go index 733f424b6..4e3b29e8a 100644 --- a/src/cmds/restic/cmd_ls.go +++ b/src/cmds/restic/cmd_ls.go @@ -47,7 +47,7 @@ func (cmd CmdLs) printNode(prefix string, n *restic.Node) string { } func (cmd CmdLs) printTree(prefix string, repo *repository.Repository, id restic.ID) error { - tree, err := restic.LoadTree(repo, id) + tree, err := repo.LoadTree(id) if err != nil { return err } diff --git a/src/restic/archiver/archive_reader_test.go b/src/restic/archiver/archive_reader_test.go index da46f030a..b402bc6d1 100644 --- a/src/restic/archiver/archive_reader_test.go +++ b/src/restic/archiver/archive_reader_test.go @@ -21,7 +21,7 @@ func loadBlob(t *testing.T, repo *repository.Repository, id restic.ID, buf []byt } func checkSavedFile(t *testing.T, repo *repository.Repository, treeID restic.ID, name string, rd io.Reader) { - tree, err := restic.LoadTree(repo, treeID) + tree, err := repo.LoadTree(treeID) if err != nil { t.Fatalf("LoadTree() returned error %v", err) } diff --git a/src/restic/checker/checker.go b/src/restic/checker/checker.go index 88a8eec8e..df879fdfd 100644 --- a/src/restic/checker/checker.go +++ b/src/restic/checker/checker.go @@ -376,7 +376,7 @@ func loadTreeWorker(repo restic.Repository, } debug.Log("checker.loadTreeWorker", "load tree %v", treeID.Str()) - tree, err := restic.LoadTree(repo, treeID) + tree, err := repo.LoadTree(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 diff --git a/src/restic/find.go b/src/restic/find.go index bfcdbb58f..dcc9d0251 100644 --- a/src/restic/find.go +++ b/src/restic/find.go @@ -6,7 +6,7 @@ package restic func FindUsedBlobs(repo Repository, treeID ID, blobs BlobSet, seen BlobSet) error { blobs.Insert(BlobHandle{ID: treeID, Type: TreeBlob}) - tree, err := LoadTree(repo, treeID) + tree, err := repo.LoadTree(treeID) if err != nil { return err } diff --git a/src/restic/fuse/dir.go b/src/restic/fuse/dir.go index a89617e5f..14f8c7f21 100644 --- a/src/restic/fuse/dir.go +++ b/src/restic/fuse/dir.go @@ -29,7 +29,7 @@ type dir struct { func newDir(repo *repository.Repository, node *restic.Node, ownerIsRoot bool) (*dir, error) { debug.Log("newDir", "new dir for %v (%v)", node.Name, node.Subtree.Str()) - tree, err := restic.LoadTree(repo, *node.Subtree) + tree, err := repo.LoadTree(*node.Subtree) if err != nil { debug.Log("newDir", " error loading tree %v: %v", node.Subtree.Str(), err) return nil, err @@ -59,7 +59,7 @@ func replaceSpecialNodes(repo *repository.Repository, node *restic.Node) ([]*res return []*restic.Node{node}, nil } - tree, err := restic.LoadTree(repo, *node.Subtree) + tree, err := repo.LoadTree(*node.Subtree) if err != nil { return nil, err } @@ -69,7 +69,7 @@ func replaceSpecialNodes(repo *repository.Repository, node *restic.Node) ([]*res func newDirFromSnapshot(repo *repository.Repository, snapshot SnapshotWithId, ownerIsRoot bool) (*dir, error) { debug.Log("newDirFromSnapshot", "new dir for snapshot %v (%v)", snapshot.ID.Str(), snapshot.Tree.Str()) - tree, err := restic.LoadTree(repo, *snapshot.Tree) + tree, err := repo.LoadTree(*snapshot.Tree) if err != nil { debug.Log("newDirFromSnapshot", " loadTree(%v) failed: %v", snapshot.ID.Str(), err) return nil, err diff --git a/src/restic/node.go b/src/restic/node.go index 427ed6303..c4cff8ca5 100644 --- a/src/restic/node.go +++ b/src/restic/node.go @@ -218,10 +218,11 @@ func (node Node) createFileAt(path string, repo Repository) error { buf = make([]byte, size) } - buf, err := repo.LoadBlob(id, DataBlob, buf) + n, err := repo.LoadDataBlob(id, buf) if err != nil { return err } + buf = buf[:n] _, err = f.Write(buf) if err != nil { diff --git a/src/restic/repository.go b/src/restic/repository.go index c0567ec98..5c1067f7b 100644 --- a/src/restic/repository.go +++ b/src/restic/repository.go @@ -31,10 +31,11 @@ type Repository interface { SaveAndEncrypt(BlobType, []byte, *ID) (ID, error) SaveJSONUnpacked(FileType, interface{}) (ID, error) - LoadJSONPack(BlobType, ID, interface{}) error LoadJSONUnpacked(FileType, ID, interface{}) error - LoadBlob(ID, BlobType, []byte) ([]byte, error) LoadAndDecrypt(FileType, ID) ([]byte, error) + + LoadTree(id ID) (*Tree, error) + LoadDataBlob(id ID, buf []byte) (int, error) } // Deleter removes all data stored in a backend/repo. diff --git a/src/restic/repository/repository.go b/src/restic/repository/repository.go index a400b85e3..87025425d 100644 --- a/src/restic/repository/repository.go +++ b/src/restic/repository/repository.go @@ -589,3 +589,45 @@ func (r *Repository) Delete() error { func (r *Repository) Close() error { return r.be.Close() } + +// LoadTree loads a tree from the repository. +func (r *Repository) LoadTree(id restic.ID) (*restic.Tree, error) { + size, err := r.idx.LookupSize(id, restic.TreeBlob) + if err != nil { + return nil, err + } + + buf := make([]byte, size) + + buf, err = r.LoadBlob(id, restic.TreeBlob, nil) + if err != nil { + return nil, err + } + + t := &restic.Tree{} + err = json.Unmarshal(buf, t) + if err != nil { + return nil, err + } + + return t, nil +} + +// LoadDataBlob loads a data blob from the repository to the buffer. +func (r *Repository) LoadDataBlob(id restic.ID, buf []byte) (int, error) { + size, err := r.idx.LookupSize(id, restic.DataBlob) + if err != nil { + return 0, err + } + + if len(buf) < int(size) { + return 0, errors.Errorf("buffer is too small for data blob (%d < %d)", len(buf), size) + } + + buf, err = r.LoadBlob(id, restic.DataBlob, buf) + if err != nil { + return 0, err + } + + return len(buf), err +} diff --git a/src/restic/restorer.go b/src/restic/restorer.go index 7ea7f0123..e3fceb67f 100644 --- a/src/restic/restorer.go +++ b/src/restic/restorer.go @@ -39,7 +39,7 @@ func NewRestorer(repo Repository, id ID) (*Restorer, error) { } func (res *Restorer) restoreTo(dst string, dir string, treeID ID) error { - tree, err := LoadTree(res.repo, treeID) + tree, err := res.repo.LoadTree(treeID) if err != nil { return res.Error(dir, nil, err) } diff --git a/src/restic/tree.go b/src/restic/tree.go index f27393fa8..f2c1c04a9 100644 --- a/src/restic/tree.go +++ b/src/restic/tree.go @@ -25,20 +25,6 @@ func (t Tree) String() string { return fmt.Sprintf("Tree<%d nodes>", len(t.Nodes)) } -type TreeLoader interface { - LoadJSONPack(BlobType, ID, interface{}) error -} - -func LoadTree(repo TreeLoader, id ID) (*Tree, error) { - tree := &Tree{} - err := repo.LoadJSONPack(TreeBlob, id, tree) - if err != nil { - return nil, err - } - - return tree, nil -} - // Equals returns true if t and other have exactly the same nodes. func (t Tree) Equals(other *Tree) bool { if len(t.Nodes) != len(other.Nodes) { @@ -85,6 +71,7 @@ func (t Tree) binarySearch(name string) (int, *Node, error) { return pos, nil, errors.New("named node not found") } +// Find returns a node with the given name. func (t Tree) Find(name string) (*Node, error) { _, node, err := t.binarySearch(name) return node, err diff --git a/src/restic/tree_test.go b/src/restic/tree_test.go index f8d632df9..3c581ec68 100644 --- a/src/restic/tree_test.go +++ b/src/restic/tree_test.go @@ -104,7 +104,7 @@ func TestLoadTree(t *testing.T) { OK(t, repo.Flush()) // load tree again - tree2, err := restic.LoadTree(repo, id) + tree2, err := repo.LoadTree(id) OK(t, err) Assert(t, tree.Equals(tree2), diff --git a/src/restic/walk/walk.go b/src/restic/walk/walk.go index 456c2b9be..fbe322f63 100644 --- a/src/restic/walk/walk.go +++ b/src/restic/walk/walk.go @@ -156,17 +156,21 @@ func loadTreeWorker(wg *sync.WaitGroup, in <-chan loadTreeJob, load treeLoader, } } +// TreeLoader loads tree objects. +type TreeLoader interface { + LoadTree(restic.ID) (*restic.Tree, error) +} + const loadTreeWorkers = 10 // Tree walks the tree specified by id recursively and sends a job for each // file and directory it finds. When the channel done is closed, processing // stops. -func Tree(repo restic.TreeLoader, id restic.ID, done chan struct{}, jobCh chan<- TreeJob) { +func Tree(repo TreeLoader, id restic.ID, done chan struct{}, jobCh chan<- TreeJob) { debug.Log("WalkTree", "start on %v, start workers", id.Str()) load := func(id restic.ID) (*restic.Tree, error) { - tree := &restic.Tree{} - err := repo.LoadJSONPack(restic.TreeBlob, id, tree) + tree, err := repo.LoadTree(id) if err != nil { return nil, err } diff --git a/src/restic/walk/walk_test.go b/src/restic/walk/walk_test.go index 1a33c0ca0..221f5df9c 100644 --- a/src/restic/walk/walk_test.go +++ b/src/restic/walk/walk_test.go @@ -95,9 +95,9 @@ type delayRepo struct { delay time.Duration } -func (d delayRepo) LoadJSONPack(t restic.BlobType, id restic.ID, dst interface{}) error { +func (d delayRepo) LoadTree(id restic.ID) (*restic.Tree, error) { time.Sleep(d.delay) - return d.repo.LoadJSONPack(t, id, dst) + return d.repo.LoadTree(id) } var repoFixture = filepath.Join("testdata", "walktree-test-repo.tar.gz")