diff mbox series

btrfs: handle tree backref walk error properly

Message ID 7d7e05a0148476dbf7cd8f076de1c076da68948e.1683792063.git.wqu@suse.com (mailing list archive)
State New, archived
Headers show
Series btrfs: handle tree backref walk error properly | expand

Commit Message

Qu Wenruo May 11, 2023, 8:01 a.m. UTC
[BUG]
Smatch reports the following errors related to commit 3093caaa4d16
("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>
---
 fs/btrfs/inode.c | 16 ++++++++++++----
 fs/btrfs/scrub.c | 28 +++++++++++++++++-----------
 2 files changed, 29 insertions(+), 15 deletions(-)

Comments

David Sterba May 11, 2023, noon UTC | #1
On Thu, May 11, 2023 at 04:01:44PM +0800, Qu Wenruo wrote:
> [BUG]
> Smatch reports the following errors related to commit 3093caaa4d16
> ("btrfs: output affected files when relocation fails"):

This patch is still in misc-next so the commit id is unstable so I could
update it in place but you're also fixing the original code and the
change description applies to both I'll apply the patch separately.
Thanks.
diff mbox series

Patch

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 4e65b2376cf3..ff449dfd9215 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 };
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index 79a2a239e3f4..44005880519f 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -473,11 +473,8 @@  static void scrub_print_common_warning(const char *errstr, struct btrfs_device *
 	struct extent_buffer *eb;
 	struct btrfs_extent_item *ei;
 	struct scrub_warning swarn;
-	unsigned long ptr = 0;
 	u64 flags = 0;
-	u64 ref_root;
 	u32 item_size;
-	u8 ref_level = 0;
 	int ret;
 
 	/* Super block error, no need to search extent tree. */
@@ -507,19 +504,28 @@  static void scrub_print_common_warning(const char *errstr, struct btrfs_device *
 	item_size = btrfs_item_size(eb, path->slots[0]);
 
 	if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
-		do {
+		unsigned long ptr = 0;
+		u8 ref_level;
+		u64 ref_root;
+
+		while (true) {
 			ret = tree_backref_for_extent(&ptr, eb, &found_key, ei,
 						      item_size, &ref_root,
 						      &ref_level);
+			if (ret < 0) {
+				btrfs_warn(fs_info,
+			"failed to resolve tree backref for logical %llu: %d",
+						  swarn.logical, ret);
+				break;
+			}
+			if (ret > 0)
+				break;
 			btrfs_warn_in_rcu(fs_info,
 "%s at logical %llu on dev %s, physical %llu: metadata %s (level %d) in tree %llu",
-				errstr, swarn.logical,
-				btrfs_dev_name(dev),
-				swarn.physical,
-				ref_level ? "node" : "leaf",
-				ret < 0 ? -1 : ref_level,
-				ret < 0 ? -1 : ref_root);
-		} while (ret != 1);
+				errstr, swarn.logical, btrfs_dev_name(dev),
+				swarn.physical, ref_level ? "node" : "leaf",
+				ref_level, ref_root);
+		}
 		btrfs_release_path(path);
 	} else {
 		struct btrfs_backref_walk_ctx ctx = { 0 };