diff --git a/progress.go b/progress.go index 9f2a5a878..9d61320f9 100644 --- a/progress.go +++ b/progress.go @@ -34,7 +34,7 @@ type Stat struct { type ProgressFunc func(s Stat, runtime time.Duration, ticker bool) -// NewProgress returns a new progress reporter. When Start() called, the +// NewProgress returns a new progress reporter. When Start() is called, the // function OnStart is executed once. Afterwards the function OnUpdate is // called when new data arrives or at least every d interval. The function // OnDone is called when Done() is called. Both functions are called @@ -43,7 +43,7 @@ func NewProgress(d time.Duration) *Progress { return &Progress{d: d} } -// Start runs resets and runs the progress reporter. +// Start resets and runs the progress reporter. func (p *Progress) Start() { if p == nil || p.running { return @@ -63,6 +63,21 @@ func (p *Progress) Start() { go p.reporter() } +// Reset resets all statistic counters to zero. +func (p *Progress) Reset() { + if p == nil { + return + } + + if !p.running { + panic("resetting a non-running Progress") + } + + p.curM.Lock() + p.cur = Stat{} + p.curM.Unlock() +} + // Report adds the statistics from s to the current state and tries to report // the accumulated statistics via the feedback channel. func (p *Progress) Report(s Stat) { @@ -79,12 +94,17 @@ func (p *Progress) Report(s Stat) { cur := p.cur p.curM.Unlock() - // update progress - if p.OnUpdate != nil { - p.fnM.Lock() - p.OnUpdate(cur, time.Since(p.start), false) - p.fnM.Unlock() + p.updateProgress(cur, false) +} + +func (p *Progress) updateProgress(cur Stat, ticker bool) { + if p.OnUpdate == nil { + return } + + p.fnM.Lock() + p.OnUpdate(cur, time.Since(p.start), ticker) + p.fnM.Unlock() } func (p *Progress) reporter() { @@ -98,12 +118,7 @@ func (p *Progress) reporter() { p.curM.Lock() cur := p.cur p.curM.Unlock() - - if p.OnUpdate != nil { - p.fnM.Lock() - p.OnUpdate(cur, time.Since(p.start), true) - p.fnM.Unlock() - } + p.updateProgress(cur, true) case <-p.cancel: p.c.Stop() return @@ -111,40 +126,23 @@ func (p *Progress) reporter() { } } -// Reset resets all statistic counters to zero. -func (p *Progress) Reset() { - if p == nil { - return - } - - if !p.running { - panic("resetting a non-running Progress") - } - - p.curM.Lock() - p.cur = Stat{} - p.curM.Unlock() -} - // Done closes the progress report. func (p *Progress) Done() { if p == nil || !p.running { return } - if p.running { - p.running = false - p.o.Do(func() { - close(p.cancel) - }) + p.running = false + p.o.Do(func() { + close(p.cancel) + }) - cur := p.cur + cur := p.cur - if p.OnDone != nil { - p.fnM.Lock() - p.OnDone(cur, time.Since(p.start), false) - p.fnM.Unlock() - } + if p.OnDone != nil { + p.fnM.Lock() + p.OnDone(cur, time.Since(p.start), false) + p.fnM.Unlock() } } diff --git a/restorer.go b/restorer.go index 54e797abc..d21300cab 100644 --- a/restorer.go +++ b/restorer.go @@ -19,57 +19,31 @@ type Restorer struct { Filter func(item string, dstpath string, node *Node) bool } -// NewRestorer creates a restorer preloaded with the content from the snapshot snid. -func NewRestorer(s *server.Server, snid backend.ID) (*Restorer, error) { - r := &Restorer{s: s} +var abortOnAllErrors = func(str string, node *Node, err error) error { return err } + +// NewRestorer creates a restorer preloaded with the content from the snapshot id. +func NewRestorer(s *server.Server, id backend.ID) (*Restorer, error) { + r := &Restorer{s: s, Error: abortOnAllErrors} var err error - r.sn, err = LoadSnapshot(s, snid) + r.sn, err = LoadSnapshot(s, id) if err != nil { return nil, arrar.Annotate(err, "load snapshot for restorer") } - // abort on all errors - r.Error = func(string, *Node, error) error { return err } - return r, nil } -func (res *Restorer) to(dst string, dir string, treeID backend.ID) error { +func (res *Restorer) restoreTo(dst string, dir string, treeID backend.ID) error { tree, err := LoadTree(res.s, treeID) if err != nil { return res.Error(dir, nil, arrar.Annotate(err, "LoadTree")) } for _, node := range tree.Nodes { - dstpath := filepath.Join(dst, dir, node.Name) - - if res.Filter == nil || - res.Filter(filepath.Join(dir, node.Name), dstpath, node) { - err := node.CreateAt(dstpath, res.s) - - // Did it fail because of ENOENT? - if arrar.Check(err, func(err error) bool { - if pe, ok := err.(*os.PathError); ok { - errn, ok := pe.Err.(syscall.Errno) - return ok && errn == syscall.ENOENT - } - return false - }) { - // Create parent directories and retry - err = os.MkdirAll(filepath.Dir(dstpath), 0700) - if err == nil || err == os.ErrExist { - err = node.CreateAt(dstpath, res.s) - } - } - - if err != nil { - err = res.Error(dstpath, node, arrar.Annotate(err, "create node")) - if err != nil { - return err - } - } + if err := res.restoreNodeTo(node, dir, dst); err != nil { + return err } if node.Type == "dir" { @@ -78,7 +52,7 @@ func (res *Restorer) to(dst string, dir string, treeID backend.ID) error { } subp := filepath.Join(dir, node.Name) - err = res.to(dst, subp, node.Subtree) + err = res.restoreTo(dst, subp, node.Subtree) if err != nil { err = res.Error(subp, node, arrar.Annotate(err, "restore subtree")) if err != nil { @@ -91,10 +65,44 @@ func (res *Restorer) to(dst string, dir string, treeID backend.ID) error { return nil } +func (res *Restorer) restoreNodeTo(node *Node, dir string, dst string) error { + dstPath := filepath.Join(dst, dir, node.Name) + + if res.Filter != nil && res.Filter(filepath.Join(dir, node.Name), dstPath, node) { + return nil + } + + err := node.CreateAt(dstPath, res.s) + + // Did it fail because of ENOENT? + if arrar.Check(err, func(err error) bool { + if pe, ok := err.(*os.PathError); ok { + errn, ok := pe.Err.(syscall.Errno) + return ok && errn == syscall.ENOENT + } + return false + }) { + // Create parent directories and retry + err = os.MkdirAll(filepath.Dir(dstPath), 0700) + if err == nil || err == os.ErrExist { + err = node.CreateAt(dstPath, res.s) + } + } + + if err != nil { + err = res.Error(dstPath, node, arrar.Annotate(err, "create node")) + if err != nil { + return err + } + } + + return nil +} + // RestoreTo creates the directories and files in the snapshot below dir. // Before an item is created, res.Filter is called. func (res *Restorer) RestoreTo(dir string) error { - return res.to(dir, "", res.sn.Tree) + return res.restoreTo(dir, "", res.sn.Tree) } func (res *Restorer) Snapshot() *Snapshot { diff --git a/snapshot_test.go b/snapshot_test.go index 3e13dd1f1..007f2e082 100644 --- a/snapshot_test.go +++ b/snapshot_test.go @@ -2,29 +2,17 @@ package restic_test import ( "testing" - "time" "github.com/restic/restic" - "github.com/restic/restic/server" . "github.com/restic/restic/test" ) -func testSnapshot(t *testing.T, s *server.Server) { - var err error - sn, err := restic.NewSnapshot([]string{"/home/foobar"}) - OK(t, err) - // sn.Tree, err = restic.Blob{ID: backend.ParseID("c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2")} - // ok(t, err) - sn.Time, err = time.Parse(time.RFC3339Nano, "2014-08-03T17:49:05.378595539+02:00") - OK(t, err) - - // _, err = sn.Save(be) - // ok(t, err) -} - -func TestSnapshot(t *testing.T) { +func TestNewSnapshot(t *testing.T) { s := SetupBackend(t) defer TeardownBackend(t, s) - testSnapshot(t, s) + paths := []string{"/home/foobar"} + + _, err := restic.NewSnapshot(paths) + OK(t, err) } diff --git a/tree.go b/tree.go index d621ced6f..f059d67dd 100644 --- a/tree.go +++ b/tree.go @@ -60,9 +60,8 @@ func (t Tree) Equals(other *Tree) bool { } func (t *Tree) Insert(node *Node) error { - pos, _, err := t.find(node.Name) + pos, _, err := t.binarySearch(node.Name) if err == nil { - // already present return ErrNodeAlreadyInTree } @@ -74,7 +73,7 @@ func (t *Tree) Insert(node *Node) error { return nil } -func (t Tree) find(name string) (int, *Node, error) { +func (t Tree) binarySearch(name string) (int, *Node, error) { pos := sort.Search(len(t.Nodes), func(i int) bool { return t.Nodes[i].Name >= name }) @@ -87,6 +86,6 @@ func (t Tree) find(name string) (int, *Node, error) { } func (t Tree) Find(name string) (*Node, error) { - _, node, err := t.find(name) + _, node, err := t.binarySearch(name) return node, err } diff --git a/tree_test.go b/tree_test.go index db9653d19..ef50e1b93 100644 --- a/tree_test.go +++ b/tree_test.go @@ -21,8 +21,7 @@ var testFiles = []struct { {"bar/bla/blubb", []byte("This is just a test!\n")}, } -// prepareDir creates a temporary directory and returns it. -func prepareDir(t *testing.T) string { +func createTempDir(t *testing.T) string { tempdir, err := ioutil.TempDir(*TestTempDir, "restic-test-") OK(t, err) @@ -48,7 +47,7 @@ func prepareDir(t *testing.T) string { } func TestTree(t *testing.T) { - dir := prepareDir(t) + dir := createTempDir(t) defer func() { if *TestCleanup { OK(t, os.RemoveAll(dir)) @@ -89,7 +88,7 @@ func TestNodeComparison(t *testing.T) { n2 := *node Assert(t, node.Equals(n2), "nodes aren't equal") - n2.Size -= 1 + n2.Size-- Assert(t, !node.Equals(n2), "nodes are equal") } diff --git a/walk.go b/walk.go index 569e5820c..2ed5df070 100644 --- a/walk.go +++ b/walk.go @@ -18,7 +18,7 @@ type WalkTreeJob struct { func walkTree(s *server.Server, path string, treeID backend.ID, done chan struct{}, jobCh chan<- WalkTreeJob) { debug.Log("walkTree", "start on %q (%v)", path, treeID.Str()) - // load tree + t, err := LoadTree(s, treeID) if err != nil { jobCh <- WalkTreeJob{Path: path, Error: err} @@ -30,15 +30,15 @@ func walkTree(s *server.Server, path string, treeID backend.ID, done chan struct if node.Type == "dir" { walkTree(s, p, node.Subtree, done, jobCh) } else { - jobCh <- WalkTreeJob{Path: p, Node: node, Error: err} + jobCh <- WalkTreeJob{Path: p, Node: node} } } - jobCh <- WalkTreeJob{Path: filepath.Join(path), Tree: t} + jobCh <- WalkTreeJob{Path: path, Tree: t} debug.Log("walkTree", "done for %q (%v)", path, treeID.Str()) } -// WalkTree walks the tree specified by ID recursively and sends a job for each +// WalkTree 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 WalkTree(server *server.Server, id backend.ID, done chan struct{}, jobCh chan<- WalkTreeJob) { diff --git a/walk_test.go b/walk_test.go index 170742d48..3697c5bb4 100644 --- a/walk_test.go +++ b/walk_test.go @@ -30,11 +30,6 @@ func TestWalkTree(t *testing.T) { // flush server, write all packs OK(t, server.Flush()) - // start benchmark - // t.ResetTimer() - - // for i := 0; i < t.N; i++ { - done := make(chan struct{}) // start tree walker @@ -89,5 +84,4 @@ func TestWalkTree(t *testing.T) { Assert(t, fsEntries == treeEntries, "wrong number of entries: %v != %v", fsEntries, treeEntries) } - // } }