From 73c9780321ebc8024b1df3d295818af3fbe79de7 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sat, 3 Aug 2024 19:10:11 +0200 Subject: [PATCH] backup: store but warn if extended metadata for item is incomplete Files were not included in the backup if the extended metadata for the file could not be read. This is rather drastic. Instead settle on returning a warning but still including the file in the backup. --- changelog/unreleased/pull-4977 | 15 +++++++++++++++ internal/archiver/archiver.go | 3 ++- internal/archiver/archiver_test.go | 26 ++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 changelog/unreleased/pull-4977 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") +}