mirror of
https://github.com/octoleo/restic.git
synced 2024-12-26 12:27:33 +00:00
Add test case for inconsistent timestamps and permissions restoration
Reproduce from https://github.com/restic/restic/issues/1212
This commit is contained in:
parent
5fd3dbccb7
commit
295ddb9e57
@ -6,6 +6,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@ -23,14 +24,17 @@ type Snapshot struct {
|
||||
}
|
||||
|
||||
type File struct {
|
||||
Data string
|
||||
Links uint64
|
||||
Inode uint64
|
||||
Data string
|
||||
Links uint64
|
||||
Inode uint64
|
||||
Mode os.FileMode
|
||||
ModTime time.Time
|
||||
}
|
||||
|
||||
type Dir struct {
|
||||
Nodes map[string]Node
|
||||
Mode os.FileMode
|
||||
Nodes map[string]Node
|
||||
Mode os.FileMode
|
||||
ModTime time.Time
|
||||
}
|
||||
|
||||
func saveFile(t testing.TB, repo restic.Repository, node File) restic.ID {
|
||||
@ -66,9 +70,14 @@ func saveDir(t testing.TB, repo restic.Repository, nodes map[string]Node, inode
|
||||
if len(n.(File).Data) > 0 {
|
||||
fc = append(fc, saveFile(t, repo, node))
|
||||
}
|
||||
mode := node.Mode
|
||||
if mode == 0 {
|
||||
mode = 0644
|
||||
}
|
||||
tree.Insert(&restic.Node{
|
||||
Type: "file",
|
||||
Mode: 0644,
|
||||
Mode: mode,
|
||||
ModTime: node.ModTime,
|
||||
Name: name,
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@ -88,6 +97,7 @@ func saveDir(t testing.TB, repo restic.Repository, nodes map[string]Node, inode
|
||||
tree.Insert(&restic.Node{
|
||||
Type: "dir",
|
||||
Mode: mode,
|
||||
ModTime: node.ModTime,
|
||||
Name: name,
|
||||
UID: uint32(os.Getuid()),
|
||||
GID: uint32(os.Getgid()),
|
||||
@ -655,6 +665,7 @@ func TestRestorerTraverseTree(t *testing.T) {
|
||||
},
|
||||
Visitor: checkVisitOrder([]TreeVisit{
|
||||
{"visitNode", "/dir/otherfile"},
|
||||
{"leaveDir", "/dir"},
|
||||
}),
|
||||
},
|
||||
}
|
||||
@ -688,3 +699,108 @@ func TestRestorerTraverseTree(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeFileMode(mode os.FileMode) os.FileMode {
|
||||
if runtime.GOOS == "windows" {
|
||||
if mode.IsDir() {
|
||||
return 0555 | os.ModeDir
|
||||
}
|
||||
return os.FileMode(0444)
|
||||
}
|
||||
return mode
|
||||
}
|
||||
|
||||
func checkConsistentInfo(t testing.TB, file string, fi os.FileInfo, modtime time.Time, mode os.FileMode) {
|
||||
if fi.Mode() != mode {
|
||||
t.Errorf("checking %q, Mode() returned wrong value, want 0%o, got 0%o", file, mode, fi.Mode())
|
||||
}
|
||||
|
||||
if !fi.ModTime().Equal(modtime) {
|
||||
t.Errorf("checking %s, ModTime() returned wrong value, want %v, got %v", file, modtime, fi.ModTime())
|
||||
}
|
||||
}
|
||||
|
||||
// test inspired from test case https://github.com/restic/restic/issues/1212
|
||||
func TestRestorerConsistentTimestampsAndPermissions(t *testing.T) {
|
||||
timeForTest := time.Date(2019, time.January, 9, 1, 46, 40, 0, time.UTC)
|
||||
|
||||
repo, cleanup := repository.TestRepository(t)
|
||||
defer cleanup()
|
||||
|
||||
_, id := saveSnapshot(t, repo, Snapshot{
|
||||
Nodes: map[string]Node{
|
||||
"dir": Dir{
|
||||
Mode: normalizeFileMode(0750 | os.ModeDir),
|
||||
ModTime: timeForTest,
|
||||
Nodes: map[string]Node{
|
||||
"file1": File{
|
||||
Mode: normalizeFileMode(os.FileMode(0700)),
|
||||
ModTime: timeForTest,
|
||||
Data: "content: file\n",
|
||||
},
|
||||
"anotherfile": File{
|
||||
Data: "content: file\n",
|
||||
},
|
||||
"subdir": Dir{
|
||||
Mode: normalizeFileMode(0700 | os.ModeDir),
|
||||
ModTime: timeForTest,
|
||||
Nodes: map[string]Node{
|
||||
"file2": File{
|
||||
Mode: normalizeFileMode(os.FileMode(0666)),
|
||||
ModTime: timeForTest,
|
||||
Links: 2,
|
||||
Inode: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
res, err := NewRestorer(repo, id)
|
||||
rtest.OK(t, err)
|
||||
|
||||
res.SelectFilter = func(item string, dstpath string, node *restic.Node) (selectedForRestore bool, childMayBeSelected bool) {
|
||||
switch filepath.ToSlash(item) {
|
||||
case "/dir":
|
||||
childMayBeSelected = true
|
||||
case "/dir/file1":
|
||||
selectedForRestore = true
|
||||
childMayBeSelected = false
|
||||
case "/dir/subdir":
|
||||
selectedForRestore = true
|
||||
childMayBeSelected = true
|
||||
case "/dir/subdir/file2":
|
||||
selectedForRestore = true
|
||||
childMayBeSelected = false
|
||||
}
|
||||
return selectedForRestore, childMayBeSelected
|
||||
}
|
||||
|
||||
tempdir, cleanup := rtest.TempDir(t)
|
||||
defer cleanup()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
err = res.RestoreTo(ctx, tempdir)
|
||||
rtest.OK(t, err)
|
||||
|
||||
var testPatterns = []struct {
|
||||
path string
|
||||
modtime time.Time
|
||||
mode os.FileMode
|
||||
}{
|
||||
{"dir", timeForTest, normalizeFileMode(0750 | os.ModeDir)},
|
||||
{filepath.Join("dir", "file1"), timeForTest, normalizeFileMode(os.FileMode(0700))},
|
||||
{filepath.Join("dir", "subdir"), timeForTest, normalizeFileMode(0700 | os.ModeDir)},
|
||||
{filepath.Join("dir", "subdir", "file2"), timeForTest, normalizeFileMode(os.FileMode(0666))},
|
||||
}
|
||||
|
||||
for _, test := range testPatterns {
|
||||
f, err := os.Stat(filepath.Join(tempdir, test.path))
|
||||
rtest.OK(t, err)
|
||||
checkConsistentInfo(t, test.path, f, test.modtime, test.mode)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user