diff options
author | Qu Wenruo <wqu@suse.com> | 2023-05-11 16:01:44 +0800 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2023-06-19 13:59:26 +0200 |
commit | b7f9945a1479a97a7aa2cc2b9d36e72f33fcb174 (patch) | |
tree | 0b166135a579b3e412290e0407335f13feb11393 /fs/btrfs/inode.c | |
parent | f880fe6e0b4b127d6e2a007fe68bdf5651677ae2 (diff) | |
download | linux-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.c | 16 |
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 }; |