mirror of
https://github.com/octoleo/restic.git
synced 2024-11-27 07:16:40 +00:00
restore: correctly handle existing hardlinks
With hardlinks there's no efficient way to detect which files are linked with each other. Thus, just start from scratch when restore has to modify a hardlinked file.
This commit is contained in:
parent
f19b69af25
commit
d265ec64f2
@ -89,7 +89,18 @@ func createFile(path string, createSize int64, sparse bool) (*os.File, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if f == nil || !fi.Mode().IsRegular() {
|
|
||||||
|
mustReplace := f == nil || !fi.Mode().IsRegular()
|
||||||
|
if !mustReplace {
|
||||||
|
ex := fs.ExtendedStat(fi)
|
||||||
|
if ex.Links > 1 {
|
||||||
|
// there is no efficient way to find out which other files might be linked to this file
|
||||||
|
// thus nuke the existing file and start with a fresh one
|
||||||
|
mustReplace = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mustReplace {
|
||||||
// close handle if we still have it
|
// close handle if we still have it
|
||||||
if f != nil {
|
if f != nil {
|
||||||
if err := f.Close(); err != nil {
|
if err := f.Close(); err != nil {
|
||||||
|
@ -45,36 +45,46 @@ func TestCreateFile(t *testing.T) {
|
|||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
name string
|
name string
|
||||||
create func(t testing.TB, path string)
|
create func(t testing.TB, path string)
|
||||||
|
check func(t testing.TB, path string)
|
||||||
err error
|
err error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"file",
|
name: "file",
|
||||||
func(t testing.TB, path string) {
|
create: func(t testing.TB, path string) {
|
||||||
rtest.OK(t, os.WriteFile(path, []byte("test-test-test-data"), 0o400))
|
rtest.OK(t, os.WriteFile(path, []byte("test-test-test-data"), 0o400))
|
||||||
},
|
},
|
||||||
nil,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"empty dir",
|
name: "empty dir",
|
||||||
func(t testing.TB, path string) {
|
create: func(t testing.TB, path string) {
|
||||||
rtest.OK(t, os.Mkdir(path, 0o400))
|
rtest.OK(t, os.Mkdir(path, 0o400))
|
||||||
},
|
},
|
||||||
nil,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"symlink",
|
name: "symlink",
|
||||||
func(t testing.TB, path string) {
|
create: func(t testing.TB, path string) {
|
||||||
rtest.OK(t, os.Symlink("./something", path))
|
rtest.OK(t, os.Symlink("./something", path))
|
||||||
},
|
},
|
||||||
nil,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filled dir",
|
name: "filled dir",
|
||||||
func(t testing.TB, path string) {
|
create: func(t testing.TB, path string) {
|
||||||
rtest.OK(t, os.Mkdir(path, 0o700))
|
rtest.OK(t, os.Mkdir(path, 0o700))
|
||||||
rtest.OK(t, os.WriteFile(filepath.Join(path, "file"), []byte("data"), 0o400))
|
rtest.OK(t, os.WriteFile(filepath.Join(path, "file"), []byte("data"), 0o400))
|
||||||
},
|
},
|
||||||
syscall.ENOTEMPTY,
|
err: syscall.ENOTEMPTY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "hardlinks",
|
||||||
|
create: func(t testing.TB, path string) {
|
||||||
|
rtest.OK(t, os.WriteFile(path, []byte("test-test-test-data"), 0o400))
|
||||||
|
rtest.OK(t, os.Link(path, path+"h"))
|
||||||
|
},
|
||||||
|
check: func(t testing.TB, path string) {
|
||||||
|
data, err := os.ReadFile(path + "h")
|
||||||
|
rtest.OK(t, err)
|
||||||
|
rtest.Equals(t, "test-test-test-data", string(data), "unexpected content change")
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,8 +102,8 @@ func TestCreateFile(t *testing.T) {
|
|||||||
|
|
||||||
for i, sc := range scenarios {
|
for i, sc := range scenarios {
|
||||||
t.Run(sc.name, func(t *testing.T) {
|
t.Run(sc.name, func(t *testing.T) {
|
||||||
for _, test := range tests {
|
for j, test := range tests {
|
||||||
path := basepath + fmt.Sprintf("%v", i)
|
path := basepath + fmt.Sprintf("%v%v", i, j)
|
||||||
sc.create(t, path)
|
sc.create(t, path)
|
||||||
f, err := createFile(path, test.size, test.isSparse)
|
f, err := createFile(path, test.size, test.isSparse)
|
||||||
if sc.err == nil {
|
if sc.err == nil {
|
||||||
@ -103,6 +113,9 @@ func TestCreateFile(t *testing.T) {
|
|||||||
rtest.Assert(t, fi.Mode().IsRegular(), "wrong filetype %v", fi.Mode())
|
rtest.Assert(t, fi.Mode().IsRegular(), "wrong filetype %v", fi.Mode())
|
||||||
rtest.Assert(t, fi.Size() <= test.size, "unexpected file size expected %v, got %v", test.size, fi.Size())
|
rtest.Assert(t, fi.Size() <= test.size, "unexpected file size expected %v, got %v", test.size, fi.Size())
|
||||||
rtest.OK(t, f.Close())
|
rtest.OK(t, f.Close())
|
||||||
|
if sc.check != nil {
|
||||||
|
sc.check(t, path)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
rtest.Assert(t, errors.Is(err, sc.err), "unexpected error got %v expected %v", err, sc.err)
|
rtest.Assert(t, errors.Is(err, sc.err), "unexpected error got %v expected %v", err, sc.err)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user