summaryrefslogtreecommitdiffstats
path: root/fs/btrfs/inode.c
diff options
context:
space:
mode:
authorQu Wenruo <wqu@suse.com>2023-05-11 16:01:44 +0800
committerDavid Sterba <dsterba@suse.com>2023-06-19 13:59:26 +0200
commitb7f9945a1479a97a7aa2cc2b9d36e72f33fcb174 (patch)
tree0b166135a579b3e412290e0407335f13feb11393 /fs/btrfs/inode.c
parentf880fe6e0b4b127d6e2a007fe68bdf5651677ae2 (diff)
downloadlinux-stable-b7f9945a1479a97a7aa2cc2b9d36e72f33fcb174.tar.gz
linux-stable-b7f9945a1479a97a7aa2cc2b9d36e72f33fcb174.tar.bz2
linux-stable-b7f9945a1479a97a7aa2cc2b9d36e72f33fcb174.zip
btrfs: handle tree backref walk error properly
[BUG] Smatch reports the following errors related to commit ("btrfs: output affected files when relocation fails"): fs/btrfs/inode.c:283 print_data_reloc_error() error: uninitialized symbol 'ref_level'. [CAUSE] That part of code is mostly copied from scrub, but unfortunately scrub code from the beginning is not doing the error handling properly. The offending code looks like this: do { ret = tree_backref_for_extent(); btrfs_warn_rl(); } while (ret != 1); There are several problems involved: - No error handling If that tree_backref_for_extent() failed, we would output the same error again and again, never really exit as it requires ret == 1 to exit. - Always do one extra output As tree_backref_for_extent() only return > 0 if there is no more backref item. This means after the last item we hit, we would output an invalid error message for ret > 0 case. [FIX] Fix the old code by: - Move @ref_root and @ref_level into the if branch And do not initialize them, so we can catch such uninitialized values just like what we do in the inode.c - Explicitly check the return value of tree_backref_for_extent() And handle ret < 0 and ret > 0 cases properly. - No more do {} while () loop Instead go while (true) {} loop since we will handle @ret manually. Reported-by: Dan Carpenter <dan.carpenter@linaro.org> Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'fs/btrfs/inode.c')
-rw-r--r--fs/btrfs/inode.c16
1 files changed, 12 insertions, 4 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index da7e5bf2f6db..333736712bd9 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -276,17 +276,25 @@ static void print_data_reloc_error(const struct btrfs_inode *inode, u64 file_off
u64 ref_root;
u8 ref_level;
- do {
+ while (true) {
ret = tree_backref_for_extent(&ptr, eb, &found_key, ei,
item_size, &ref_root,
&ref_level);
+ if (ret < 0) {
+ btrfs_warn_rl(fs_info,
+ "failed to resolve tree backref for logical %llu: %d",
+ logical, ret);
+ break;
+ }
+ if (ret > 0)
+ break;
+
btrfs_warn_rl(fs_info,
"csum error at logical %llu mirror %u: metadata %s (level %d) in tree %llu",
logical, mirror_num,
(ref_level ? "node" : "leaf"),
- (ret < 0 ? -1 : ref_level),
- (ret < 0 ? -1 : ref_root));
- } while (ret != 1);
+ ref_level, ref_root);
+ }
btrfs_release_path(&path);
} else {
struct btrfs_backref_walk_ctx ctx = { 0 };