mirror of
https://github.com/restic/restic.git
synced 2026-06-01 22:59:44 +00:00
backup: prevent exclude of backup targets
Track backup targets explicitly specified by the user and prevent excluding them. This for example ensures that `restic backup --exclude-if-present .git /home/user/data` backs up the `data` folder even if there is a `.git` folder in `/home/user`. Note that this does not suffice for commands like `restic backup --exclude data /home/user/data` as the exclude pattern will still match every single file within `data`.
This commit is contained in:
@@ -334,7 +334,7 @@ func (arch *Archiver) saveDir(ctx context.Context, snPath string, dir string, me
|
||||
return futureNode{}, err
|
||||
}
|
||||
snItem := join(snPath, name)
|
||||
fn, excluded, err := arch.save(ctx, snItem, pathname, oldNode)
|
||||
fn, excluded, err := arch.save(ctx, snItem, pathname, oldNode, false)
|
||||
|
||||
// return error early if possible
|
||||
if err != nil {
|
||||
@@ -449,7 +449,10 @@ func (arch *Archiver) allBlobsPresent(previous *data.Node) bool {
|
||||
// Errors and completion needs to be handled by the caller.
|
||||
//
|
||||
// snPath is the path within the current snapshot.
|
||||
func (arch *Archiver) save(ctx context.Context, snPath, target string, previous *data.Node) (fn futureNode, excluded bool, err error) {
|
||||
//
|
||||
// explicit is true when this path was a backup target (tree leaf with Explicit
|
||||
// set); Excludes (`SelectByName` and `Select`) are skipped for that path only.
|
||||
func (arch *Archiver) save(ctx context.Context, snPath, target string, previous *data.Node, explicit bool) (fn futureNode, excluded bool, err error) {
|
||||
start := time.Now()
|
||||
|
||||
debug.Log("%v target %q, previous %v", snPath, target, previous)
|
||||
@@ -472,7 +475,7 @@ func (arch *Archiver) save(ctx context.Context, snPath, target string, previous
|
||||
return err
|
||||
}
|
||||
// exclude files by path before running Lstat to reduce number of lstat calls
|
||||
if !arch.SelectByName(abstarget) {
|
||||
if !explicit && !arch.SelectByName(abstarget) {
|
||||
debug.Log("%v is excluded by path", target)
|
||||
return futureNode{}, true, nil
|
||||
}
|
||||
@@ -500,7 +503,7 @@ func (arch *Archiver) save(ctx context.Context, snPath, target string, previous
|
||||
// ignore if file disappeared since it was returned by readdir
|
||||
return filterError(filterNotExist(err))
|
||||
}
|
||||
if !arch.Select(abstarget, fi, arch.FS) {
|
||||
if !explicit && !arch.Select(abstarget, fi, arch.FS) {
|
||||
debug.Log("%v is excluded", target)
|
||||
return futureNode{}, true, nil
|
||||
}
|
||||
@@ -694,7 +697,7 @@ func (arch *Archiver) saveTree(ctx context.Context, snPath string, atree *tree,
|
||||
if err != nil {
|
||||
return futureNode{}, 0, err
|
||||
}
|
||||
fn, excluded, err := arch.save(ctx, pathname, subatree.Path, oldNode)
|
||||
fn, excluded, err := arch.save(ctx, pathname, subatree.Path, oldNode, subatree.Explicit)
|
||||
|
||||
if err != nil {
|
||||
err = arch.error(subatree.Path, err)
|
||||
@@ -775,9 +778,12 @@ func (arch *Archiver) dirPathToNode(snPath, target string) (node *data.Node, err
|
||||
// resolveRelativeTargets replaces targets that only contain relative
|
||||
// directories ("." or "../../") with the contents of the directory. Each
|
||||
// element of target is processed with fs.Clean().
|
||||
func resolveRelativeTargets(filesys fs.FS, targets []string) ([]string, error) {
|
||||
//
|
||||
// Paths returned with Explicit true are those the user listed literally; paths
|
||||
// inserted from directory expansion have Explicit false.
|
||||
func resolveRelativeTargets(filesys fs.FS, targets []string) ([]BackupTarget, error) {
|
||||
debug.Log("targets before resolving: %v", targets)
|
||||
result := make([]string, 0, len(targets))
|
||||
result := make([]BackupTarget, 0, len(targets))
|
||||
for _, target := range targets {
|
||||
if target != "" && filesys.VolumeName(target) == target {
|
||||
// special case to allow users to also specify a volume name "C:" instead of a path "C:\"
|
||||
@@ -787,7 +793,7 @@ func resolveRelativeTargets(filesys fs.FS, targets []string) ([]string, error) {
|
||||
}
|
||||
pc, _ := pathComponents(filesys, target, false)
|
||||
if len(pc) > 0 {
|
||||
result = append(result, target)
|
||||
result = append(result, BackupTarget{Path: target, Explicit: true})
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -799,7 +805,10 @@ func resolveRelativeTargets(filesys fs.FS, targets []string) ([]string, error) {
|
||||
sort.Strings(entries)
|
||||
|
||||
for _, name := range entries {
|
||||
result = append(result, filesys.Join(target, name))
|
||||
result = append(result, BackupTarget{
|
||||
Path: filesys.Join(target, name),
|
||||
Explicit: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -223,7 +223,7 @@ func TestArchiverSave(t *testing.T) {
|
||||
wg, ctx := errgroup.WithContext(ctx)
|
||||
arch.runWorkers(ctx, wg, uploader)
|
||||
|
||||
node, excluded, err := arch.save(ctx, "/", filepath.Join(tempdir, "file"), nil)
|
||||
node, excluded, err := arch.save(ctx, "/", filepath.Join(tempdir, "file"), nil, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -300,7 +300,7 @@ func TestArchiverSaveReaderFS(t *testing.T) {
|
||||
wg, ctx := errgroup.WithContext(ctx)
|
||||
arch.runWorkers(ctx, wg, uploader)
|
||||
|
||||
node, excluded, err := arch.save(ctx, "/", filename, nil)
|
||||
node, excluded, err := arch.save(ctx, "/", filename, nil, false)
|
||||
t.Logf("Save returned %v %v", node, err)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1106,7 +1106,11 @@ func TestArchiverSaveTree(t *testing.T) {
|
||||
wg, ctx := errgroup.WithContext(ctx)
|
||||
arch.runWorkers(ctx, wg, uploader)
|
||||
|
||||
atree, err := newTree(testFS, test.targets)
|
||||
bt, err := resolveRelativeTargets(testFS, test.targets)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
atree, err := newTree(testFS, bt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1485,7 +1489,11 @@ func TestResolveRelativeTargetsSpecial(t *testing.T) {
|
||||
|
||||
targets, err := resolveRelativeTargets(&fs.Local{}, test.targets)
|
||||
rtest.OK(t, err)
|
||||
rtest.Equals(t, test.expected, targets)
|
||||
paths := make([]string, len(targets))
|
||||
for i, tgt := range targets {
|
||||
paths[i] = tgt.Path
|
||||
}
|
||||
rtest.Equals(t, test.expected, paths)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2456,7 +2464,7 @@ func TestRacyFileTypeSwap(t *testing.T) {
|
||||
arch.runWorkers(ctx, wg, uploader)
|
||||
|
||||
// fs.Track will panic if the file was not closed
|
||||
_, excluded, err := arch.save(ctx, "/", tempfile, nil)
|
||||
_, excluded, err := arch.save(ctx, "/", tempfile, nil, false)
|
||||
rtest.Assert(t, err != nil && strings.Contains(err.Error(), "changed type, refusing to archive"), "save() returned wrong error: %v", err)
|
||||
tpe := "file"
|
||||
if dirError {
|
||||
@@ -2545,7 +2553,7 @@ func TestIrregularFile(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
arch := New(repo, fs.Track{FS: override}, Options{})
|
||||
_, excluded, err := arch.save(ctx, "/", tempfile, nil)
|
||||
_, excluded, err := arch.save(ctx, "/", tempfile, nil, false)
|
||||
if err == nil {
|
||||
t.Fatalf("Save() should have failed")
|
||||
}
|
||||
@@ -2595,7 +2603,7 @@ func TestDisappearedFile(t *testing.T) {
|
||||
// the subsequent file.Stat() call. Thus test both cases.
|
||||
for _, errorOnOpen := range []bool{false, true} {
|
||||
arch := New(repo, fs.Track{FS: &missingFS{FS: &fs.Local{}, errorOnOpen: errorOnOpen}}, Options{})
|
||||
_, excluded, err := arch.save(ctx, "/", filepath.Join(tempdir, "testdir"), nil)
|
||||
_, excluded, err := arch.save(ctx, "/", filepath.Join(tempdir, "testdir"), nil, false)
|
||||
rtest.OK(t, err)
|
||||
rtest.Assert(t, excluded, "testfile should have been excluded")
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func (s *Scanner) scanTree(ctx context.Context, stats ScanStats, tree tree) (Sca
|
||||
return ScanStats{}, err
|
||||
}
|
||||
|
||||
stats, err = s.scan(ctx, stats, abstarget)
|
||||
stats, err = s.scan(ctx, stats, abstarget, tree.Explicit)
|
||||
if err != nil {
|
||||
return ScanStats{}, err
|
||||
}
|
||||
@@ -96,13 +96,14 @@ func (s *Scanner) Scan(ctx context.Context, targets []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Scanner) scan(ctx context.Context, stats ScanStats, target string) (ScanStats, error) {
|
||||
// explicit is true when this path was an explicit backup target (same meaning as tree.Explicit on a leaf).
|
||||
func (s *Scanner) scan(ctx context.Context, stats ScanStats, target string, explicit bool) (ScanStats, error) {
|
||||
if ctx.Err() != nil {
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
// exclude files by path before running stat to reduce number of lstat calls
|
||||
if !s.SelectByName(target) {
|
||||
if !explicit && !s.SelectByName(target) {
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
@@ -113,7 +114,7 @@ func (s *Scanner) scan(ctx context.Context, stats ScanStats, target string) (Sca
|
||||
}
|
||||
|
||||
// run remaining select functions that require file information
|
||||
if !s.Select(target, fi, s.FS) {
|
||||
if !explicit && !s.Select(target, fi, s.FS) {
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
@@ -129,7 +130,7 @@ func (s *Scanner) scan(ctx context.Context, stats ScanStats, target string) (Sca
|
||||
sort.Strings(names)
|
||||
|
||||
for _, name := range names {
|
||||
stats, err = s.scan(ctx, stats, s.FS.Join(target, name))
|
||||
stats, err = s.scan(ctx, stats, s.FS.Join(target, name), false)
|
||||
if err != nil {
|
||||
return stats, err
|
||||
}
|
||||
|
||||
+24
-11
@@ -25,6 +25,7 @@ type tree struct {
|
||||
Path string // where the files/dirs to be saved are found
|
||||
FileInfoPath string // where the dir can be found that is not included itself, but its subdirs
|
||||
Root string // parent directory of the tree
|
||||
Explicit bool // Explicit is true when Path was passed as a backup target
|
||||
}
|
||||
|
||||
// pathComponents returns all path components of p. If a virtual directory
|
||||
@@ -94,8 +95,9 @@ func rootDirectory(fs fs.FS, target string) string {
|
||||
return rel
|
||||
}
|
||||
|
||||
// Add adds a new file or directory to the tree.
|
||||
func (t *tree) Add(fs fs.FS, path string) error {
|
||||
// Add adds a new file or directory to the tree. explicit marks a path the user
|
||||
// named as a backup target (see BackupTarget.Explicit).
|
||||
func (t *tree) Add(fs fs.FS, path string, explicit bool) error {
|
||||
if path == "" {
|
||||
panic("invalid path (empty string)")
|
||||
}
|
||||
@@ -138,13 +140,14 @@ func (t *tree) Add(fs fs.FS, path string) error {
|
||||
// use the original root dir if this is a virtual directory (volume name on Windows)
|
||||
subroot = root
|
||||
}
|
||||
err := tree.add(fs, path, subroot, pc[1:])
|
||||
err := tree.add(fs, path, subroot, pc[1:], explicit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tree.FileInfoPath = subroot
|
||||
} else {
|
||||
tree.Path = path
|
||||
tree.Explicit = explicit
|
||||
}
|
||||
|
||||
t.Nodes[name] = tree
|
||||
@@ -152,7 +155,7 @@ func (t *tree) Add(fs fs.FS, path string) error {
|
||||
}
|
||||
|
||||
// add adds a new target path into the tree.
|
||||
func (t *tree) add(fs fs.FS, target, root string, pc []string) error {
|
||||
func (t *tree) add(fs fs.FS, target, root string, pc []string, explicit bool) error {
|
||||
if len(pc) == 0 {
|
||||
return errors.Errorf("invalid path %q", target)
|
||||
}
|
||||
@@ -167,7 +170,7 @@ func (t *tree) add(fs fs.FS, target, root string, pc []string) error {
|
||||
node, ok := t.Nodes[name]
|
||||
|
||||
if !ok {
|
||||
t.Nodes[name] = tree{Path: target}
|
||||
t.Nodes[name] = tree{Path: target, Explicit: explicit}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -175,6 +178,7 @@ func (t *tree) add(fs fs.FS, target, root string, pc []string) error {
|
||||
return errors.Errorf("path is already set for target %v", target)
|
||||
}
|
||||
node.Path = target
|
||||
node.Explicit = explicit
|
||||
t.Nodes[name] = node
|
||||
return nil
|
||||
}
|
||||
@@ -187,7 +191,7 @@ func (t *tree) add(fs fs.FS, target, root string, pc []string) error {
|
||||
subroot := fs.Join(root, name)
|
||||
node.FileInfoPath = subroot
|
||||
|
||||
err := node.add(fs, target, subroot, pc[1:])
|
||||
err := node.add(fs, target, subroot, pc[1:], explicit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -221,7 +225,7 @@ func (t tree) NodeNames() []string {
|
||||
// formatTree returns a text representation of the tree t.
|
||||
func formatTree(t tree, indent string) (s string) {
|
||||
for name, node := range t.Nodes {
|
||||
s += fmt.Sprintf("%v/%v, root %q, path %q, meta %q\n", indent, name, node.Root, node.Path, node.FileInfoPath)
|
||||
s += fmt.Sprintf("%v/%v, root %q, path %q, meta %q, explicit %v\n", indent, name, node.Root, node.Path, node.FileInfoPath, node.Explicit)
|
||||
s += formatTree(node, indent+" ")
|
||||
}
|
||||
return s
|
||||
@@ -255,6 +259,7 @@ func unrollTree(f fs.FS, t *tree) error {
|
||||
t.Nodes[entry] = tree{Path: f.Join(t.Path, entry)}
|
||||
}
|
||||
t.Path = ""
|
||||
t.Explicit = false
|
||||
}
|
||||
|
||||
for i, subtree := range t.Nodes {
|
||||
@@ -269,13 +274,21 @@ func unrollTree(f fs.FS, t *tree) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// BackupTarget is a resolved backup path and whether the user passed this path
|
||||
// literally. Paths inserted when expanding a target with no path components
|
||||
// (for example ".") have Explicit false so include/exclude rules still apply.
|
||||
type BackupTarget struct {
|
||||
Path string
|
||||
Explicit bool
|
||||
}
|
||||
|
||||
// newTree creates a Tree from the target files/directories.
|
||||
func newTree(fs fs.FS, targets []string) (*tree, error) {
|
||||
func newTree(fs fs.FS, targets []BackupTarget) (*tree, error) {
|
||||
debug.Log("targets: %v", targets)
|
||||
tree := &tree{}
|
||||
seen := make(map[string]struct{})
|
||||
for _, target := range targets {
|
||||
target = fs.Clean(target)
|
||||
for _, tgt := range targets {
|
||||
target := fs.Clean(tgt.Path)
|
||||
|
||||
// skip duplicate targets
|
||||
if _, ok := seen[target]; ok {
|
||||
@@ -283,7 +296,7 @@ func newTree(fs fs.FS, targets []string) (*tree, error) {
|
||||
}
|
||||
seen[target] = struct{}{}
|
||||
|
||||
err := tree.Add(fs, target)
|
||||
err := tree.Add(fs, target, tgt.Explicit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -14,6 +14,14 @@ import (
|
||||
// debug.Log requires Tree.String.
|
||||
var _ fmt.Stringer = tree{}
|
||||
|
||||
func testBackupTargets(paths []string) []BackupTarget {
|
||||
tgts := make([]BackupTarget, len(paths))
|
||||
for i, p := range paths {
|
||||
tgts[i] = BackupTarget{Path: p, Explicit: true}
|
||||
}
|
||||
return tgts
|
||||
}
|
||||
|
||||
func TestPathComponents(t *testing.T) {
|
||||
var tests = []struct {
|
||||
p string
|
||||
@@ -150,24 +158,24 @@ func TestTree(t *testing.T) {
|
||||
{
|
||||
targets: []string{"foo"},
|
||||
want: tree{Nodes: map[string]tree{
|
||||
"foo": {Path: "foo", Root: "."},
|
||||
"foo": {Path: "foo", Root: ".", Explicit: true},
|
||||
}},
|
||||
},
|
||||
{
|
||||
targets: []string{"foo", "bar", "baz"},
|
||||
want: tree{Nodes: map[string]tree{
|
||||
"foo": {Path: "foo", Root: "."},
|
||||
"bar": {Path: "bar", Root: "."},
|
||||
"baz": {Path: "baz", Root: "."},
|
||||
"foo": {Path: "foo", Root: ".", Explicit: true},
|
||||
"bar": {Path: "bar", Root: ".", Explicit: true},
|
||||
"baz": {Path: "baz", Root: ".", Explicit: true},
|
||||
}},
|
||||
},
|
||||
{
|
||||
targets: []string{"foo/user1", "foo/user2", "foo/other"},
|
||||
want: tree{Nodes: map[string]tree{
|
||||
"foo": {Root: ".", FileInfoPath: "foo", Nodes: map[string]tree{
|
||||
"user1": {Path: filepath.FromSlash("foo/user1")},
|
||||
"user2": {Path: filepath.FromSlash("foo/user2")},
|
||||
"other": {Path: filepath.FromSlash("foo/other")},
|
||||
"user1": {Path: filepath.FromSlash("foo/user1"), Explicit: true},
|
||||
"user2": {Path: filepath.FromSlash("foo/user2"), Explicit: true},
|
||||
"other": {Path: filepath.FromSlash("foo/other"), Explicit: true},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
@@ -176,8 +184,8 @@ func TestTree(t *testing.T) {
|
||||
want: tree{Nodes: map[string]tree{
|
||||
"foo": {Root: ".", FileInfoPath: "foo", Nodes: map[string]tree{
|
||||
"work": {FileInfoPath: filepath.FromSlash("foo/work"), Nodes: map[string]tree{
|
||||
"user1": {Path: filepath.FromSlash("foo/work/user1")},
|
||||
"user2": {Path: filepath.FromSlash("foo/work/user2")},
|
||||
"user1": {Path: filepath.FromSlash("foo/work/user1"), Explicit: true},
|
||||
"user2": {Path: filepath.FromSlash("foo/work/user2"), Explicit: true},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
@@ -186,25 +194,25 @@ func TestTree(t *testing.T) {
|
||||
targets: []string{"foo/user1", "bar/user1", "foo/other"},
|
||||
want: tree{Nodes: map[string]tree{
|
||||
"foo": {Root: ".", FileInfoPath: "foo", Nodes: map[string]tree{
|
||||
"user1": {Path: filepath.FromSlash("foo/user1")},
|
||||
"other": {Path: filepath.FromSlash("foo/other")},
|
||||
"user1": {Path: filepath.FromSlash("foo/user1"), Explicit: true},
|
||||
"other": {Path: filepath.FromSlash("foo/other"), Explicit: true},
|
||||
}},
|
||||
"bar": {Root: ".", FileInfoPath: "bar", Nodes: map[string]tree{
|
||||
"user1": {Path: filepath.FromSlash("bar/user1")},
|
||||
"user1": {Path: filepath.FromSlash("bar/user1"), Explicit: true},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
{
|
||||
targets: []string{"../work"},
|
||||
want: tree{Nodes: map[string]tree{
|
||||
"work": {Root: "..", Path: filepath.FromSlash("../work")},
|
||||
"work": {Root: "..", Path: filepath.FromSlash("../work"), Explicit: true},
|
||||
}},
|
||||
},
|
||||
{
|
||||
targets: []string{"../work/other"},
|
||||
want: tree{Nodes: map[string]tree{
|
||||
"work": {Root: "..", FileInfoPath: filepath.FromSlash("../work"), Nodes: map[string]tree{
|
||||
"other": {Path: filepath.FromSlash("../work/other")},
|
||||
"other": {Path: filepath.FromSlash("../work/other"), Explicit: true},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
@@ -212,11 +220,11 @@ func TestTree(t *testing.T) {
|
||||
targets: []string{"foo/user1", "../work/other", "foo/user2"},
|
||||
want: tree{Nodes: map[string]tree{
|
||||
"foo": {Root: ".", FileInfoPath: "foo", Nodes: map[string]tree{
|
||||
"user1": {Path: filepath.FromSlash("foo/user1")},
|
||||
"user2": {Path: filepath.FromSlash("foo/user2")},
|
||||
"user1": {Path: filepath.FromSlash("foo/user1"), Explicit: true},
|
||||
"user2": {Path: filepath.FromSlash("foo/user2"), Explicit: true},
|
||||
}},
|
||||
"work": {Root: "..", FileInfoPath: filepath.FromSlash("../work"), Nodes: map[string]tree{
|
||||
"other": {Path: filepath.FromSlash("../work/other")},
|
||||
"other": {Path: filepath.FromSlash("../work/other"), Explicit: true},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
@@ -224,11 +232,11 @@ func TestTree(t *testing.T) {
|
||||
targets: []string{"foo/user1", "../foo/other", "foo/user2"},
|
||||
want: tree{Nodes: map[string]tree{
|
||||
"foo": {Root: ".", FileInfoPath: "foo", Nodes: map[string]tree{
|
||||
"user1": {Path: filepath.FromSlash("foo/user1")},
|
||||
"user2": {Path: filepath.FromSlash("foo/user2")},
|
||||
"user1": {Path: filepath.FromSlash("foo/user1"), Explicit: true},
|
||||
"user2": {Path: filepath.FromSlash("foo/user2"), Explicit: true},
|
||||
}},
|
||||
"foo-1": {Root: "..", FileInfoPath: filepath.FromSlash("../foo"), Nodes: map[string]tree{
|
||||
"other": {Path: filepath.FromSlash("../foo/other")},
|
||||
"other": {Path: filepath.FromSlash("../foo/other"), Explicit: true},
|
||||
}},
|
||||
}},
|
||||
},
|
||||
@@ -245,8 +253,8 @@ func TestTree(t *testing.T) {
|
||||
Root: ".",
|
||||
FileInfoPath: "foo",
|
||||
Nodes: map[string]tree{
|
||||
"file": {Path: filepath.FromSlash("foo/file")},
|
||||
"work": {Path: filepath.FromSlash("foo/work")},
|
||||
"file": {Path: filepath.FromSlash("foo/file"), Explicit: false},
|
||||
"work": {Path: filepath.FromSlash("foo/work"), Explicit: true},
|
||||
},
|
||||
},
|
||||
}},
|
||||
@@ -266,8 +274,8 @@ func TestTree(t *testing.T) {
|
||||
Root: ".",
|
||||
FileInfoPath: "foo",
|
||||
Nodes: map[string]tree{
|
||||
"file": {Path: filepath.FromSlash("foo/file")},
|
||||
"work": {Path: filepath.FromSlash("foo/work")},
|
||||
"file": {Path: filepath.FromSlash("foo/file"), Explicit: false},
|
||||
"work": {Path: filepath.FromSlash("foo/work"), Explicit: true},
|
||||
},
|
||||
},
|
||||
}},
|
||||
@@ -287,8 +295,8 @@ func TestTree(t *testing.T) {
|
||||
"work": {
|
||||
FileInfoPath: filepath.FromSlash("foo/work"),
|
||||
Nodes: map[string]tree{
|
||||
"user1": {Path: filepath.FromSlash("foo/work/user1")},
|
||||
"user2": {Path: filepath.FromSlash("foo/work/user2")},
|
||||
"user1": {Path: filepath.FromSlash("foo/work/user1"), Explicit: false},
|
||||
"user2": {Path: filepath.FromSlash("foo/work/user2"), Explicit: true},
|
||||
},
|
||||
},
|
||||
}},
|
||||
@@ -308,8 +316,8 @@ func TestTree(t *testing.T) {
|
||||
"foo": {Root: ".", FileInfoPath: "foo", Nodes: map[string]tree{
|
||||
"work": {FileInfoPath: filepath.FromSlash("foo/work"),
|
||||
Nodes: map[string]tree{
|
||||
"user1": {Path: filepath.FromSlash("foo/work/user1")},
|
||||
"user2": {Path: filepath.FromSlash("foo/work/user2")},
|
||||
"user1": {Path: filepath.FromSlash("foo/work/user1"), Explicit: false},
|
||||
"user2": {Path: filepath.FromSlash("foo/work/user2"), Explicit: true},
|
||||
},
|
||||
},
|
||||
}},
|
||||
@@ -334,16 +342,17 @@ func TestTree(t *testing.T) {
|
||||
targets: []string{"foo/work/user2/data/secret", "foo"},
|
||||
want: tree{Nodes: map[string]tree{
|
||||
"foo": {Root: ".", FileInfoPath: "foo", Nodes: map[string]tree{
|
||||
"other": {Path: filepath.FromSlash("foo/other")},
|
||||
"other": {Path: filepath.FromSlash("foo/other"), Explicit: false},
|
||||
"work": {FileInfoPath: filepath.FromSlash("foo/work"), Nodes: map[string]tree{
|
||||
"user2": {FileInfoPath: filepath.FromSlash("foo/work/user2"), Nodes: map[string]tree{
|
||||
"data": {FileInfoPath: filepath.FromSlash("foo/work/user2/data"), Nodes: map[string]tree{
|
||||
"secret": {
|
||||
Path: filepath.FromSlash("foo/work/user2/data/secret"),
|
||||
Path: filepath.FromSlash("foo/work/user2/data/secret"),
|
||||
Explicit: true,
|
||||
},
|
||||
}},
|
||||
}},
|
||||
"user3": {Path: filepath.FromSlash("foo/work/user3")},
|
||||
"user3": {Path: filepath.FromSlash("foo/work/user3"), Explicit: false},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
@@ -373,11 +382,12 @@ func TestTree(t *testing.T) {
|
||||
"driveA": {FileInfoPath: filepath.FromSlash("mnt/driveA"), Nodes: map[string]tree{
|
||||
"work": {FileInfoPath: filepath.FromSlash("mnt/driveA/work"), Nodes: map[string]tree{
|
||||
"driveB": {
|
||||
Path: filepath.FromSlash("mnt/driveA/work/driveB"),
|
||||
Path: filepath.FromSlash("mnt/driveA/work/driveB"),
|
||||
Explicit: true,
|
||||
},
|
||||
"test1": {Path: filepath.FromSlash("mnt/driveA/work/test1")},
|
||||
"test1": {Path: filepath.FromSlash("mnt/driveA/work/test1"), Explicit: false},
|
||||
}},
|
||||
"test2": {Path: filepath.FromSlash("mnt/driveA/test2")},
|
||||
"test2": {Path: filepath.FromSlash("mnt/driveA/test2"), Explicit: false},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
@@ -387,7 +397,7 @@ func TestTree(t *testing.T) {
|
||||
want: tree{Nodes: map[string]tree{
|
||||
"foo": {Root: ".", FileInfoPath: "foo", Nodes: map[string]tree{
|
||||
"work": {FileInfoPath: filepath.FromSlash("foo/work"), Nodes: map[string]tree{
|
||||
"user": {Path: filepath.FromSlash("foo/work/user")},
|
||||
"user": {Path: filepath.FromSlash("foo/work/user"), Explicit: true},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
@@ -397,7 +407,7 @@ func TestTree(t *testing.T) {
|
||||
want: tree{Nodes: map[string]tree{
|
||||
"foo": {Root: ".", FileInfoPath: "foo", Nodes: map[string]tree{
|
||||
"work": {FileInfoPath: filepath.FromSlash("foo/work"), Nodes: map[string]tree{
|
||||
"user": {Path: filepath.FromSlash("foo/work/user")},
|
||||
"user": {Path: filepath.FromSlash("foo/work/user"), Explicit: true},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
@@ -409,7 +419,7 @@ func TestTree(t *testing.T) {
|
||||
"c": {Root: `c:\`, FileInfoPath: `c:\`, Nodes: map[string]tree{
|
||||
"users": {FileInfoPath: `c:\users`, Nodes: map[string]tree{
|
||||
"foobar": {FileInfoPath: `c:\users\foobar`, Nodes: map[string]tree{
|
||||
"temp": {Path: `c:\users\foobar\temp`},
|
||||
"temp": {Path: `c:\users\foobar\temp`, Explicit: true},
|
||||
}},
|
||||
}},
|
||||
}},
|
||||
@@ -445,7 +455,7 @@ func TestTree(t *testing.T) {
|
||||
back := rtest.Chdir(t, tempdir)
|
||||
defer back()
|
||||
|
||||
tree, err := newTree(fs.Local{}, test.targets)
|
||||
tree, err := newTree(fs.Local{}, testBackupTargets(test.targets))
|
||||
if test.mustError {
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
|
||||
Reference in New Issue
Block a user