btrfs-progs: check: orig: reject bad metadata backref with invalid level

[BUG]
There is a bug report that kernel tree-checker rejected an invalid
metadata item:

 corrupt leaf: block=934474399744 slot=68 extent bytenr=425173254144 len=16384 invalid tree level, have 33554432 expect [0, 7]

But original mode btrfs-check reports nothing wrong.
(lowmem mode will just crash, and fixed in previous patch).

[CAUSE]
For original mode it doesn't really check tree level, thus didn't find
the problem.

[FIX]
I don't have a good idea to completely make original mode to verify the
level in backref and in the tree block (while lowmem does that).

But at least we can detect obviously corrupted level just like kernel.

Now original mode will detect such problem:

 ...
 [2/7] checking extents
 ERROR: tree block 30457856 has bad backref level, has 256 expect [0, 7]
 ref mismatch on [30457856 16384] extent item 0, found 1
 tree backref 30457856 root 5 not found in extent tree
 backpointer mismatch on [30457856 16384]
 ERROR: errors found in extent allocation tree or chunk allocation
 [3/7] checking free space tree
 ...

Reported-by: Stickstoff <stickstoff@posteo.de>
Link: https://lore.kernel.org/linux-btrfs/6ed4cd5a-7430-f326-4056-25ae7eb44416@posteo.de/
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Qu Wenruo 2022-01-17 10:38:49 +08:00 committed by David Sterba
parent bf21c82675
commit 1a8f283c97
1 changed files with 19 additions and 0 deletions

View File

@ -5447,6 +5447,25 @@ static int process_extent_item(struct btrfs_root *root,
if (metadata)
btrfs_check_subpage_eb_alignment(gfs_info, key.objectid, num_bytes);
ptr = (unsigned long)(ei + 1);
if (metadata) {
u64 level;
if (key.type == BTRFS_EXTENT_ITEM_KEY) {
struct btrfs_tree_block_info *info;
info = (struct btrfs_tree_block_info *)ptr;
level = btrfs_tree_block_level(eb, info);
} else {
level = key.offset;
}
if (level >= BTRFS_MAX_LEVEL) {
error(
"tree block %llu has bad backref level, has %llu expect [0, %u]",
key.objectid, level, BTRFS_MAX_LEVEL - 1);
return -EUCLEAN;
}
}
memset(&tmpl, 0, sizeof(tmpl));
tmpl.start = key.objectid;
tmpl.nr = num_bytes;