diff --git a/cmd/restic/cmd_restore.go b/cmd/restic/cmd_restore.go index 393d0347d..e4d939fd2 100644 --- a/cmd/restic/cmd_restore.go +++ b/cmd/restic/cmd_restore.go @@ -5,6 +5,7 @@ import ( "path/filepath" "github.com/restic/restic" + "github.com/restic/restic/debug" ) type CmdRestore struct { @@ -78,11 +79,17 @@ func (cmd CmdRestore) Execute(args []string) error { // TODO: a filter against the full path sucks as filepath.Match doesn't match // directory separators on '*'. still, it's better than nothing. if len(args) > 2 { - res.Filter = func(item string, dstpath string, node *restic.Node) bool { - matched, err := filepath.Match(item, args[2]) + pattern := args[2] + cmd.global.Verbosef("filter pattern %q\n", pattern) + + res.SelectForRestore = func(item string, dstpath string, node *restic.Node) bool { + matched, err := filepath.Match(pattern, node.Name) if err != nil { panic(err) } + if !matched { + debug.Log("restic.restore", "item %v doesn't match pattern %q", item, pattern) + } return matched } } diff --git a/node.go b/node.go index f677199c8..2a118f815 100644 --- a/node.go +++ b/node.go @@ -104,6 +104,8 @@ func nodeTypeFromFileInfo(fi os.FileInfo) string { // CreateAt creates the node at the given path and restores all the meta data. func (node *Node) CreateAt(path string, repo *repository.Repository) error { + debug.Log("Node.CreateAt", "create node %v at %v", node.Name, path) + switch node.Type { case "dir": if err := node.createDirAt(path); err != nil { @@ -135,7 +137,12 @@ func (node *Node) CreateAt(path string, repo *repository.Repository) error { return fmt.Errorf("filetype %q not implemented!\n", node.Type) } - return node.restoreMetadata(path) + err := node.restoreMetadata(path) + if err != nil { + debug.Log("Node.CreateAt", "restoreMetadata(%s) error %v", path, err) + } + + return err } func (node Node) restoreMetadata(path string) error { @@ -156,6 +163,7 @@ func (node Node) restoreMetadata(path string) error { if node.Type != "dir" { err = node.RestoreTimestamps(path) if err != nil { + debug.Log("Node.restoreMetadata", "error restoring timestamps for dir %v: %v", path, err) return err } } diff --git a/restorer.go b/restorer.go index d97bd0a5f..05c6b10d4 100644 --- a/restorer.go +++ b/restorer.go @@ -7,6 +7,7 @@ import ( "syscall" "github.com/restic/restic/backend" + "github.com/restic/restic/debug" "github.com/restic/restic/repository" "github.com/juju/errors" @@ -17,8 +18,8 @@ type Restorer struct { repo *repository.Repository sn *Snapshot - Error func(dir string, node *Node, err error) error - Filter func(item string, dstpath string, node *Node) bool + Error func(dir string, node *Node, err error) error + SelectForRestore func(item string, dstpath string, node *Node) bool } var restorerAbortOnAllErrors = func(str string, node *Node, err error) error { return err } @@ -44,7 +45,8 @@ func (res *Restorer) restoreTo(dst string, dir string, treeID backend.ID) error } for _, node := range tree.Nodes { - if err := res.restoreNodeTo(node, dir, dst); err != nil { + excluded, err := res.restoreNodeTo(node, dir, dst) + if err != nil { return err } @@ -62,10 +64,12 @@ func (res *Restorer) restoreTo(dst string, dir string, treeID backend.ID) error } } - // Restore directory timestamp at the end. If we would do it earlier, restoring files within - // the directory would overwrite the timestamp of the directory they are in. - if err := node.RestoreTimestamps(filepath.Join(dst, dir, node.Name)); err != nil { - return err + if !excluded { + // Restore directory timestamp at the end. If we would do it earlier, restoring files within + // the directory would overwrite the timestamp of the directory they are in. + if err := node.RestoreTimestamps(filepath.Join(dst, dir, node.Name)); err != nil { + return err + } } } } @@ -73,19 +77,30 @@ func (res *Restorer) restoreTo(dst string, dir string, treeID backend.ID) error return nil } -func (res *Restorer) restoreNodeTo(node *Node, dir string, dst string) error { +func (res *Restorer) restoreNodeTo(node *Node, dir string, dst string) (excluded bool, err error) { + debug.Log("Restorer.restoreNodeTo", "node %v, dir %v, dst %v", node.Name, dir, dst) dstPath := filepath.Join(dst, dir, node.Name) - if res.Filter != nil && res.Filter(filepath.Join(dir, node.Name), dstPath, node) { - return nil + if res.SelectForRestore != nil { + debug.Log("Restorer.restoreNodeTo", "running select filter for %v", node.Name) + + if !res.SelectForRestore(filepath.Join(dir, node.Name), dstPath, node) { + debug.Log("Restorer.restoreNodeTo", "SelectForRestore returned false") + return true, nil + } } - err := node.CreateAt(dstPath, res.repo) + err = node.CreateAt(dstPath, res.repo) + if err != nil { + debug.Log("Restorer.restoreNodeTo", "node.CreateAt(%s) error %v", dstPath, err) + } // Did it fail because of ENOENT? if pe, ok := errors.Cause(err).(*os.PathError); ok { errn, ok := pe.Err.(syscall.Errno) if ok && errn == syscall.ENOENT { + debug.Log("Restorer.restoreNodeTo", "create intermediate paths") + // Create parent directories and retry err = os.MkdirAll(filepath.Dir(dstPath), 0700) if err == nil || err == os.ErrExist { @@ -95,13 +110,16 @@ func (res *Restorer) restoreNodeTo(node *Node, dir string, dst string) error { } if err != nil { + debug.Log("Restorer.restoreNodeTo", "error %v", err) err = res.Error(dstPath, node, errors.Annotate(err, "create node")) if err != nil { - return err + return false, err } } - return nil + debug.Log("Restorer.restoreNodeTo", "successfully restored %v", node.Name) + + return false, nil } // RestoreTo creates the directories and files in the snapshot below dir.