Commit 0785a9aa authored by Qu Wenruo's avatar Qu Wenruo Committed by David Sterba

btrfs: tree-checker: Add EXTENT_DATA_REF check

EXTENT_DATA_REF is a little like DIR_ITEM which contains hash in its
key->offset.

This patch will check the following contents:
- Key->objectid
  Basic alignment check.

- Hash
  Hash of each extent_data_ref item must match key->offset.

- Offset
  Basic alignment check.
Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent e2406a6f
...@@ -2447,6 +2447,7 @@ enum btrfs_inline_ref_type { ...@@ -2447,6 +2447,7 @@ enum btrfs_inline_ref_type {
int btrfs_get_extent_inline_ref_type(const struct extent_buffer *eb, int btrfs_get_extent_inline_ref_type(const struct extent_buffer *eb,
struct btrfs_extent_inline_ref *iref, struct btrfs_extent_inline_ref *iref,
enum btrfs_inline_ref_type is_data); enum btrfs_inline_ref_type is_data);
u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset);
u64 btrfs_csum_bytes_to_leaves(struct btrfs_fs_info *fs_info, u64 csum_bytes); u64 btrfs_csum_bytes_to_leaves(struct btrfs_fs_info *fs_info, u64 csum_bytes);
......
...@@ -438,7 +438,7 @@ int btrfs_get_extent_inline_ref_type(const struct extent_buffer *eb, ...@@ -438,7 +438,7 @@ int btrfs_get_extent_inline_ref_type(const struct extent_buffer *eb,
return BTRFS_REF_TYPE_INVALID; return BTRFS_REF_TYPE_INVALID;
} }
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 high_crc = ~(u32)0;
u32 low_crc = ~(u32)0; u32 low_crc = ~(u32)0;
......
...@@ -1187,6 +1187,51 @@ static int check_simple_keyed_refs(struct extent_buffer *leaf, ...@@ -1187,6 +1187,51 @@ static int check_simple_keyed_refs(struct extent_buffer *leaf,
return 0; return 0;
} }
static int check_extent_data_ref(struct extent_buffer *leaf,
struct btrfs_key *key, int slot)
{
struct btrfs_extent_data_ref *dref;
unsigned long ptr = btrfs_item_ptr_offset(leaf, slot);
const unsigned long end = ptr + btrfs_item_size_nr(leaf, slot);
if (btrfs_item_size_nr(leaf, slot) % sizeof(*dref) != 0) {
generic_err(leaf, slot,
"invalid item size, have %u expect aligned to %zu for key type %u",
btrfs_item_size_nr(leaf, slot),
sizeof(*dref), key->type);
}
if (!IS_ALIGNED(key->objectid, leaf->fs_info->sectorsize)) {
generic_err(leaf, slot,
"invalid key objectid for shared block ref, have %llu expect aligned to %u",
key->objectid, leaf->fs_info->sectorsize);
return -EUCLEAN;
}
for (; ptr < end; ptr += sizeof(*dref)) {
u64 root_objectid;
u64 owner;
u64 offset;
u64 hash;
dref = (struct btrfs_extent_data_ref *)ptr;
root_objectid = btrfs_extent_data_ref_root(leaf, dref);
owner = btrfs_extent_data_ref_objectid(leaf, dref);
offset = btrfs_extent_data_ref_offset(leaf, dref);
hash = hash_extent_data_ref(root_objectid, owner, offset);
if (hash != key->offset) {
extent_err(leaf, slot,
"invalid extent data ref hash, item has 0x%016llx key has 0x%016llx",
hash, key->offset);
return -EUCLEAN;
}
if (!IS_ALIGNED(offset, leaf->fs_info->sectorsize)) {
extent_err(leaf, slot,
"invalid extent data backref offset, have %llu expect aligned to %u",
offset, leaf->fs_info->sectorsize);
}
}
return 0;
}
/* /*
* Common point to switch the item-specific validation. * Common point to switch the item-specific validation.
*/ */
...@@ -1234,6 +1279,9 @@ static int check_leaf_item(struct extent_buffer *leaf, ...@@ -1234,6 +1279,9 @@ static int check_leaf_item(struct extent_buffer *leaf,
case BTRFS_SHARED_BLOCK_REF_KEY: case BTRFS_SHARED_BLOCK_REF_KEY:
ret = check_simple_keyed_refs(leaf, key, slot); ret = check_simple_keyed_refs(leaf, key, slot);
break; break;
case BTRFS_EXTENT_DATA_REF_KEY:
ret = check_extent_data_ref(leaf, key, slot);
break;
} }
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment