Merge pull request #1446 from restic/fix-relative-restore
restore: Fix restore to relative path
This commit is contained in:
commit
26e5db1849
2 changed files with 107 additions and 0 deletions
|
@ -67,6 +67,7 @@ func (res *Restorer) restoreTo(ctx context.Context, target, location string, tre
|
||||||
nodeLocation := filepath.Join(location, nodeName)
|
nodeLocation := filepath.Join(location, nodeName)
|
||||||
|
|
||||||
if target == nodeTarget || !fs.HasPathPrefix(target, nodeTarget) {
|
if target == nodeTarget || !fs.HasPathPrefix(target, nodeTarget) {
|
||||||
|
debug.Log("target: %v %v", target, nodeTarget)
|
||||||
debug.Log("node %q has invalid target path %q", node.Name, nodeTarget)
|
debug.Log("node %q has invalid target path %q", node.Name, nodeTarget)
|
||||||
err := res.Error(nodeLocation, node, errors.New("node has invalid path"))
|
err := res.Error(nodeLocation, node, errors.New("node has invalid path"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -145,6 +146,14 @@ func (res *Restorer) restoreNodeTo(ctx context.Context, node *Node, target, loca
|
||||||
// RestoreTo creates the directories and files in the snapshot below dst.
|
// RestoreTo creates the directories and files in the snapshot below dst.
|
||||||
// Before an item is created, res.Filter is called.
|
// Before an item is created, res.Filter is called.
|
||||||
func (res *Restorer) RestoreTo(ctx context.Context, dst string) error {
|
func (res *Restorer) RestoreTo(ctx context.Context, dst string) error {
|
||||||
|
var err error
|
||||||
|
if !filepath.IsAbs(dst) {
|
||||||
|
dst, err = filepath.Abs(dst)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Abs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
idx := NewHardlinkIndex()
|
idx := NewHardlinkIndex()
|
||||||
return res.restoreTo(ctx, dst, string(filepath.Separator), *res.sn.Tree, idx)
|
return res.restoreTo(ctx, dst, string(filepath.Separator), *res.sn.Tree, idx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -310,3 +310,101 @@ func TestRestorer(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func chdir(t testing.TB, target string) func() {
|
||||||
|
prev, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("chdir to %v", target)
|
||||||
|
err = os.Chdir(target)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
t.Logf("chdir back to %v", prev)
|
||||||
|
err = os.Chdir(prev)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRestorerRelative(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
Snapshot
|
||||||
|
Files map[string]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Snapshot: Snapshot{
|
||||||
|
Nodes: map[string]Node{
|
||||||
|
"foo": File{"content: foo\n"},
|
||||||
|
"dirtest": Dir{
|
||||||
|
Nodes: map[string]Node{
|
||||||
|
"file": File{"content: file\n"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Files: map[string]string{
|
||||||
|
"foo": "content: foo\n",
|
||||||
|
"dirtest/file": "content: file\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
repo, cleanup := repository.TestRepository(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
_, id := saveSnapshot(t, repo, test.Snapshot)
|
||||||
|
t.Logf("snapshot saved as %v", id.Str())
|
||||||
|
|
||||||
|
res, err := restic.NewRestorer(repo, id)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tempdir, cleanup := rtest.TempDir(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
cleanup = chdir(t, tempdir)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
errors := make(map[string]string)
|
||||||
|
res.Error = func(dir string, node *restic.Node, err error) error {
|
||||||
|
t.Logf("restore returned error for %q in dir %v: %v", node.Name, dir, err)
|
||||||
|
dir = toSlash(dir)
|
||||||
|
errors[dir+"#"+node.Name] = err.Error()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
err = res.RestoreTo(ctx, "restore")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for filename, err := range errors {
|
||||||
|
t.Errorf("unexpected error for %v found: %v", filename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for filename, content := range test.Files {
|
||||||
|
data, err := ioutil.ReadFile(filepath.Join(tempdir, "restore", filepath.FromSlash(filename)))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unable to read file %v: %v", filename, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(data, []byte(content)) {
|
||||||
|
t.Errorf("file %v has wrong content: want %q, got %q", filename, content, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue