diff --git a/cmd/restic/cmd_backup.go b/cmd/restic/cmd_backup.go index dbb631e05..e238a8bf2 100644 --- a/cmd/restic/cmd_backup.go +++ b/cmd/restic/cmd_backup.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "os" "path/filepath" @@ -73,6 +74,10 @@ func (cmd CmdBackup) Usage() string { return "DIR/FILE [snapshot-ID]" } +func isFile(fi os.FileInfo) bool { + return fi.Mode()&(os.ModeType|os.ModeCharDevice) == 0 +} + func (cmd CmdBackup) Execute(args []string) error { if len(args) == 0 || len(args) > 2 { return fmt.Errorf("wrong number of parameters, Usage: %s", cmd.Usage()) @@ -112,54 +117,59 @@ func (cmd CmdBackup) Execute(args []string) error { // return true // } - sc := restic.NewScanner(scanProgress) + stat := restic.Stat{} + + term := terminal.IsTerminal(int(os.Stdout.Fd())) + + start := time.Now() + err = filepath.Walk(target, func(p string, fi os.FileInfo, err error) error { + if isFile(fi) { + stat.Files++ + stat.Bytes += uint64(fi.Size()) + } else if fi.IsDir() { + stat.Dirs++ + } + + if term { + fmt.Printf("\x1b[2K\r[%s] %d directories, %d files, %s", + format_duration(time.Since(start)), stat.Dirs, stat.Files, format_bytes(stat.Bytes)) + } + + // TODO: handle error? + return nil + }) - newTree, err := sc.Scan(target) if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) return err } + fmt.Printf("\nDone in %s\n", format_duration(time.Since(start))) + if parentSnapshotID != nil { - fmt.Printf("load old snapshot\n") - sn, err := restic.LoadSnapshot(s, parentSnapshotID) - if err != nil { - return err - } - - oldTree, err := restic.LoadTreeRecursive(filepath.Dir(sn.Dir), s, sn.Tree) - if err != nil { - return err - } - - err = newTree.CopyFrom(oldTree, &s) - if err != nil { - return err - } + return errors.New("not implemented") } archiveProgress := restic.NewProgress(time.Second) - targetStat := newTree.StatTodo() if terminal.IsTerminal(int(os.Stdout.Fd())) { var bps, eta uint64 - itemsTodo := targetStat.Files + targetStat.Dirs + itemsTodo := stat.Files + stat.Dirs archiveProgress.OnUpdate = func(s restic.Stat, d time.Duration, ticker bool) { sec := uint64(d / time.Second) - if targetStat.Bytes > 0 && sec > 0 && ticker { + if stat.Bytes > 0 && sec > 0 && ticker { bps = s.Bytes / sec if bps > 0 { - eta = (targetStat.Bytes - s.Bytes) / bps + eta = (stat.Bytes - s.Bytes) / bps } } itemsDone := s.Files + s.Dirs fmt.Printf("\x1b[2K\r[%s] %3.2f%% %s/s %s / %s %d / %d items ETA %s", format_duration(d), - float64(s.Bytes)/float64(targetStat.Bytes)*100, + float64(s.Bytes)/float64(stat.Bytes)*100, format_bytes(bps), - format_bytes(s.Bytes), format_bytes(targetStat.Bytes), + format_bytes(s.Bytes), format_bytes(stat.Bytes), itemsDone, itemsTodo, format_seconds(eta)) } @@ -168,7 +178,7 @@ func (cmd CmdBackup) Execute(args []string) error { sec := uint64(d / time.Second) fmt.Printf("\nduration: %s, %.2fMiB/s\n", format_duration(d), - float64(targetStat.Bytes)/float64(sec)/(1<<20)) + float64(stat.Bytes)/float64(sec)/(1<<20)) } } diff --git a/scanner.go b/scanner.go deleted file mode 100644 index fc2daba46..000000000 --- a/scanner.go +++ /dev/null @@ -1,119 +0,0 @@ -package restic - -import ( - "os" - "path/filepath" - - "github.com/juju/arrar" -) - -type FilterFunc func(item string, fi os.FileInfo) bool -type ErrorFunc func(dir string, fi os.FileInfo, err error) error - -type Scanner struct { - Error ErrorFunc - Filter FilterFunc - - p *Progress -} - -func NewScanner(p *Progress) *Scanner { - sc := &Scanner{p: p} - - // abort on all errors - sc.Error = func(s string, fi os.FileInfo, err error) error { return err } - // allow all files - sc.Filter = func(string, os.FileInfo) bool { return true } - - return sc -} - -func scan(filterFn FilterFunc, progress *Progress, dir string) (*Tree, error) { - var err error - - // open and list path - fd, err := os.Open(dir) - defer fd.Close() - - if err != nil { - return nil, err - } - - entries, err := fd.Readdir(-1) - if err != nil { - return nil, err - } - - // build new tree - tree := NewTree() - for _, entry := range entries { - path := filepath.Join(dir, entry.Name()) - - if !filterFn(path, entry) { - continue - } - - node, err := NodeFromFileInfo(path, entry) - if err != nil { - // TODO: error processing - return nil, err - } - - err = tree.Insert(node) - if err != nil { - return nil, err - } - - if entry.IsDir() { - // save all errors in node.err, sort out later - node.tree, node.err = scan(filterFn, progress, path) - } - } - - for _, node := range tree.Nodes { - if node.Type == "file" && node.Content != nil { - continue - } - - switch node.Type { - case "file": - progress.Report(Stat{Files: 1, Bytes: node.Size}) - case "dir": - progress.Report(Stat{Dirs: 1}) - } - } - - return tree, nil -} - -func (sc *Scanner) Scan(path string) (*Tree, error) { - sc.p.Start() - defer sc.p.Done() - - fi, err := os.Lstat(path) - if err != nil { - return nil, arrar.Annotatef(err, "Lstat(%q)", path) - } - - node, err := NodeFromFileInfo(path, fi) - if err != nil { - return nil, arrar.Annotate(err, "NodeFromFileInfo()") - } - - tree := NewTree() - tree.Insert(node) - if node.Type != "dir" { - sc.p.Report(Stat{Files: 1, Bytes: node.Size}) - - return tree, nil - } - - sc.p.Report(Stat{Dirs: 1}) - - node.tree, err = scan(sc.Filter, sc.p, path) - if err != nil { - return nil, arrar.Annotate(err, "loadTree()") - } - - return tree, nil -} diff --git a/scanner_test.go b/scanner_test.go deleted file mode 100644 index 4629cac29..000000000 --- a/scanner_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package restic_test - -import ( - "flag" - "testing" - - "github.com/restic/restic" -) - -var scanDir = flag.String("test.scandir", ".", "test/benchmark scanning a real directory (default: .)") - -func TestScanner(t *testing.T) { - sc := restic.NewScanner(nil) - - tree, err := sc.Scan(*scanDir) - ok(t, err) - - stats := tree.Stat() - - assert(t, stats.Files > 0, - "no files in dir %v", *scanDir) -} - -func BenchmarkScanner(b *testing.B) { - for i := 0; i < b.N; i++ { - _, err := restic.NewScanner(nil).Scan(*scanDir) - ok(b, err) - } -}