btrfs: tree-checker: Add check for INODE_REF
authorQu Wenruo <wqu@suse.com>
Mon, 26 Aug 2019 07:40:39 +0000 (15:40 +0800)
committerDavid Sterba <dsterba@suse.com>
Mon, 18 Nov 2019 11:46:46 +0000 (12:46 +0100)
For INODE_REF we will check:
- Objectid (ino) against previous key
  To detect missing INODE_ITEM.

- No overflow/padding in the data payload
  Much like DIR_ITEM, but with less members to 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/tree-checker.c

index 5aca82e67309d4b84af5b2c2d590fbf867e0a48a..f041ec5f415c41de51c126aa6466f4f86d8ce016 100644 (file)
@@ -1247,6 +1247,56 @@ static int check_extent_data_ref(struct extent_buffer *leaf,
        return 0;
 }
 
+#define inode_ref_err(fs_info, eb, slot, fmt, args...)                 \
+       inode_item_err(fs_info, eb, slot, fmt, ##args)
+static int check_inode_ref(struct extent_buffer *leaf,
+                          struct btrfs_key *key, struct btrfs_key *prev_key,
+                          int slot)
+{
+       struct btrfs_inode_ref *iref;
+       unsigned long ptr;
+       unsigned long end;
+
+       /* namelen can't be 0, so item_size == sizeof() is also invalid */
+       if (btrfs_item_size_nr(leaf, slot) <= sizeof(*iref)) {
+               inode_ref_err(fs_info, leaf, slot,
+                       "invalid item size, have %u expect (%zu, %u)",
+                       btrfs_item_size_nr(leaf, slot),
+                       sizeof(*iref), BTRFS_LEAF_DATA_SIZE(leaf->fs_info));
+               return -EUCLEAN;
+       }
+
+       ptr = btrfs_item_ptr_offset(leaf, slot);
+       end = ptr + btrfs_item_size_nr(leaf, slot);
+       while (ptr < end) {
+               u16 namelen;
+
+               if (ptr + sizeof(iref) > end) {
+                       inode_ref_err(fs_info, leaf, slot,
+                       "inode ref overflow, ptr %lu end %lu inode_ref_size %zu",
+                               ptr, end, sizeof(iref));
+                       return -EUCLEAN;
+               }
+
+               iref = (struct btrfs_inode_ref *)ptr;
+               namelen = btrfs_inode_ref_name_len(leaf, iref);
+               if (ptr + sizeof(*iref) + namelen > end) {
+                       inode_ref_err(fs_info, leaf, slot,
+                               "inode ref overflow, ptr %lu end %lu namelen %u",
+                               ptr, end, namelen);
+                       return -EUCLEAN;
+               }
+
+               /*
+                * NOTE: In theory we should record all found index numbers
+                * to find any duplicated indexes, but that will be too time
+                * consuming for inodes with too many hard links.
+                */
+               ptr += sizeof(*iref) + namelen;
+       }
+       return 0;
+}
+
 /*
  * Common point to switch the item-specific validation.
  */
@@ -1269,6 +1319,9 @@ static int check_leaf_item(struct extent_buffer *leaf,
        case BTRFS_XATTR_ITEM_KEY:
                ret = check_dir_item(leaf, key, prev_key, slot);
                break;
+       case BTRFS_INODE_REF_KEY:
+               ret = check_inode_ref(leaf, key, prev_key, slot);
+               break;
        case BTRFS_BLOCK_GROUP_ITEM_KEY:
                ret = check_block_group_item(leaf, key, slot);
                break;