btrfs: tree-checker: Add EXTENT_DATA_REF check
authorQu Wenruo <wqu@suse.com>
Fri, 9 Aug 2019 01:24:24 +0000 (09:24 +0800)
committerDavid Sterba <dsterba@suse.com>
Mon, 9 Sep 2019 12:59:12 +0000 (14:59 +0200)
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 <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/ctree.h
fs/btrfs/extent-tree.c
fs/btrfs/tree-checker.c

index b161224..2079374 100644 (file)
@@ -2447,6 +2447,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);
 
index cd21055..5e8c6a0 100644 (file)
@@ -438,7 +438,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;
index 0a56616..9645389 100644 (file)
@@ -1187,6 +1187,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;
+       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.
  */
@@ -1234,6 +1279,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;
 }