diff --git a/changelog/unreleased/pull-4977 b/changelog/unreleased/pull-4977 new file mode 100644 index 000000000..702df29a7 --- /dev/null +++ b/changelog/unreleased/pull-4977 @@ -0,0 +1,15 @@ +Change: let `backup` store files with incomplete metadata + +If restic failed to read the extended metadata for a file or folder while +creating a backup, then the file or folder was not included in the resulting +snapshot. Instead, only a warning message was printed along with exiting +with exit code 3. + +Now, restic also includes items for which the extended metadata could not +be read in a snapshot. The warning message has been changed to read +``` +incomplete metadata for /path/to/file: details on error +``` + +https://github.com/restic/restic/issues/4953 +https://github.com/restic/restic/pull/4977 diff --git a/internal/archiver/archiver.go b/internal/archiver/archiver.go index d9f089e81..e44151298 100644 --- a/internal/archiver/archiver.go +++ b/internal/archiver/archiver.go @@ -263,7 +263,8 @@ func (arch *Archiver) nodeFromFileInfo(snPath, filename string, fi os.FileInfo, // overwrite name to match that within the snapshot node.Name = path.Base(snPath) if err != nil { - return node, fmt.Errorf("nodeFromFileInfo %v: %w", filename, err) + err = fmt.Errorf("incomplete metadata for %v: %w", filename, err) + return node, arch.error(filename, err) } return node, err } diff --git a/internal/archiver/archiver_test.go b/internal/archiver/archiver_test.go index f38d5b0de..b519387db 100644 --- a/internal/archiver/archiver_test.go +++ b/internal/archiver/archiver_test.go @@ -3,6 +3,7 @@ package archiver import ( "bytes" "context" + "fmt" "io" "os" "path/filepath" @@ -2338,3 +2339,28 @@ func TestRacyFileSwap(t *testing.T) { t.Errorf("Save() excluded the node, that's unexpected") } } + +func TestMetadataBackupErrorFiltering(t *testing.T) { + tempdir := t.TempDir() + repo := repository.TestRepository(t) + + filename := filepath.Join(tempdir, "file") + rtest.OK(t, os.WriteFile(filename, []byte("example"), 0o600)) + fi, err := os.Stat(filename) + rtest.OK(t, err) + + arch := New(repo, fs.Local{}, Options{}) + + var filteredErr error + replacementErr := fmt.Errorf("replacement") + arch.Error = func(item string, err error) error { + filteredErr = err + return replacementErr + } + + // check that errors from reading extended metadata are properly filtered + node, err := arch.nodeFromFileInfo("file", filename+"invalid", fi, false) + rtest.Assert(t, node != nil, "node is missing") + rtest.Assert(t, err == replacementErr, "expected %v got %v", replacementErr, err) + rtest.Assert(t, filteredErr != nil, "missing inner error") +}