* [PATCH 1/3] btrfs-progs: introduce "btrfs rescue fix-data-csums"
2025-05-11 6:48 [PATCH 0/3] btrfs-progs: introduce a skeleton version of "btrfs rescue fix-data-csums" Qu Wenruo
@ 2025-05-11 6:48 ` Qu Wenruo
2025-05-11 6:48 ` [PATCH 2/3] btrfs-progs: fix a bug in btrfs_find_item() Qu Wenruo
2025-05-11 6:48 ` [PATCH 3/3] btrfs-progs: fix-data-csums: show affected files Qu Wenruo
2 siblings, 0 replies; 4+ messages in thread
From: Qu Wenruo @ 2025-05-11 6:48 UTC (permalink / raw)
To: linux-btrfs
It's a long known problem that direct IO can lead to data checksum
mismatches if the user space is also modifying its buffer during
writeback.
Although it's fixed in the recent v6.15 release (and backported to older
kernels), we still need a user friendly way to fix those problems.
This patch introduce the dryrun version of "btrfs rescue
fix-data-csums", which reports the logical bytenr and corrupted mirrors.
It is planned to implement the following repair methods:
- Update the data checksum item
Using specified mirror or the most agreed checksum using on-disk data,
to update the checksum item in checksum tree.
- Deleting all involved files
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
Documentation/btrfs-rescue.rst | 19 ++
Makefile | 2 +-
cmds/rescue-fix-data-csums.c | 326 +++++++++++++++++++++++++++++++++
cmds/rescue.c | 44 +++++
cmds/rescue.h | 6 +
5 files changed, 396 insertions(+), 1 deletion(-)
create mode 100644 cmds/rescue-fix-data-csums.c
diff --git a/Documentation/btrfs-rescue.rst b/Documentation/btrfs-rescue.rst
index f52e6c26359a..fe2596e5d558 100644
--- a/Documentation/btrfs-rescue.rst
+++ b/Documentation/btrfs-rescue.rst
@@ -50,6 +50,25 @@ fix-device-size <device>
WARNING: CPU: 3 PID: 439 at fs/btrfs/ctree.h:1559 btrfs_update_device+0x1c5/0x1d0 [btrfs]
+fix-data-csums [-d] <device>
+ fix data checksum mismatch
+
+ There is a long existing problem that if a user space program is doing
+ direct IO and modifies the buffer before the write back finished, it
+ can lead to data checksum mismatches.
+
+ This problem is known but not fixed until upstream release v6.15
+ (backported to older kernels). So it's possible to hit false data
+ checksum mismatch.
+
+ In that case this program can be utilized to repair such problem.
+
+ ``Options``
+
+ -d
+ dryrun, will report errors but not repair, the default behavior.
+
+
.. _man-rescue-clear-ino-cache:
clear-ino-cache <device>
diff --git a/Makefile b/Makefile
index 7e36aa425736..75fa2249b078 100644
--- a/Makefile
+++ b/Makefile
@@ -256,7 +256,7 @@ cmds_objects = cmds/subvolume.o cmds/subvolume-list.o \
cmds/inspect.o cmds/balance.o cmds/send.o cmds/receive.o \
cmds/quota.o cmds/qgroup.o cmds/replace.o check/main.o \
cmds/restore.o cmds/rescue.o cmds/rescue-chunk-recover.o \
- cmds/rescue-super-recover.o \
+ cmds/rescue-super-recover.o cmds/rescue-fix-data-csums.o \
cmds/property.o cmds/filesystem-usage.o cmds/inspect-dump-tree.o \
cmds/inspect-dump-super.o cmds/inspect-tree-stats.o cmds/filesystem-du.o \
cmds/reflink.o \
diff --git a/cmds/rescue-fix-data-csums.c b/cmds/rescue-fix-data-csums.c
new file mode 100644
index 000000000000..66e34b279f13
--- /dev/null
+++ b/cmds/rescue-fix-data-csums.c
@@ -0,0 +1,326 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include "kerncompat.h"
+#include "kernel-shared/disk-io.h"
+#include "kernel-shared/ctree.h"
+#include "kernel-shared/volumes.h"
+#include "common/messages.h"
+#include "common/open-utils.h"
+#include "cmds/rescue.h"
+
+/*
+ * Record one corrupted data blocks.
+ *
+ * We do not report immediately, this is for future file deleting support.
+ */
+struct corrupted_block {
+ struct list_head list;
+ /* The logical bytenr of the exact corrupted block. */
+ u64 logical;
+
+ /* Which mirror failed. */
+ unsigned long *error_mirror_bitmap;
+
+ /* The bytenr/length for the data extent covering above @bytenr. */
+ u64 extent_start;
+ u64 extent_len;
+
+ unsigned int num_mirrors;
+};
+
+static int global_repair_mode;
+LIST_HEAD(corrupted_blocks);
+
+static int add_corrupted_block(struct btrfs_fs_info *fs_info, u64 logical,
+ unsigned int mirror, unsigned int num_mirrors)
+{
+ struct corrupted_block *last;
+ struct btrfs_root *extent_root = btrfs_extent_root(fs_info, logical);
+ struct btrfs_path path = { 0 };
+ struct btrfs_key key;
+ u64 size;
+ int ret;
+
+ if (list_empty(&corrupted_blocks))
+ goto add;
+
+ last = list_entry(corrupted_blocks.prev, struct corrupted_block, list);
+ /* The last entry is the same, just set update the error mirror bitmap. */
+ if (last->logical == logical) {
+ UASSERT(last->error_mirror_bitmap);
+ set_bit(mirror, last->error_mirror_bitmap);
+ return 0;
+ }
+add:
+ last = calloc(1, sizeof(*last));
+ if (!last)
+ return -ENOMEM;
+ last->error_mirror_bitmap = calloc(1, BITS_TO_LONGS(num_mirrors));
+ if (!last->error_mirror_bitmap) {
+ free(last);
+ return -ENOMEM;
+ }
+ set_bit(mirror, last->error_mirror_bitmap);
+ last->logical = logical;
+ last->num_mirrors = num_mirrors;
+
+ /* Locate the extent start/len for @logical. */
+ key.objectid = logical;
+ if (btrfs_fs_incompat(fs_info, SKINNY_METADATA))
+ key.type = BTRFS_METADATA_ITEM_KEY;
+ else
+ key.type = BTRFS_EXTENT_ITEM_KEY;
+ key.offset = (u64)-1;
+
+ ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
+ if (ret < 0)
+ goto out_free;
+ if (ret == 0) {
+ ret = -EUCLEAN;
+ goto out_free;
+ }
+ ret = btrfs_previous_extent_item(extent_root, &path, 0);
+ if (ret > 0)
+ ret = -ENOENT;
+ if (ret < 0)
+ goto out_free;
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ if (key.type == BTRFS_METADATA_ITEM_KEY)
+ size = fs_info->nodesize;
+ else
+ size = key.offset;
+ btrfs_release_path(&path);
+
+ if (key.objectid > logical || key.objectid + size <= logical) {
+ ret = -ENOENT;
+ goto out_free;
+ }
+ last->extent_start = key.objectid;
+ last->extent_len = key.offset;
+ list_add_tail(&last->list, &corrupted_blocks);
+ return 0;
+
+out_free:
+ btrfs_release_path(&path);
+ free(last->error_mirror_bitmap);
+ free(last);
+ return ret;
+}
+
+/*
+ * Verify all mirrors for @logical.
+ *
+ * If something critical happened, return <0 and should end the run immediately.
+ * Otherwise return 0, including data checksum mismatch or read failure.
+ */
+static int verify_one_data_block(struct btrfs_fs_info *fs_info,
+ struct extent_buffer *leaf,
+ unsigned long leaf_offset, u64 logical,
+ unsigned int num_mirrors)
+{
+ const u32 blocksize = fs_info->sectorsize;
+ const u32 csum_size = fs_info->csum_size;
+ u8 *buf;
+ u8 csum[BTRFS_CSUM_SIZE];
+ u8 csum_expected[BTRFS_CSUM_SIZE];
+ int ret = 0;
+
+ buf = malloc(blocksize);
+ if (!buf)
+ return -ENOMEM;
+
+ for (int mirror = 1; mirror <= num_mirrors; mirror++) {
+ u64 read_len = blocksize;
+
+ ret = read_data_from_disk(fs_info, buf, logical, &read_len, mirror);
+ if (ret < 0) {
+ /* IO error, add one record. */
+ ret = add_corrupted_block(fs_info, logical, mirror, num_mirrors);
+ if (ret < 0)
+ break;
+ }
+ /* Verify the data checksum. */
+ btrfs_csum_data(fs_info, fs_info->csum_type, buf, csum, blocksize);
+ read_extent_buffer(leaf, csum_expected, leaf_offset, csum_size);
+ if (memcmp(csum_expected, csum, csum_size) != 0) {
+ ret = add_corrupted_block(fs_info, logical, mirror, num_mirrors);
+ if (ret < 0)
+ break;
+ }
+ }
+
+ free(buf);
+ return ret;
+}
+
+static int iterate_one_csum_item(struct btrfs_fs_info *fs_info, struct btrfs_path *path)
+{
+ struct btrfs_key key;
+ const unsigned long item_ptr_off = btrfs_item_ptr_offset(path->nodes[0],
+ path->slots[0]);
+ const u32 blocksize = fs_info->sectorsize;
+ int num_mirrors;
+ u64 data_size;
+ u64 cur;
+ char *buf;
+ int ret = 0;
+
+ buf = malloc(blocksize);
+ if (!buf)
+ return -ENOMEM;
+
+ btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
+ data_size = btrfs_item_size(path->nodes[0], path->slots[0]) /
+ fs_info->csum_size * blocksize;
+ num_mirrors = btrfs_num_copies(fs_info, key.offset, data_size);
+
+ for (cur = 0; cur < data_size; cur += blocksize) {
+ const unsigned long leaf_offset = item_ptr_off +
+ cur / blocksize * fs_info->csum_size;
+
+ ret = verify_one_data_block(fs_info, path->nodes[0], leaf_offset,
+ key.offset + cur, num_mirrors);
+ if (ret < 0)
+ break;
+ }
+ free(buf);
+ return ret;
+}
+
+static int iterate_csum_root(struct btrfs_fs_info *fs_info, struct btrfs_root *csum_root)
+{
+ struct btrfs_path path = { 0 };
+ struct btrfs_key key;
+ int ret;
+
+ key.objectid = 0;
+ key.type = 0;
+ key.offset = 0;
+
+ ret = btrfs_search_slot(NULL, csum_root, &key, &path, 0, 0);
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to get the first tree block of csum tree: %m");
+ return ret;
+ }
+ UASSERT(ret > 0);
+ while (true) {
+ btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
+ if (key.type != BTRFS_EXTENT_CSUM_KEY)
+ goto next;
+ ret = iterate_one_csum_item(fs_info, &path);
+ if (ret < 0)
+ break;
+next:
+ ret = btrfs_next_item(csum_root, &path);
+ if (ret > 0) {
+ ret = 0;
+ break;
+ }
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to get next csum item: %m");
+ }
+ }
+ btrfs_release_path(&path);
+ return ret;
+}
+
+static void report_corrupted_blocks(void)
+{
+ struct corrupted_block *entry;
+
+ if (list_empty(&corrupted_blocks)) {
+ printf("No data checksum mismatch found\n");
+ return;
+ }
+
+ list_for_each_entry(entry, &corrupted_blocks, list) {
+ bool has_printed = false;
+
+ printf("logical=%llu corrtuped mirrors=", entry->logical);
+ /* Poor man's bitmap print. */
+ for (int i = 1; i <= entry->num_mirrors; i++) {
+ if (test_bit(i, entry->error_mirror_bitmap)) {
+ if (has_printed)
+ printf(",");
+ printf("%d", i);
+ has_printed=true;
+ }
+ }
+ printf("\n");
+ }
+}
+
+static void free_corrupted_blocks(void)
+{
+ while (!list_empty(&corrupted_blocks)) {
+ struct corrupted_block *entry;
+
+ entry = list_entry(corrupted_blocks.next, struct corrupted_block, list);
+ list_del_init(&entry->list);
+ free(entry->error_mirror_bitmap);
+ free(entry);
+ }
+}
+
+int btrfs_recover_fix_data_csums(const char *path, enum btrfs_fix_data_csums_mode mode)
+{
+ struct btrfs_fs_info *fs_info;
+ struct btrfs_root *csum_root;
+ struct open_ctree_args oca = { 0 };
+ int ret;
+
+ if (mode >= BTRFS_FIX_DATA_CSUMS_LAST)
+ return -EINVAL;
+
+ ret = check_mounted(path);
+ if (ret < 0) {
+ errno = -ret;
+ error("could not check mount status: %m");
+ return ret;
+ }
+ if (ret > 0) {
+ error("%s is currently mounted", path);
+ return -EBUSY;
+ }
+
+ global_repair_mode = mode;
+ oca.filename = path;
+ oca.flags = OPEN_CTREE_WRITES;
+ fs_info = open_ctree_fs_info(&oca);
+ if (!fs_info) {
+ error("failed to open btrfs at %s", path);
+ return -EIO;
+ }
+ csum_root = btrfs_csum_root(fs_info, 0);
+ if (!csum_root) {
+ error("failed to get csum root");
+ ret = -EIO;
+ goto out_close;
+ }
+ ret = iterate_csum_root(fs_info, csum_root);
+ if (ret) {
+ errno = -ret;
+ error("failed to iterate csum tree: %m");
+ }
+ report_corrupted_blocks();
+out_close:
+ free_corrupted_blocks();
+ close_ctree_fs_info(fs_info);
+ return ret;
+}
diff --git a/cmds/rescue.c b/cmds/rescue.c
index c60bf11675b9..29dedb94499e 100644
--- a/cmds/rescue.c
+++ b/cmds/rescue.c
@@ -22,6 +22,7 @@
#include <errno.h>
#include <stdbool.h>
#include <unistd.h>
+#include <getopt.h>
#include "kernel-lib/list.h"
#include "kernel-shared/ctree.h"
#include "kernel-shared/volumes.h"
@@ -275,6 +276,48 @@ out:
}
static DEFINE_SIMPLE_COMMAND(rescue_fix_device_size, "fix-device-size");
+static const char * const cmd_rescue_fix_data_csums_usage[] = {
+ "btrfs rescue fix-data-csums <device>",
+ "Fix data checksum mismatches.",
+ NULL
+};
+
+static int cmd_rescue_fix_data_csums(const struct cmd_struct *cmd,
+ int argc, char **argv)
+{
+ enum btrfs_fix_data_csums_mode mode = BTRFS_FIX_DATA_CSUMS_DRY_RUN;
+ int ret;
+ optind = 0;
+
+ while (1) {
+ int c;
+ enum { GETOPT_VAL_DRYRUN = GETOPT_VAL_FIRST, };
+ static const struct option long_options [] = {
+ {"dryrun", no_argument, NULL, 'd'},
+ {"NULL", 0, NULL, 0},
+ };
+ c = getopt_long(argc, argv, "d", long_options, NULL);
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'd':
+ mode = BTRFS_FIX_DATA_CSUMS_DRY_RUN;
+ break;
+ default:
+ usage_unknown_option(cmd, argv);
+ }
+ }
+ if (check_argc_min(argc - optind, 1))
+ return 1;
+ ret = btrfs_recover_fix_data_csums(argv[optind], mode);
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to fix data checksums: %m");
+ }
+ return !!ret;
+}
+static DEFINE_SIMPLE_COMMAND(rescue_fix_data_csums, "fix-data-csums");
+
static const char * const cmd_rescue_create_control_device_usage[] = {
"btrfs rescue create-control-device",
"Create /dev/btrfs-control (see 'CONTROL DEVICE' in btrfs(5))",
@@ -527,6 +570,7 @@ static const struct cmd_group rescue_cmd_group = {
&cmd_struct_rescue_super_recover,
&cmd_struct_rescue_zero_log,
&cmd_struct_rescue_fix_device_size,
+ &cmd_struct_rescue_fix_data_csums,
&cmd_struct_rescue_create_control_device,
&cmd_struct_rescue_clear_ino_cache,
&cmd_struct_rescue_clear_space_cache,
diff --git a/cmds/rescue.h b/cmds/rescue.h
index 5a9e46b7aae0..246f15c38564 100644
--- a/cmds/rescue.h
+++ b/cmds/rescue.h
@@ -20,7 +20,13 @@
#ifndef __BTRFS_RESCUE_H__
#define __BTRFS_RESCUE_H__
+enum btrfs_fix_data_csums_mode {
+ BTRFS_FIX_DATA_CSUMS_DRY_RUN,
+ BTRFS_FIX_DATA_CSUMS_LAST,
+};
+
int btrfs_recover_superblocks(const char *path, int yes);
int btrfs_recover_chunk_tree(const char *path, int yes);
+int btrfs_recover_fix_data_csums(const char *path, enum btrfs_fix_data_csums_mode mode);
#endif
--
2.49.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH 2/3] btrfs-progs: fix a bug in btrfs_find_item()
2025-05-11 6:48 [PATCH 0/3] btrfs-progs: introduce a skeleton version of "btrfs rescue fix-data-csums" Qu Wenruo
2025-05-11 6:48 ` [PATCH 1/3] btrfs-progs: introduce " Qu Wenruo
@ 2025-05-11 6:48 ` Qu Wenruo
2025-05-11 6:48 ` [PATCH 3/3] btrfs-progs: fix-data-csums: show affected files Qu Wenruo
2 siblings, 0 replies; 4+ messages in thread
From: Qu Wenruo @ 2025-05-11 6:48 UTC (permalink / raw)
To: linux-btrfs
[BUG]
There is a seldomly utilized function, btrfs_find_item(), which has no
document and is not behaving correctly.
Inside backref.c, iterate_inode_refs() and btrfs_ref_to_path() both
utilize this function, to find the parent inode.
However btrfs_find_item() will never return 0 if @ioff is passed as 0
for such usage, result early failure for all kinds of inode iteration
functions.
[CAUSE]
Both functions pass 0 as the @ioff parameter initially, e.g:
We have the following fs tree root:
item 0 key (256 INODE_ITEM 0) itemoff 16123 itemsize 160
generation 3 transid 9 size 6 nbytes 16384
block group 0 mode 40755 links 1 uid 0 gid 0 rdev 0
sequence 1 flags 0x0(none)
item 1 key (256 INODE_REF 256) itemoff 16111 itemsize 12
index 0 namelen 2 name: ..
item 2 key (256 DIR_ITEM 2507850652) itemoff 16078 itemsize 33
location key (257 INODE_ITEM 0) type FILE
transid 9 data_len 0 name_len 3
name: foo
item 3 key (256 DIR_INDEX 2) itemoff 16045 itemsize 33
location key (257 INODE_ITEM 0) type FILE
transid 9 data_len 0 name_len 3
name: foo
item 4 key (257 INODE_ITEM 0) itemoff 15885 itemsize 160
generation 9 transid 9 size 16384 nbytes 16384
block group 0 mode 100600 links 1 uid 0 gid 0 rdev 0
sequence 4 flags 0x0(none)
item 5 key (257 INODE_REF 256) itemoff 15872 itemsize 13
index 2 namelen 3 name: foo
item 6 key (257 EXTENT_DATA 0) itemoff 15819 itemsize 53
generation 9 type 1 (regular)
extent data disk byte 13631488 nr 16384
extent data offset 0 nr 16384 ram 16384
extent compression 0 (none)
Then we call paths_from_inode() with:
- @inum = 257
- ipath = {.fs_root = 5}
Then we got the following sequence:
iterate_irefs(257, fs_root, inode_to_path)
|- iterate_inode_refs()
|- inode_ref_info()
|- btrfs_find_item(257, 0, fs_root)
| Returned 1, with @found_key updated to
| (257, INODE_REF, 256).
This makes iterate_irefs() exit immediately, but obviously that
btrfs_find_item() call is to find any INODE_REF, not to find the
exact match.
[FIX]
If btrfs_find_item() found an item matching the objectid and type, then
it should return 0 other than 1.
Fix it and keep the behavior the same across btrfs-progs and the kernel.
Since we're here, also add some comments explaining the function.
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
kernel-shared/ctree.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/kernel-shared/ctree.c b/kernel-shared/ctree.c
index 3184c916175e..f90de606e7b1 100644
--- a/kernel-shared/ctree.c
+++ b/kernel-shared/ctree.c
@@ -1246,6 +1246,17 @@ static void reada_for_search(struct btrfs_fs_info *fs_info,
}
}
+/*
+ * Find the first key in @fs_root that matches all the following conditions:
+ *
+ * - key.obojectid == @iobjectid
+ * - key.type == @key_type
+ * - key.offset >= ioff
+ *
+ * Return 0 if such key can be found, and @found_key is updated.
+ * Return >0 if no such key can be found.
+ * Return <0 for critical errors.
+ */
int btrfs_find_item(struct btrfs_root *fs_root, struct btrfs_path *found_path,
u64 iobjectid, u64 ioff, u8 key_type,
struct btrfs_key *found_key)
@@ -1280,10 +1291,10 @@ int btrfs_find_item(struct btrfs_root *fs_root, struct btrfs_path *found_path,
btrfs_item_key_to_cpu(eb, found_key, path->slots[0]);
if (found_key->type != key.type ||
- found_key->objectid != key.objectid) {
+ found_key->objectid != key.objectid)
ret = 1;
- goto out;
- }
+ else
+ ret = 0;
out:
if (path != found_path)
--
2.49.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH 3/3] btrfs-progs: fix-data-csums: show affected files
2025-05-11 6:48 [PATCH 0/3] btrfs-progs: introduce a skeleton version of "btrfs rescue fix-data-csums" Qu Wenruo
2025-05-11 6:48 ` [PATCH 1/3] btrfs-progs: introduce " Qu Wenruo
2025-05-11 6:48 ` [PATCH 2/3] btrfs-progs: fix a bug in btrfs_find_item() Qu Wenruo
@ 2025-05-11 6:48 ` Qu Wenruo
2 siblings, 0 replies; 4+ messages in thread
From: Qu Wenruo @ 2025-05-11 6:48 UTC (permalink / raw)
To: linux-btrfs
Previously "btrfs rescue fix-data-csums" only show affected logical
bytenr, which is not helpful to determine which files are affected.
Enhance the output by also outputting the affected subvolumes (in
numeric form), and the file paths inside that subvolume.
The example looks like this:
logical=13631488 corrtuped mirrors=1 affected files:
(subvolume 5)/foo
(subvolume 5)/dir/bar
logical=13635584 corrtuped mirrors=1 affected files:
(subvolume 5)/foo
(subvolume 5)/dir/bar
Although the end result is still not perfect, it's still much easier to
find out which files are affected.
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
cmds/rescue-fix-data-csums.c | 59 ++++++++++++++++++++++++++++++++++--
1 file changed, 56 insertions(+), 3 deletions(-)
diff --git a/cmds/rescue-fix-data-csums.c b/cmds/rescue-fix-data-csums.c
index 66e34b279f13..bcba2741f2df 100644
--- a/cmds/rescue-fix-data-csums.c
+++ b/cmds/rescue-fix-data-csums.c
@@ -18,6 +18,7 @@
#include "kernel-shared/disk-io.h"
#include "kernel-shared/ctree.h"
#include "kernel-shared/volumes.h"
+#include "kernel-shared/backref.h"
#include "common/messages.h"
#include "common/open-utils.h"
#include "cmds/rescue.h"
@@ -201,6 +202,49 @@ static int iterate_one_csum_item(struct btrfs_fs_info *fs_info, struct btrfs_pat
return ret;
}
+static int print_filenames(u64 ino, u64 offset, u64 rootid, void *ctx)
+{
+ struct btrfs_fs_info *fs_info = ctx;
+ struct btrfs_root *root;
+ struct btrfs_key key;
+ struct inode_fs_paths *ipath;
+ struct btrfs_path path = { 0 };
+ int ret;
+
+ key.objectid = rootid;
+ key.type = BTRFS_ROOT_ITEM_KEY;
+ key.offset = (u64)-1;
+
+ root = btrfs_read_fs_root(fs_info, &key);
+ if (IS_ERR(root)) {
+ ret = PTR_ERR(root);
+ errno = -ret;
+ error("failed to get subvolume %llu: %m", rootid);
+ return ret;
+ }
+ ipath = init_ipath(128 * BTRFS_PATH_NAME_MAX, root, &path);
+ if (IS_ERR(ipath)) {
+ ret = PTR_ERR(ipath);
+ errno = -ret;
+ error("failed to initialize ipath: %m");
+ return ret;
+ }
+ ret = paths_from_inode(ino, ipath);
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to resolve root %llu ino %llu to paths: %m", rootid, ino);
+ goto out;
+ }
+ for (int i = 0; i < ipath->fspath->elem_cnt; i++)
+ printf(" (subvolume %llu)/%s\n", rootid, (char *)ipath->fspath->val[i]);
+ if (ipath->fspath->elem_missed)
+ printf(" (subvolume %llu) %d files not printed\n", rootid,
+ ipath->fspath->elem_missed);
+out:
+ free_ipath(ipath);
+ return ret;
+}
+
static int iterate_csum_root(struct btrfs_fs_info *fs_info, struct btrfs_root *csum_root)
{
struct btrfs_path path = { 0 };
@@ -240,9 +284,10 @@ next:
return ret;
}
-static void report_corrupted_blocks(void)
+static void report_corrupted_blocks(struct btrfs_fs_info *fs_info)
{
struct corrupted_block *entry;
+ struct btrfs_path path = { 0 };
if (list_empty(&corrupted_blocks)) {
printf("No data checksum mismatch found\n");
@@ -251,6 +296,7 @@ static void report_corrupted_blocks(void)
list_for_each_entry(entry, &corrupted_blocks, list) {
bool has_printed = false;
+ int ret;
printf("logical=%llu corrtuped mirrors=", entry->logical);
/* Poor man's bitmap print. */
@@ -262,7 +308,14 @@ static void report_corrupted_blocks(void)
has_printed=true;
}
}
- printf("\n");
+ printf(" affected files:\n");
+ ret = iterate_inodes_from_logical(entry->logical, fs_info, &path,
+ print_filenames, fs_info);
+ if (ret < 0) {
+ errno = -ret;
+ error("failed to iterate involved files: %m");
+ break;
+ }
}
}
@@ -318,7 +371,7 @@ int btrfs_recover_fix_data_csums(const char *path, enum btrfs_fix_data_csums_mod
errno = -ret;
error("failed to iterate csum tree: %m");
}
- report_corrupted_blocks();
+ report_corrupted_blocks(fs_info);
out_close:
free_corrupted_blocks();
close_ctree_fs_info(fs_info);
--
2.49.0
^ permalink raw reply related [flat|nested] 4+ messages in thread