From a7dc18e6977018d74d158d7a96127d335124f5db Mon Sep 17 00:00:00 2001 From: Daniel Danner Date: Fri, 20 Oct 2023 20:15:53 +0200 Subject: [PATCH 1/3] Add bitrot detection to "diff" command This introduces a new modifier to the output of the diff command. It appears whenever two files being compared only differ in their content but not in their metadata. As far as we know, under normal circumstances, this should only ever happen if some kind of bitrot has happened in the source file. The prerequisite for this detection to work is that the right-side snapshot of the comparison has been created with "backup --force". --- changelog/unreleased/pull-4526 | 11 +++++++++++ cmd/restic/cmd_diff.go | 9 +++++++++ doc/075_scripting.rst | 3 ++- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 changelog/unreleased/pull-4526 diff --git a/changelog/unreleased/pull-4526 b/changelog/unreleased/pull-4526 new file mode 100644 index 000000000..3a538f57a --- /dev/null +++ b/changelog/unreleased/pull-4526 @@ -0,0 +1,11 @@ +Enhancement: Add bitrot detection to `diff` command + +The output of the `diff` command now includes the modifier `?` for files +to indicate bitrot in backed up files. It will appear whenever there is a +difference in content while the metadata is exactly the same. Since files with +unchanged metadata are normally not read again when creating a backup, the +detection is only effective if the right-hand side of the diff has been created +with "backup --force". + +https://github.com/restic/restic/issues/805 +https://github.com/restic/restic/pull/4526 diff --git a/cmd/restic/cmd_diff.go b/cmd/restic/cmd_diff.go index c54fc06d4..329d11d02 100644 --- a/cmd/restic/cmd_diff.go +++ b/cmd/restic/cmd_diff.go @@ -27,6 +27,7 @@ directory: * U The metadata (access mode, timestamps, ...) for the item was updated * M The file's content was modified * T The type was changed, e.g. a file was made a symlink +* ? Bitrot detected: The file's content has changed but all metadata is the same To only compare files in specific subfolders, you can use the ":" syntax, where "subfolder" is a path within the @@ -272,6 +273,14 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref !reflect.DeepEqual(node1.Content, node2.Content) { mod += "M" stats.ChangedFiles++ + + node1NilContent := node1 + node2NilContent := node2 + node1NilContent.Content = nil + node2NilContent.Content = nil + if node1NilContent.Equals(*node2NilContent) { + mod += "?" + } } else if c.opts.ShowMetadata && !node1.Equals(*node2) { mod += "U" } diff --git a/doc/075_scripting.rst b/doc/075_scripting.rst index d94074232..f46572209 100644 --- a/doc/075_scripting.rst +++ b/doc/075_scripting.rst @@ -201,7 +201,8 @@ change +------------------+--------------------------------------------------------------+ | ``modifier`` | Type of change, a concatenation of the following characters: | | | "+" = added, "-" = removed, "T" = entry type changed, | -| | "M" = file content changed, "U" = metadata changed | +| | "M" = file content changed, "U" = metadata changed, | +| | "?" = bitrot detected | +------------------+--------------------------------------------------------------+ statistics From 3549635243d77679871fa6dd54b1d17b9ac5cb7b Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sat, 6 Jan 2024 20:07:46 +0100 Subject: [PATCH 2/3] diff: copy nodes before modifying them for bitrot detection --- cmd/restic/cmd_diff.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/restic/cmd_diff.go b/cmd/restic/cmd_diff.go index 329d11d02..aafc558b8 100644 --- a/cmd/restic/cmd_diff.go +++ b/cmd/restic/cmd_diff.go @@ -274,11 +274,13 @@ func (c *Comparer) diffTree(ctx context.Context, stats *DiffStatsContainer, pref mod += "M" stats.ChangedFiles++ - node1NilContent := node1 - node2NilContent := node2 + node1NilContent := *node1 + node2NilContent := *node2 node1NilContent.Content = nil node2NilContent.Content = nil - if node1NilContent.Equals(*node2NilContent) { + // the bitrot detection may not work if `backup --ignore-inode` or `--ignore-ctime` were used + if node1NilContent.Equals(node2NilContent) { + // probable bitrot detected mod += "?" } } else if c.opts.ShowMetadata && !node1.Equals(*node2) { From 4f6b1bb6f6388678345088a509e98a6560aee7a5 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sat, 6 Jan 2024 20:11:02 +0100 Subject: [PATCH 3/3] diff: document limitations regarding metadata --- cmd/restic/cmd_diff.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/restic/cmd_diff.go b/cmd/restic/cmd_diff.go index aafc558b8..ea40d2860 100644 --- a/cmd/restic/cmd_diff.go +++ b/cmd/restic/cmd_diff.go @@ -29,6 +29,9 @@ directory: * T The type was changed, e.g. a file was made a symlink * ? Bitrot detected: The file's content has changed but all metadata is the same +Metadata comparison will likely not work if a backup was created using the +'--ignore-inode' or '--ignore-ctime' option. + To only compare files in specific subfolders, you can use the ":" syntax, where "subfolder" is a path within the snapshot.