2
2
mirror of https://github.com/octoleo/restic.git synced 2024-06-13 14:22:23 +00:00

find: Use walker.Walk

This commit is contained in:
Alexander Neumann 2018-06-09 18:56:40 +02:00
parent 3a86f4852b
commit 081743d0a5
3 changed files with 62 additions and 71 deletions

View File

@ -3,7 +3,6 @@ package main
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"path"
"strings" "strings"
"time" "time"
@ -11,7 +10,9 @@ import (
"github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/filter"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
"github.com/restic/restic/internal/walker"
) )
var cmdFind = &cobra.Command{ var cmdFind = &cobra.Command{
@ -94,7 +95,7 @@ type statefulOutput struct {
hits int hits int
} }
func (s *statefulOutput) PrintJSON(prefix string, node *restic.Node) { func (s *statefulOutput) PrintJSON(path string, node *restic.Node) {
type findNode restic.Node type findNode restic.Node
b, err := json.Marshal(struct { b, err := json.Marshal(struct {
// Add these attributes // Add these attributes
@ -111,7 +112,7 @@ func (s *statefulOutput) PrintJSON(prefix string, node *restic.Node) {
Content byte `json:"content,omitempty"` Content byte `json:"content,omitempty"`
Subtree byte `json:"subtree,omitempty"` Subtree byte `json:"subtree,omitempty"`
}{ }{
Path: path.Join(prefix, node.Name), Path: path,
Permissions: node.Mode.String(), Permissions: node.Mode.String(),
findNode: (*findNode)(node), findNode: (*findNode)(node),
}) })
@ -138,7 +139,7 @@ func (s *statefulOutput) PrintJSON(prefix string, node *restic.Node) {
s.hits++ s.hits++
} }
func (s *statefulOutput) PrintNormal(prefix string, node *restic.Node) { func (s *statefulOutput) PrintNormal(path string, node *restic.Node) {
if s.newsn != s.oldsn { if s.newsn != s.oldsn {
if s.oldsn != nil { if s.oldsn != nil {
Verbosef("\n") Verbosef("\n")
@ -146,14 +147,14 @@ func (s *statefulOutput) PrintNormal(prefix string, node *restic.Node) {
s.oldsn = s.newsn s.oldsn = s.newsn
Verbosef("Found matching entries in snapshot %s\n", s.oldsn.ID().Str()) Verbosef("Found matching entries in snapshot %s\n", s.oldsn.ID().Str())
} }
Printf(formatNode(prefix, node, s.ListLong) + "\n") Printf(formatNode(path, node, s.ListLong) + "\n")
} }
func (s *statefulOutput) Print(prefix string, node *restic.Node) { func (s *statefulOutput) Print(path string, node *restic.Node) {
if s.JSON { if s.JSON {
s.PrintJSON(prefix, node) s.PrintJSON(path, node)
} else { } else {
s.PrintNormal(prefix, node) s.PrintNormal(path, node)
} }
} }
@ -177,80 +178,72 @@ type Finder struct {
repo restic.Repository repo restic.Repository
pat findPattern pat findPattern
out statefulOutput out statefulOutput
notfound restic.IDSet ignoreTrees restic.IDSet
} }
// findInTree traverses a tree and outputs matches. foundInSubtree is true if func (f *Finder) findInSnapshot(ctx context.Context, sn *restic.Snapshot) error {
// some match has been found within some subtree. If err is non-nil, the value debug.Log("searching in snapshot %s\n for entries within [%s %s]", sn.ID(), f.pat.oldest, f.pat.newest)
// of foundInSubtree is invalid.
func (f *Finder) findInTree(ctx context.Context, treeID restic.ID, prefix string) (foundInSubtree bool, err error) { if sn.Tree == nil {
if f.notfound.Has(treeID) { return errors.Errorf("snapshot %v has no tree", sn.ID().Str())
debug.Log("%v skipping tree %v, has already been checked", prefix, treeID)
return false, nil
} }
debug.Log("%v checking tree %v\n", prefix, treeID) f.out.newsn = sn
return walker.Walk(ctx, f.repo, *sn.Tree, f.ignoreTrees, func(nodepath string, node *restic.Node, err error) (bool, error) {
tree, err := f.repo.LoadTree(ctx, treeID)
if err != nil { if err != nil {
return false, err return false, err
} }
var found bool if node == nil {
for _, node := range tree.Nodes { return false, nil
debug.Log(" testing entry %q\n", node.Name) }
name := node.Name name := node.Name
if f.pat.ignoreCase { if f.pat.ignoreCase {
name = strings.ToLower(name) name = strings.ToLower(name)
} }
m, err := path.Match(f.pat.pattern, name) foundMatch, err := filter.Match(f.pat.pattern, nodepath)
if err != nil { if err != nil {
return false, err return false, err
} }
if m { var (
ignoreIfNoMatch = true
errIfNoMatch error
)
if node.Type == "dir" {
childMayMatch, err := filter.ChildMatch(f.pat.pattern, nodepath)
if err != nil {
return false, err
}
if !childMayMatch {
ignoreIfNoMatch = true
errIfNoMatch = walker.SkipNode
} else {
ignoreIfNoMatch = false
}
}
if !foundMatch {
return ignoreIfNoMatch, errIfNoMatch
}
if !f.pat.oldest.IsZero() && node.ModTime.Before(f.pat.oldest) { if !f.pat.oldest.IsZero() && node.ModTime.Before(f.pat.oldest) {
debug.Log(" ModTime is older than %s\n", f.pat.oldest) debug.Log(" ModTime is older than %s\n", f.pat.oldest)
continue return ignoreIfNoMatch, errIfNoMatch
} }
if !f.pat.newest.IsZero() && node.ModTime.After(f.pat.newest) { if !f.pat.newest.IsZero() && node.ModTime.After(f.pat.newest) {
debug.Log(" ModTime is newer than %s\n", f.pat.newest) debug.Log(" ModTime is newer than %s\n", f.pat.newest)
continue return ignoreIfNoMatch, errIfNoMatch
} }
debug.Log(" found match\n") debug.Log(" found match\n")
found = true f.out.Print(nodepath, node)
f.out.Print(prefix, node) return false, nil
} })
if node.Type == "dir" {
foundSubtree, err := f.findInTree(ctx, *node.Subtree, path.Join(prefix, node.Name))
if err != nil {
return false, err
}
if foundSubtree {
found = true
}
}
}
if !found {
f.notfound.Insert(treeID)
}
return found, nil
}
func (f *Finder) findInSnapshot(ctx context.Context, sn *restic.Snapshot) error {
debug.Log("searching in snapshot %s\n for entries within [%s %s]", sn.ID(), f.pat.oldest, f.pat.newest)
f.out.newsn = sn
_, err := f.findInTree(ctx, *sn.Tree, "/")
return err
} }
func runFind(opts FindOptions, gopts GlobalOptions, args []string) error { func runFind(opts FindOptions, gopts GlobalOptions, args []string) error {
@ -301,7 +294,7 @@ func runFind(opts FindOptions, gopts GlobalOptions, args []string) error {
repo: repo, repo: repo,
pat: pat, pat: pat,
out: statefulOutput{ListLong: opts.ListLong, JSON: globalOptions.JSON}, out: statefulOutput{ListLong: opts.ListLong, JSON: globalOptions.JSON},
notfound: restic.NewIDSet(), ignoreTrees: restic.NewIDSet(),
} }
for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, opts.Snapshots) { for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, opts.Snapshots) {
if err = f.findInSnapshot(ctx, sn); err != nil { if err = f.findInSnapshot(ctx, sn); err != nil {

View File

@ -2,7 +2,7 @@ package main
import ( import (
"context" "context"
"path/filepath" "path"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -53,10 +53,10 @@ func printTree(ctx context.Context, repo *repository.Repository, id *restic.ID,
} }
for _, entry := range tree.Nodes { for _, entry := range tree.Nodes {
Printf("%s\n", formatNode(prefix, entry, lsOptions.ListLong)) entryPath := path.Join(prefix, entry.Name)
Printf("%s\n", formatNode(entryPath, entry, lsOptions.ListLong))
if entry.Type == "dir" && entry.Subtree != nil { if entry.Type == "dir" && entry.Subtree != nil {
entryPath := prefix + string(filepath.Separator) + entry.Name
if err = printTree(ctx, repo, entry.Subtree, entryPath); err != nil { if err = printTree(ctx, repo, entry.Subtree, entryPath); err != nil {
return err return err
} }

View File

@ -3,7 +3,6 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"path"
"time" "time"
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
@ -63,10 +62,9 @@ func formatDuration(d time.Duration) string {
return formatSeconds(sec) return formatSeconds(sec)
} }
func formatNode(prefix string, n *restic.Node, long bool) string { func formatNode(path string, n *restic.Node, long bool) string {
nodepath := path.Join(prefix, n.Name)
if !long { if !long {
return nodepath return path
} }
var mode os.FileMode var mode os.FileMode
@ -92,6 +90,6 @@ func formatNode(prefix string, n *restic.Node, long bool) string {
return fmt.Sprintf("%s %5d %5d %6d %s %s%s", return fmt.Sprintf("%s %5d %5d %6d %s %s%s",
mode|n.Mode, n.UID, n.GID, n.Size, mode|n.Mode, n.UID, n.GID, n.Size,
n.ModTime.Format(TimeFormat), nodepath, n.ModTime.Format(TimeFormat), path,
target) target)
} }