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: Qu Wenruo <w...@suse.com>
---
 fs/btrfs/ctree.h        |  1 +
 fs/btrfs/extent-tree.c  |  2 +-
 fs/btrfs/tree-checker.c | 48 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 0a61dff27f57..710ea3a6608c 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2679,6 +2679,7 @@ enum btrfs_inline_ref_type {
 int btrfs_get_extent_inline_ref_type(const struct extent_buffer *eb,
                                     struct btrfs_extent_inline_ref *iref,
                                     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);
 
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index b4e9e36b65f1..c0888ed503df 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -1114,7 +1114,7 @@ int btrfs_get_extent_inline_ref_type(const struct 
extent_buffer *eb,
        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 low_crc = ~(u32)0;
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index 6ef174c7fb05..115725f742c2 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -1177,6 +1177,51 @@ static int check_simple_keyed_refs(struct extent_buffer 
*leaf,
        return 0;
 }
 
+static int check_extent_data_ref(struct extent_buffer *leaf,
+                                struct btrfs_key *key, int slot)
+{
+       struct btrfs_extent_data_ref *dref;
+       u64 ptr = btrfs_item_ptr_offset(leaf, slot);
+       u64 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 %lu 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 have 0x%016llx key have 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.
  */
@@ -1224,6 +1269,9 @@ static int check_leaf_item(struct extent_buffer *leaf,
        case BTRFS_SHARED_BLOCK_REF_KEY:
                ret = check_simple_keyed_refs(leaf, key, slot);
                break;
+       case BTRFS_EXTENT_DATA_REF_KEY:
+               ret = check_extent_data_ref(leaf, key, slot);
+               break;
        }
        return ret;
 }
-- 
2.22.0

Reply via email to