2015-01-04 17:23:00 +00:00
|
|
|
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
|
2015-01-10 22:40:10 +00:00
|
|
|
tree := NewTree()
|
2015-01-04 17:23:00 +00:00
|
|
|
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
|
2015-01-04 21:39:30 +00:00
|
|
|
node.tree, node.err = scan(filterFn, progress, path)
|
2015-01-04 17:23:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-10 22:40:10 +00:00
|
|
|
for _, node := range tree.Nodes {
|
2015-01-04 17:23:00 +00:00
|
|
|
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})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-10 22:40:10 +00:00
|
|
|
return tree, nil
|
2015-01-04 17:23:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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()")
|
|
|
|
}
|
|
|
|
|
2015-01-10 22:40:10 +00:00
|
|
|
tree := NewTree()
|
|
|
|
tree.Insert(node)
|
2015-01-04 17:23:00 +00:00
|
|
|
if node.Type != "dir" {
|
|
|
|
sc.p.Report(Stat{Files: 1, Bytes: node.Size})
|
|
|
|
|
2015-01-10 22:40:10 +00:00
|
|
|
return tree, nil
|
2015-01-04 17:23:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sc.p.Report(Stat{Dirs: 1})
|
|
|
|
|
2015-01-04 21:39:30 +00:00
|
|
|
node.tree, err = scan(sc.Filter, sc.p, path)
|
2015-01-04 17:23:00 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, arrar.Annotate(err, "loadTree()")
|
|
|
|
}
|
|
|
|
|
2015-01-10 22:40:10 +00:00
|
|
|
return tree, nil
|
2015-01-04 17:23:00 +00:00
|
|
|
}
|