btrfs-progs: check: introduce function to check data backref in extent tree

Introduce a new function check_data_extent_item() to check if the
corresponding data backref exists in extent tree.

Signed-off-by: Lu Fengqi <lufq.fnst@cn.fujitsu.com>
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Reviewed-by: Josef Bacik <jbacik@fb.com>
Signed-off-by: David Sterba <dsterba@suse.com>
master
Lu Fengqi 2016-04-23 15:26:25 +08:00 committed by David Sterba
parent ae60006cee
commit b0d360b541
3 changed files with 159 additions and 1 deletions

View File

@ -442,6 +442,7 @@ struct root_item_info {
*/
#define BACKREF_MISSING (1 << 0) /* Backref missing in extent tree */
#define BACKREF_MISMATCH (1 << 1) /* Backref exists but does not match */
#define BYTES_UNALIGNED (1 << 2) /* Some bytes are not aligned */
static void *print_status_check(void *p)
{
@ -8704,6 +8705,161 @@ out:
return err;
}
/*
* Check EXTENT_DATA item, mainly for its dbackref in extent tree
*
* Return >0 any error found and output error message
* Return 0 for no error found
*/
static int check_extent_data_item(struct btrfs_root *root,
struct extent_buffer *eb, int slot)
{
struct btrfs_file_extent_item *fi;
struct btrfs_path path;
struct btrfs_root *extent_root = root->fs_info->extent_root;
struct btrfs_key fi_key;
struct btrfs_key dbref_key;
struct extent_buffer *leaf;
struct btrfs_extent_item *ei;
struct btrfs_extent_inline_ref *iref;
struct btrfs_extent_data_ref *dref;
u64 owner;
u64 file_extent_gen;
u64 disk_bytenr;
u64 disk_num_bytes;
u64 extent_num_bytes;
u64 extent_flags;
u64 extent_gen;
u32 item_size;
unsigned long end;
unsigned long ptr;
int type;
u64 ref_root;
int found_dbackref = 0;
int err = 0;
int ret;
btrfs_item_key_to_cpu(eb, &fi_key, slot);
fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
file_extent_gen = btrfs_file_extent_generation(eb, fi);
/* Nothing to check for hole and inline data extents */
if (btrfs_file_extent_type(eb, fi) == BTRFS_FILE_EXTENT_INLINE ||
btrfs_file_extent_disk_bytenr(eb, fi) == 0)
return 0;
disk_bytenr = btrfs_file_extent_disk_bytenr(eb, fi);
disk_num_bytes = btrfs_file_extent_disk_num_bytes(eb, fi);
extent_num_bytes = btrfs_file_extent_num_bytes(eb, fi);
/* Check unaligned disk_num_bytes and num_bytes */
if (!IS_ALIGNED(disk_num_bytes, root->sectorsize)) {
error(
"file extent [%llu, %llu] has unaligned disk num bytes: %llu, should be aligned to %u",
fi_key.objectid, fi_key.offset, disk_num_bytes,
root->sectorsize);
err |= BYTES_UNALIGNED;
} else {
data_bytes_allocated += disk_num_bytes;
}
if (!IS_ALIGNED(extent_num_bytes, root->sectorsize)) {
error(
"file extent [%llu, %llu] has unaligned num bytes: %llu, should be aligned to %u",
fi_key.objectid, fi_key.offset, extent_num_bytes,
root->sectorsize);
err |= BYTES_UNALIGNED;
} else {
data_bytes_referenced += extent_num_bytes;
}
owner = btrfs_header_owner(eb);
/* Check the extent item of the file extent in extent tree */
btrfs_init_path(&path);
dbref_key.objectid = btrfs_file_extent_disk_bytenr(eb, fi);
dbref_key.type = BTRFS_EXTENT_ITEM_KEY;
dbref_key.offset = btrfs_file_extent_disk_num_bytes(eb, fi);
ret = btrfs_search_slot(NULL, extent_root, &dbref_key, &path, 0, 0);
if (ret) {
err |= BACKREF_MISSING;
goto error;
}
leaf = path.nodes[0];
slot = path.slots[0];
ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
extent_flags = btrfs_extent_flags(leaf, ei);
extent_gen = btrfs_extent_generation(leaf, ei);
if (!(extent_flags & BTRFS_EXTENT_FLAG_DATA)) {
error(
"extent[%llu %llu] backref type mismatch, wanted bit: %llx",
disk_bytenr, disk_num_bytes,
BTRFS_EXTENT_FLAG_DATA);
err |= BACKREF_MISMATCH;
}
if (file_extent_gen < extent_gen) {
error(
"extent[%llu %llu] backref generation mismatch, wanted: <=%llu, have: %llu",
disk_bytenr, disk_num_bytes, file_extent_gen,
extent_gen);
err |= BACKREF_MISMATCH;
}
/* Check data backref inside that extent item */
item_size = btrfs_item_size_nr(leaf, path.slots[0]);
iref = (struct btrfs_extent_inline_ref *)(ei + 1);
ptr = (unsigned long)iref;
end = (unsigned long)ei + item_size;
while (ptr < end) {
iref = (struct btrfs_extent_inline_ref *)ptr;
type = btrfs_extent_inline_ref_type(leaf, iref);
dref = (struct btrfs_extent_data_ref *)(&iref->offset);
if (type == BTRFS_EXTENT_DATA_REF_KEY) {
ref_root = btrfs_extent_data_ref_root(leaf, dref);
if (ref_root == owner || ref_root == root->objectid)
found_dbackref = 1;
} else if (type == BTRFS_SHARED_DATA_REF_KEY) {
found_dbackref = !check_tree_block_ref(root, NULL,
btrfs_extent_inline_ref_offset(leaf, iref),
0, owner);
}
if (found_dbackref)
break;
ptr += btrfs_extent_inline_ref_size(type);
}
/* Didn't found inlined data backref, try EXTENT_DATA_REF_KEY */
if (!found_dbackref) {
btrfs_release_path(&path);
btrfs_init_path(&path);
dbref_key.objectid = btrfs_file_extent_disk_bytenr(eb, fi);
dbref_key.type = BTRFS_EXTENT_DATA_REF_KEY;
dbref_key.offset = hash_extent_data_ref(root->objectid,
fi_key.objectid, fi_key.offset);
ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
&dbref_key, &path, 0, 0);
if (!ret)
found_dbackref = 1;
}
if (!found_dbackref)
err |= BACKREF_MISSING;
error:
btrfs_release_path(&path);
if (err & BACKREF_MISSING) {
error("data extent[%llu %llu] backref lost",
disk_bytenr, disk_num_bytes);
}
return err;
}
static int btrfs_fsck_reinit_root(struct btrfs_trans_handle *trans,
struct btrfs_root *root, int overwrite)
{

View File

@ -2364,6 +2364,8 @@ int exclude_super_stripes(struct btrfs_root *root,
struct btrfs_block_group_cache *cache);
u64 add_new_free_space(struct btrfs_block_group_cache *block_group,
struct btrfs_fs_info *info, u64 start, u64 end);
u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset);
/* ctree.c */
int btrfs_comp_cpu_keys(struct btrfs_key *k1, struct btrfs_key *k2);
int btrfs_del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root,

View File

@ -598,7 +598,7 @@ static int convert_extent_item_v0(struct btrfs_trans_handle *trans,
}
#endif
static u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset)
u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset)
{
u32 high_crc = ~(u32)0;
u32 low_crc = ~(u32)0;