mirror of
https://gitee.com/bianbu-linux/linux-6.6
synced 2025-04-24 14:07:52 -04:00
btrfs: tree-checker: validate dref root and objectid
[ Upstream commit f333a3c7e8323499aa65038e77fe8f3199d4e283 ] [CORRUPTION] There is a bug report that btrfs flips RO due to a corruption in the extent tree, the involved dumps looks like this: item 188 key (402811572224 168 4096) itemoff 14598 itemsize 79 extent refs 3 gen 3678544 flags 1 ref#0: extent data backref root 13835058055282163977 objectid 281473384125923 offset 81432576 count 1 ref#1: shared data backref parent 1947073626112 count 1 ref#2: shared data backref parent 1156030103552 count 1 BTRFS critical (device vdc1: state EA): unable to find ref byte nr 402811572224 parent 0 root 265 owner 28703026 offset 81432576 slot 189 BTRFS error (device vdc1: state EA): failed to run delayed ref for logical 402811572224 num_bytes 4096 type 178 action 2 ref_mod 1: -2 [CAUSE] The corrupted entry is ref#0 of item 188. The root number 13835058055282163977 is beyond the upper limit for root items (the current limit is 1 << 48), and the objectid also looks suspicious. Only the offset and count is correct. [ENHANCEMENT] Although it's still unknown why we have such many bytes corrupted randomly, we can still enhance the tree-checker for data backrefs by: - Validate the root value For now there should only be 3 types of roots can have data backref: * subvolume trees * data reloc trees * root tree Only for v1 space cache - validate the objectid value The objectid should be a valid inode number. Hopefully we can catch such problem in the future with the new checkers. Reported-by: Kai Krakow <hurikhan77@gmail.com> Link: https://lore.kernel.org/linux-btrfs/CAMthOuPjg5RDT-G_LXeBBUUtzt3cq=JywF+D1_h+JYxe=WKp-Q@mail.gmail.com/#t Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
02c19d769d
commit
9b090ccd80
1 changed files with 47 additions and 0 deletions
|
@ -1266,6 +1266,19 @@ static void extent_err(const struct extent_buffer *eb, int slot,
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_valid_dref_root(u64 rootid)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The following tree root objectids are allowed to have a data backref:
|
||||||
|
* - subvolume trees
|
||||||
|
* - data reloc tree
|
||||||
|
* - tree root
|
||||||
|
* For v1 space cache
|
||||||
|
*/
|
||||||
|
return is_fstree(rootid) || rootid == BTRFS_DATA_RELOC_TREE_OBJECTID ||
|
||||||
|
rootid == BTRFS_ROOT_TREE_OBJECTID;
|
||||||
|
}
|
||||||
|
|
||||||
static int check_extent_item(struct extent_buffer *leaf,
|
static int check_extent_item(struct extent_buffer *leaf,
|
||||||
struct btrfs_key *key, int slot,
|
struct btrfs_key *key, int slot,
|
||||||
struct btrfs_key *prev_key)
|
struct btrfs_key *prev_key)
|
||||||
|
@ -1418,6 +1431,8 @@ static int check_extent_item(struct extent_buffer *leaf,
|
||||||
struct btrfs_extent_data_ref *dref;
|
struct btrfs_extent_data_ref *dref;
|
||||||
struct btrfs_shared_data_ref *sref;
|
struct btrfs_shared_data_ref *sref;
|
||||||
u64 seq;
|
u64 seq;
|
||||||
|
u64 dref_root;
|
||||||
|
u64 dref_objectid;
|
||||||
u64 dref_offset;
|
u64 dref_offset;
|
||||||
u64 inline_offset;
|
u64 inline_offset;
|
||||||
u8 inline_type;
|
u8 inline_type;
|
||||||
|
@ -1461,11 +1476,26 @@ static int check_extent_item(struct extent_buffer *leaf,
|
||||||
*/
|
*/
|
||||||
case BTRFS_EXTENT_DATA_REF_KEY:
|
case BTRFS_EXTENT_DATA_REF_KEY:
|
||||||
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
|
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
|
||||||
|
dref_root = btrfs_extent_data_ref_root(leaf, dref);
|
||||||
|
dref_objectid = btrfs_extent_data_ref_objectid(leaf, dref);
|
||||||
dref_offset = btrfs_extent_data_ref_offset(leaf, dref);
|
dref_offset = btrfs_extent_data_ref_offset(leaf, dref);
|
||||||
seq = hash_extent_data_ref(
|
seq = hash_extent_data_ref(
|
||||||
btrfs_extent_data_ref_root(leaf, dref),
|
btrfs_extent_data_ref_root(leaf, dref),
|
||||||
btrfs_extent_data_ref_objectid(leaf, dref),
|
btrfs_extent_data_ref_objectid(leaf, dref),
|
||||||
btrfs_extent_data_ref_offset(leaf, dref));
|
btrfs_extent_data_ref_offset(leaf, dref));
|
||||||
|
if (unlikely(!is_valid_dref_root(dref_root))) {
|
||||||
|
extent_err(leaf, slot,
|
||||||
|
"invalid data ref root value %llu",
|
||||||
|
dref_root);
|
||||||
|
return -EUCLEAN;
|
||||||
|
}
|
||||||
|
if (unlikely(dref_objectid < BTRFS_FIRST_FREE_OBJECTID ||
|
||||||
|
dref_objectid > BTRFS_LAST_FREE_OBJECTID)) {
|
||||||
|
extent_err(leaf, slot,
|
||||||
|
"invalid data ref objectid value %llu",
|
||||||
|
dref_root);
|
||||||
|
return -EUCLEAN;
|
||||||
|
}
|
||||||
if (unlikely(!IS_ALIGNED(dref_offset,
|
if (unlikely(!IS_ALIGNED(dref_offset,
|
||||||
fs_info->sectorsize))) {
|
fs_info->sectorsize))) {
|
||||||
extent_err(leaf, slot,
|
extent_err(leaf, slot,
|
||||||
|
@ -1601,6 +1631,8 @@ static int check_extent_data_ref(struct extent_buffer *leaf,
|
||||||
return -EUCLEAN;
|
return -EUCLEAN;
|
||||||
}
|
}
|
||||||
for (; ptr < end; ptr += sizeof(*dref)) {
|
for (; ptr < end; ptr += sizeof(*dref)) {
|
||||||
|
u64 root;
|
||||||
|
u64 objectid;
|
||||||
u64 offset;
|
u64 offset;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1608,7 +1640,22 @@ static int check_extent_data_ref(struct extent_buffer *leaf,
|
||||||
* overflow from the leaf due to hash collisions.
|
* overflow from the leaf due to hash collisions.
|
||||||
*/
|
*/
|
||||||
dref = (struct btrfs_extent_data_ref *)ptr;
|
dref = (struct btrfs_extent_data_ref *)ptr;
|
||||||
|
root = btrfs_extent_data_ref_root(leaf, dref);
|
||||||
|
objectid = btrfs_extent_data_ref_objectid(leaf, dref);
|
||||||
offset = btrfs_extent_data_ref_offset(leaf, dref);
|
offset = btrfs_extent_data_ref_offset(leaf, dref);
|
||||||
|
if (unlikely(!is_valid_dref_root(root))) {
|
||||||
|
extent_err(leaf, slot,
|
||||||
|
"invalid extent data backref root value %llu",
|
||||||
|
root);
|
||||||
|
return -EUCLEAN;
|
||||||
|
}
|
||||||
|
if (unlikely(objectid < BTRFS_FIRST_FREE_OBJECTID ||
|
||||||
|
objectid > BTRFS_LAST_FREE_OBJECTID)) {
|
||||||
|
extent_err(leaf, slot,
|
||||||
|
"invalid extent data backref objectid value %llu",
|
||||||
|
root);
|
||||||
|
return -EUCLEAN;
|
||||||
|
}
|
||||||
if (unlikely(!IS_ALIGNED(offset, leaf->fs_info->sectorsize))) {
|
if (unlikely(!IS_ALIGNED(offset, leaf->fs_info->sectorsize))) {
|
||||||
extent_err(leaf, slot,
|
extent_err(leaf, slot,
|
||||||
"invalid extent data backref offset, have %llu expect aligned to %u",
|
"invalid extent data backref offset, have %llu expect aligned to %u",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue