btrfs-progs: check: add check for metadata level mismatch

For a skinny metadata item in the extent tree, the key offset represents
the level of the tree it points to. This adds a check that these values
match, as otherwise it can cause a volume to go readonly when deleting a
large number of inodes.

See https://github.com/maharmstone/ntfs2btrfs/issues/51

Pull-request: #623
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Mark Harmstone <mark@harmstone.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Mark Harmstone 2023-04-30 22:45:31 +01:00 committed by David Sterba
parent b390c276c2
commit d8317a445c
2 changed files with 18 additions and 2 deletions

View File

@ -4195,7 +4195,8 @@ static int maybe_free_extent_rec(struct cache_tree *extent_cache,
rec->num_duplicates == 0 && !all_backpointers_checked(rec, 0) &&
!rec->bad_full_backref && !rec->crossing_stripes &&
rec->generation <= super_gen + 1 &&
!rec->wrong_chunk_type) {
!rec->wrong_chunk_type &&
(!rec->metadata || rec->info_level == rec->level)) {
remove_cache_extent(extent_cache, &rec->cache);
free_all_extent_backrefs(rec);
list_del_init(&rec->list);
@ -4767,6 +4768,7 @@ static int add_extent_rec_nolookup(struct cache_tree *extent_cache,
rec->extent_item_refs = tmpl->extent_item_refs;
rec->parent_generation = tmpl->parent_generation;
rec->generation = tmpl->generation;
rec->level = tmpl->level;
INIT_LIST_HEAD(&rec->backrefs);
INIT_LIST_HEAD(&rec->dups);
INIT_LIST_HEAD(&rec->list);
@ -4848,6 +4850,7 @@ static int add_extent_rec(struct cache_tree *extent_cache,
rec->num_duplicates++;
} else {
rec->nr = tmpl->nr;
rec->level = tmpl->level;
rec->found_rec = 1;
}
}
@ -5592,6 +5595,8 @@ static int process_extent_item(struct btrfs_root *root,
if (metadata)
btrfs_check_subpage_eb_alignment(gfs_info, key.objectid, num_bytes);
memset(&tmpl, 0, sizeof(tmpl));
ptr = (unsigned long)(ei + 1);
if (metadata) {
u64 level;
@ -5610,8 +5615,10 @@ static int process_extent_item(struct btrfs_root *root,
key.objectid, level, BTRFS_MAX_LEVEL - 1);
return -EUCLEAN;
}
tmpl.level = (u8)level;
}
memset(&tmpl, 0, sizeof(tmpl));
tmpl.start = key.objectid;
tmpl.nr = num_bytes;
tmpl.extent_item_refs = refs;
@ -8144,6 +8151,14 @@ static int check_extent_refs(struct btrfs_root *root,
cur_err = 1;
}
}
if (rec->metadata && rec->level != rec->info_level) {
fprintf(stderr,
"metadata level mismatch on [%llu, %llu]\n",
rec->start, rec->nr);
cur_err = 1;
}
if (rec->refs != rec->extent_item_refs) {
fprintf(stderr, "ref mismatch on [%llu %llu] ",
(unsigned long long)rec->start,

View File

@ -90,6 +90,7 @@ struct extent_record {
u64 info_objectid;
u32 num_duplicates;
u8 info_level;
u8 level;
unsigned int flag_block_full_backref:2;
unsigned int found_rec:1;
unsigned int content_checked:1;