btrfs: tree-checker: get fs_info from eb in check_extent_data_item
[sfrench/cifs-2.6.git] / fs / btrfs / tree-checker.c
index a62e1e837a89642301d119275ecea126a5842eec..c5dd7adea306746c52900e091b4bb52f9bee349c 100644 (file)
  * Append generic "corrupt leaf/node root=%llu block=%llu slot=%d: " to @fmt.
  * Allows callers to customize the output.
  */
-__printf(4, 5)
+__printf(3, 4)
 __cold
-static void generic_err(const struct btrfs_fs_info *fs_info,
-                       const struct extent_buffer *eb, int slot,
+static void generic_err(const struct extent_buffer *eb, int slot,
                        const char *fmt, ...)
 {
+       const struct btrfs_fs_info *fs_info = eb->fs_info;
        struct va_format vaf;
        va_list args;
 
@@ -66,12 +66,12 @@ static void generic_err(const struct btrfs_fs_info *fs_info,
  * Customized reporter for extent data item, since its key objectid and
  * offset has its own meaning.
  */
-__printf(4, 5)
+__printf(3, 4)
 __cold
-static void file_extent_err(const struct btrfs_fs_info *fs_info,
-                           const struct extent_buffer *eb, int slot,
+static void file_extent_err(const struct extent_buffer *eb, int slot,
                            const char *fmt, ...)
 {
+       const struct btrfs_fs_info *fs_info = eb->fs_info;
        struct btrfs_key key;
        struct va_format vaf;
        va_list args;
@@ -97,23 +97,23 @@ static void file_extent_err(const struct btrfs_fs_info *fs_info,
 #define CHECK_FE_ALIGNED(fs_info, leaf, slot, fi, name, alignment)           \
 ({                                                                           \
        if (!IS_ALIGNED(btrfs_file_extent_##name((leaf), (fi)), (alignment))) \
-               file_extent_err((fs_info), (leaf), (slot),                    \
+               file_extent_err((leaf), (slot),                               \
        "invalid %s for file extent, have %llu, should be aligned to %u",     \
                        (#name), btrfs_file_extent_##name((leaf), (fi)),      \
                        (alignment));                                         \
        (!IS_ALIGNED(btrfs_file_extent_##name((leaf), (fi)), (alignment)));   \
 })
 
-static int check_extent_data_item(struct btrfs_fs_info *fs_info,
-                                 struct extent_buffer *leaf,
+static int check_extent_data_item(struct extent_buffer *leaf,
                                  struct btrfs_key *key, int slot)
 {
+       struct btrfs_fs_info *fs_info = leaf->fs_info;
        struct btrfs_file_extent_item *fi;
        u32 sectorsize = fs_info->sectorsize;
        u32 item_size = btrfs_item_size_nr(leaf, slot);
 
        if (!IS_ALIGNED(key->offset, sectorsize)) {
-               file_extent_err(fs_info, leaf, slot,
+               file_extent_err(leaf, slot,
 "unaligned file_offset for file extent, have %llu should be aligned to %u",
                        key->offset, sectorsize);
                return -EUCLEAN;
@@ -122,7 +122,7 @@ static int check_extent_data_item(struct btrfs_fs_info *fs_info,
        fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item);
 
        if (btrfs_file_extent_type(leaf, fi) > BTRFS_FILE_EXTENT_TYPES) {
-               file_extent_err(fs_info, leaf, slot,
+               file_extent_err(leaf, slot,
                "invalid type for file extent, have %u expect range [0, %u]",
                        btrfs_file_extent_type(leaf, fi),
                        BTRFS_FILE_EXTENT_TYPES);
@@ -134,14 +134,14 @@ static int check_extent_data_item(struct btrfs_fs_info *fs_info,
         * and must be caught in open_ctree().
         */
        if (btrfs_file_extent_compression(leaf, fi) > BTRFS_COMPRESS_TYPES) {
-               file_extent_err(fs_info, leaf, slot,
+               file_extent_err(leaf, slot,
        "invalid compression for file extent, have %u expect range [0, %u]",
                        btrfs_file_extent_compression(leaf, fi),
                        BTRFS_COMPRESS_TYPES);
                return -EUCLEAN;
        }
        if (btrfs_file_extent_encryption(leaf, fi)) {
-               file_extent_err(fs_info, leaf, slot,
+               file_extent_err(leaf, slot,
                        "invalid encryption for file extent, have %u expect 0",
                        btrfs_file_extent_encryption(leaf, fi));
                return -EUCLEAN;
@@ -149,7 +149,7 @@ static int check_extent_data_item(struct btrfs_fs_info *fs_info,
        if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) {
                /* Inline extent must have 0 as key offset */
                if (key->offset) {
-                       file_extent_err(fs_info, leaf, slot,
+                       file_extent_err(leaf, slot,
                "invalid file_offset for inline file extent, have %llu expect 0",
                                key->offset);
                        return -EUCLEAN;
@@ -163,7 +163,7 @@ static int check_extent_data_item(struct btrfs_fs_info *fs_info,
                /* Uncompressed inline extent size must match item size */
                if (item_size != BTRFS_FILE_EXTENT_INLINE_DATA_START +
                    btrfs_file_extent_ram_bytes(leaf, fi)) {
-                       file_extent_err(fs_info, leaf, slot,
+                       file_extent_err(leaf, slot,
        "invalid ram_bytes for uncompressed inline extent, have %u expect %llu",
                                item_size, BTRFS_FILE_EXTENT_INLINE_DATA_START +
                                btrfs_file_extent_ram_bytes(leaf, fi));
@@ -174,7 +174,7 @@ static int check_extent_data_item(struct btrfs_fs_info *fs_info,
 
        /* Regular or preallocated extent has fixed item size */
        if (item_size != sizeof(*fi)) {
-               file_extent_err(fs_info, leaf, slot,
+               file_extent_err(leaf, slot,
        "invalid item size for reg/prealloc file extent, have %u expect %zu",
                        item_size, sizeof(*fi));
                return -EUCLEAN;
@@ -188,27 +188,27 @@ static int check_extent_data_item(struct btrfs_fs_info *fs_info,
        return 0;
 }
 
-static int check_csum_item(struct btrfs_fs_info *fs_info,
-                          struct extent_buffer *leaf, struct btrfs_key *key,
+static int check_csum_item(struct extent_buffer *leaf, struct btrfs_key *key,
                           int slot)
 {
+       struct btrfs_fs_info *fs_info = leaf->fs_info;
        u32 sectorsize = fs_info->sectorsize;
        u32 csumsize = btrfs_super_csum_size(fs_info->super_copy);
 
        if (key->objectid != BTRFS_EXTENT_CSUM_OBJECTID) {
-               generic_err(fs_info, leaf, slot,
+               generic_err(leaf, slot,
                "invalid key objectid for csum item, have %llu expect %llu",
                        key->objectid, BTRFS_EXTENT_CSUM_OBJECTID);
                return -EUCLEAN;
        }
        if (!IS_ALIGNED(key->offset, sectorsize)) {
-               generic_err(fs_info, leaf, slot,
+               generic_err(leaf, slot,
        "unaligned key offset for csum item, have %llu should be aligned to %u",
                        key->offset, sectorsize);
                return -EUCLEAN;
        }
        if (!IS_ALIGNED(btrfs_item_size_nr(leaf, slot), csumsize)) {
-               generic_err(fs_info, leaf, slot,
+               generic_err(leaf, slot,
        "unaligned item size for csum item, have %u should be aligned to %u",
                        btrfs_item_size_nr(leaf, slot), csumsize);
                return -EUCLEAN;
@@ -220,12 +220,12 @@ static int check_csum_item(struct btrfs_fs_info *fs_info,
  * Customized reported for dir_item, only important new info is key->objectid,
  * which represents inode number
  */
-__printf(4, 5)
+__printf(3, 4)
 __cold
-static void dir_item_err(const struct btrfs_fs_info *fs_info,
-                        const struct extent_buffer *eb, int slot,
+static void dir_item_err(const struct extent_buffer *eb, int slot,
                         const char *fmt, ...)
 {
+       const struct btrfs_fs_info *fs_info = eb->fs_info;
        struct btrfs_key key;
        struct va_format vaf;
        va_list args;
@@ -244,10 +244,10 @@ static void dir_item_err(const struct btrfs_fs_info *fs_info,
        va_end(args);
 }
 
-static int check_dir_item(struct btrfs_fs_info *fs_info,
-                         struct extent_buffer *leaf,
+static int check_dir_item(struct extent_buffer *leaf,
                          struct btrfs_key *key, int slot)
 {
+       struct btrfs_fs_info *fs_info = leaf->fs_info;
        struct btrfs_dir_item *di;
        u32 item_size = btrfs_item_size_nr(leaf, slot);
        u32 cur = 0;
@@ -263,7 +263,7 @@ static int check_dir_item(struct btrfs_fs_info *fs_info,
 
                /* header itself should not cross item boundary */
                if (cur + sizeof(*di) > item_size) {
-                       dir_item_err(fs_info, leaf, slot,
+                       dir_item_err(leaf, slot,
                "dir item header crosses item boundary, have %zu boundary %u",
                                cur + sizeof(*di), item_size);
                        return -EUCLEAN;
@@ -272,7 +272,7 @@ static int check_dir_item(struct btrfs_fs_info *fs_info,
                /* dir type check */
                dir_type = btrfs_dir_type(leaf, di);
                if (dir_type >= BTRFS_FT_MAX) {
-                       dir_item_err(fs_info, leaf, slot,
+                       dir_item_err(leaf, slot,
                        "invalid dir item type, have %u expect [0, %u)",
                                dir_type, BTRFS_FT_MAX);
                        return -EUCLEAN;
@@ -280,14 +280,14 @@ static int check_dir_item(struct btrfs_fs_info *fs_info,
 
                if (key->type == BTRFS_XATTR_ITEM_KEY &&
                    dir_type != BTRFS_FT_XATTR) {
-                       dir_item_err(fs_info, leaf, slot,
+                       dir_item_err(leaf, slot,
                "invalid dir item type for XATTR key, have %u expect %u",
                                dir_type, BTRFS_FT_XATTR);
                        return -EUCLEAN;
                }
                if (dir_type == BTRFS_FT_XATTR &&
                    key->type != BTRFS_XATTR_ITEM_KEY) {
-                       dir_item_err(fs_info, leaf, slot,
+                       dir_item_err(leaf, slot,
                        "xattr dir type found for non-XATTR key");
                        return -EUCLEAN;
                }
@@ -300,13 +300,13 @@ static int check_dir_item(struct btrfs_fs_info *fs_info,
                name_len = btrfs_dir_name_len(leaf, di);
                data_len = btrfs_dir_data_len(leaf, di);
                if (name_len > max_name_len) {
-                       dir_item_err(fs_info, leaf, slot,
+                       dir_item_err(leaf, slot,
                        "dir item name len too long, have %u max %u",
                                name_len, max_name_len);
                        return -EUCLEAN;
                }
                if (name_len + data_len > BTRFS_MAX_XATTR_SIZE(fs_info)) {
-                       dir_item_err(fs_info, leaf, slot,
+                       dir_item_err(leaf, slot,
                        "dir item name and data len too long, have %u max %u",
                                name_len + data_len,
                                BTRFS_MAX_XATTR_SIZE(fs_info));
@@ -314,7 +314,7 @@ static int check_dir_item(struct btrfs_fs_info *fs_info,
                }
 
                if (data_len && dir_type != BTRFS_FT_XATTR) {
-                       dir_item_err(fs_info, leaf, slot,
+                       dir_item_err(leaf, slot,
                        "dir item with invalid data len, have %u expect 0",
                                data_len);
                        return -EUCLEAN;
@@ -324,7 +324,7 @@ static int check_dir_item(struct btrfs_fs_info *fs_info,
 
                /* header and name/data should not cross item boundary */
                if (cur + total_size > item_size) {
-                       dir_item_err(fs_info, leaf, slot,
+                       dir_item_err(leaf, slot,
                "dir item data crosses item boundary, have %u boundary %u",
                                cur + total_size, item_size);
                        return -EUCLEAN;
@@ -342,7 +342,7 @@ static int check_dir_item(struct btrfs_fs_info *fs_info,
                                        (unsigned long)(di + 1), name_len);
                        name_hash = btrfs_name_hash(namebuf, name_len);
                        if (key->offset != name_hash) {
-                               dir_item_err(fs_info, leaf, slot,
+                               dir_item_err(leaf, slot,
                "name hash mismatch with key, have 0x%016x expect 0x%016llx",
                                        name_hash, key->offset);
                                return -EUCLEAN;
@@ -354,12 +354,12 @@ static int check_dir_item(struct btrfs_fs_info *fs_info,
        return 0;
 }
 
-__printf(4, 5)
+__printf(3, 4)
 __cold
-static void block_group_err(const struct btrfs_fs_info *fs_info,
-                           const struct extent_buffer *eb, int slot,
+static void block_group_err(const struct extent_buffer *eb, int slot,
                            const char *fmt, ...)
 {
+       const struct btrfs_fs_info *fs_info = eb->fs_info;
        struct btrfs_key key;
        struct va_format vaf;
        va_list args;
@@ -378,8 +378,7 @@ static void block_group_err(const struct btrfs_fs_info *fs_info,
        va_end(args);
 }
 
-static int check_block_group_item(struct btrfs_fs_info *fs_info,
-                                 struct extent_buffer *leaf,
+static int check_block_group_item(struct extent_buffer *leaf,
                                  struct btrfs_key *key, int slot)
 {
        struct btrfs_block_group_item bgi;
@@ -392,13 +391,13 @@ static int check_block_group_item(struct btrfs_fs_info *fs_info,
         * handle it.  We care more about the size.
         */
        if (key->offset == 0) {
-               block_group_err(fs_info, leaf, slot,
+               block_group_err(leaf, slot,
                                "invalid block group size 0");
                return -EUCLEAN;
        }
 
        if (item_size != sizeof(bgi)) {
-               block_group_err(fs_info, leaf, slot,
+               block_group_err(leaf, slot,
                        "invalid item size, have %u expect %zu",
                                item_size, sizeof(bgi));
                return -EUCLEAN;
@@ -408,7 +407,7 @@ static int check_block_group_item(struct btrfs_fs_info *fs_info,
                           sizeof(bgi));
        if (btrfs_block_group_chunk_objectid(&bgi) !=
            BTRFS_FIRST_CHUNK_TREE_OBJECTID) {
-               block_group_err(fs_info, leaf, slot,
+               block_group_err(leaf, slot,
                "invalid block group chunk objectid, have %llu expect %llu",
                                btrfs_block_group_chunk_objectid(&bgi),
                                BTRFS_FIRST_CHUNK_TREE_OBJECTID);
@@ -416,7 +415,7 @@ static int check_block_group_item(struct btrfs_fs_info *fs_info,
        }
 
        if (btrfs_block_group_used(&bgi) > key->offset) {
-               block_group_err(fs_info, leaf, slot,
+               block_group_err(leaf, slot,
                        "invalid block group used, have %llu expect [0, %llu)",
                                btrfs_block_group_used(&bgi), key->offset);
                return -EUCLEAN;
@@ -424,7 +423,7 @@ static int check_block_group_item(struct btrfs_fs_info *fs_info,
 
        flags = btrfs_block_group_flags(&bgi);
        if (hweight64(flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) > 1) {
-               block_group_err(fs_info, leaf, slot,
+               block_group_err(leaf, slot,
 "invalid profile flags, have 0x%llx (%lu bits set) expect no more than 1 bit set",
                        flags & BTRFS_BLOCK_GROUP_PROFILE_MASK,
                        hweight64(flags & BTRFS_BLOCK_GROUP_PROFILE_MASK));
@@ -437,7 +436,7 @@ static int check_block_group_item(struct btrfs_fs_info *fs_info,
            type != BTRFS_BLOCK_GROUP_SYSTEM &&
            type != (BTRFS_BLOCK_GROUP_METADATA |
                           BTRFS_BLOCK_GROUP_DATA)) {
-               block_group_err(fs_info, leaf, slot,
+               block_group_err(leaf, slot,
 "invalid type, have 0x%llx (%lu bits set) expect either 0x%llx, 0x%llx, 0x%llx or 0x%llx",
                        type, hweight64(type),
                        BTRFS_BLOCK_GROUP_DATA, BTRFS_BLOCK_GROUP_METADATA,
@@ -448,6 +447,327 @@ static int check_block_group_item(struct btrfs_fs_info *fs_info,
        return 0;
 }
 
+__printf(5, 6)
+__cold
+static void chunk_err(const struct btrfs_fs_info *fs_info,
+                     const struct extent_buffer *leaf,
+                     const struct btrfs_chunk *chunk, u64 logical,
+                     const char *fmt, ...)
+{
+       bool is_sb;
+       struct va_format vaf;
+       va_list args;
+       int i;
+       int slot = -1;
+
+       /* Only superblock eb is able to have such small offset */
+       is_sb = (leaf->start == BTRFS_SUPER_INFO_OFFSET);
+
+       if (!is_sb) {
+               /*
+                * Get the slot number by iterating through all slots, this
+                * would provide better readability.
+                */
+               for (i = 0; i < btrfs_header_nritems(leaf); i++) {
+                       if (btrfs_item_ptr_offset(leaf, i) ==
+                                       (unsigned long)chunk) {
+                               slot = i;
+                               break;
+                       }
+               }
+       }
+       va_start(args, fmt);
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+       if (is_sb)
+               btrfs_crit(fs_info,
+               "corrupt superblock syschunk array: chunk_start=%llu, %pV",
+                          logical, &vaf);
+       else
+               btrfs_crit(fs_info,
+       "corrupt leaf: root=%llu block=%llu slot=%d chunk_start=%llu, %pV",
+                          BTRFS_CHUNK_TREE_OBJECTID, leaf->start, slot,
+                          logical, &vaf);
+       va_end(args);
+}
+
+/*
+ * The common chunk check which could also work on super block sys chunk array.
+ *
+ * Return -EUCLEAN if anything is corrupted.
+ * Return 0 if everything is OK.
+ */
+int btrfs_check_chunk_valid(struct btrfs_fs_info *fs_info,
+                           struct extent_buffer *leaf,
+                           struct btrfs_chunk *chunk, u64 logical)
+{
+       u64 length;
+       u64 stripe_len;
+       u16 num_stripes;
+       u16 sub_stripes;
+       u64 type;
+       u64 features;
+       bool mixed = false;
+
+       length = btrfs_chunk_length(leaf, chunk);
+       stripe_len = btrfs_chunk_stripe_len(leaf, chunk);
+       num_stripes = btrfs_chunk_num_stripes(leaf, chunk);
+       sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk);
+       type = btrfs_chunk_type(leaf, chunk);
+
+       if (!num_stripes) {
+               chunk_err(fs_info, leaf, chunk, logical,
+                         "invalid chunk num_stripes, have %u", num_stripes);
+               return -EUCLEAN;
+       }
+       if (!IS_ALIGNED(logical, fs_info->sectorsize)) {
+               chunk_err(fs_info, leaf, chunk, logical,
+               "invalid chunk logical, have %llu should aligned to %u",
+                         logical, fs_info->sectorsize);
+               return -EUCLEAN;
+       }
+       if (btrfs_chunk_sector_size(leaf, chunk) != fs_info->sectorsize) {
+               chunk_err(fs_info, leaf, chunk, logical,
+                         "invalid chunk sectorsize, have %u expect %u",
+                         btrfs_chunk_sector_size(leaf, chunk),
+                         fs_info->sectorsize);
+               return -EUCLEAN;
+       }
+       if (!length || !IS_ALIGNED(length, fs_info->sectorsize)) {
+               chunk_err(fs_info, leaf, chunk, logical,
+                         "invalid chunk length, have %llu", length);
+               return -EUCLEAN;
+       }
+       if (!is_power_of_2(stripe_len) || stripe_len != BTRFS_STRIPE_LEN) {
+               chunk_err(fs_info, leaf, chunk, logical,
+                         "invalid chunk stripe length: %llu",
+                         stripe_len);
+               return -EUCLEAN;
+       }
+       if (~(BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_BLOCK_GROUP_PROFILE_MASK) &
+           type) {
+               chunk_err(fs_info, leaf, chunk, logical,
+                         "unrecognized chunk type: 0x%llx",
+                         ~(BTRFS_BLOCK_GROUP_TYPE_MASK |
+                           BTRFS_BLOCK_GROUP_PROFILE_MASK) &
+                         btrfs_chunk_type(leaf, chunk));
+               return -EUCLEAN;
+       }
+
+       if (!is_power_of_2(type & BTRFS_BLOCK_GROUP_PROFILE_MASK) &&
+           (type & BTRFS_BLOCK_GROUP_PROFILE_MASK) != 0) {
+               chunk_err(fs_info, leaf, chunk, logical,
+               "invalid chunk profile flag: 0x%llx, expect 0 or 1 bit set",
+                         type & BTRFS_BLOCK_GROUP_PROFILE_MASK);
+               return -EUCLEAN;
+       }
+       if ((type & BTRFS_BLOCK_GROUP_TYPE_MASK) == 0) {
+               chunk_err(fs_info, leaf, chunk, logical,
+       "missing chunk type flag, have 0x%llx one bit must be set in 0x%llx",
+                         type, BTRFS_BLOCK_GROUP_TYPE_MASK);
+               return -EUCLEAN;
+       }
+
+       if ((type & BTRFS_BLOCK_GROUP_SYSTEM) &&
+           (type & (BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA))) {
+               chunk_err(fs_info, leaf, chunk, logical,
+                         "system chunk with data or metadata type: 0x%llx",
+                         type);
+               return -EUCLEAN;
+       }
+
+       features = btrfs_super_incompat_flags(fs_info->super_copy);
+       if (features & BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS)
+               mixed = true;
+
+       if (!mixed) {
+               if ((type & BTRFS_BLOCK_GROUP_METADATA) &&
+                   (type & BTRFS_BLOCK_GROUP_DATA)) {
+                       chunk_err(fs_info, leaf, chunk, logical,
+                       "mixed chunk type in non-mixed mode: 0x%llx", type);
+                       return -EUCLEAN;
+               }
+       }
+
+       if ((type & BTRFS_BLOCK_GROUP_RAID10 && sub_stripes != 2) ||
+           (type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes != 2) ||
+           (type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < 2) ||
+           (type & BTRFS_BLOCK_GROUP_RAID6 && num_stripes < 3) ||
+           (type & BTRFS_BLOCK_GROUP_DUP && num_stripes != 2) ||
+           ((type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 && num_stripes != 1)) {
+               chunk_err(fs_info, leaf, chunk, logical,
+                       "invalid num_stripes:sub_stripes %u:%u for profile %llu",
+                       num_stripes, sub_stripes,
+                       type & BTRFS_BLOCK_GROUP_PROFILE_MASK);
+               return -EUCLEAN;
+       }
+
+       return 0;
+}
+
+__printf(4, 5)
+__cold
+static void dev_item_err(const struct btrfs_fs_info *fs_info,
+                        const struct extent_buffer *eb, int slot,
+                        const char *fmt, ...)
+{
+       struct btrfs_key key;
+       struct va_format vaf;
+       va_list args;
+
+       btrfs_item_key_to_cpu(eb, &key, slot);
+       va_start(args, fmt);
+
+       vaf.fmt = fmt;
+       vaf.va = &args;
+
+       btrfs_crit(fs_info,
+       "corrupt %s: root=%llu block=%llu slot=%d devid=%llu %pV",
+               btrfs_header_level(eb) == 0 ? "leaf" : "node",
+               btrfs_header_owner(eb), btrfs_header_bytenr(eb), slot,
+               key.objectid, &vaf);
+       va_end(args);
+}
+
+static int check_dev_item(struct btrfs_fs_info *fs_info,
+                         struct extent_buffer *leaf,
+                         struct btrfs_key *key, int slot)
+{
+       struct btrfs_dev_item *ditem;
+       u64 max_devid = max(BTRFS_MAX_DEVS(fs_info), BTRFS_MAX_DEVS_SYS_CHUNK);
+
+       if (key->objectid != BTRFS_DEV_ITEMS_OBJECTID) {
+               dev_item_err(fs_info, leaf, slot,
+                            "invalid objectid: has=%llu expect=%llu",
+                            key->objectid, BTRFS_DEV_ITEMS_OBJECTID);
+               return -EUCLEAN;
+       }
+       if (key->offset > max_devid) {
+               dev_item_err(fs_info, leaf, slot,
+                            "invalid devid: has=%llu expect=[0, %llu]",
+                            key->offset, max_devid);
+               return -EUCLEAN;
+       }
+       ditem = btrfs_item_ptr(leaf, slot, struct btrfs_dev_item);
+       if (btrfs_device_id(leaf, ditem) != key->offset) {
+               dev_item_err(fs_info, leaf, slot,
+                            "devid mismatch: key has=%llu item has=%llu",
+                            key->offset, btrfs_device_id(leaf, ditem));
+               return -EUCLEAN;
+       }
+
+       /*
+        * For device total_bytes, we don't have reliable way to check it, as
+        * it can be 0 for device removal. Device size check can only be done
+        * by dev extents check.
+        */
+       if (btrfs_device_bytes_used(leaf, ditem) >
+           btrfs_device_total_bytes(leaf, ditem)) {
+               dev_item_err(fs_info, leaf, slot,
+                            "invalid bytes used: have %llu expect [0, %llu]",
+                            btrfs_device_bytes_used(leaf, ditem),
+                            btrfs_device_total_bytes(leaf, ditem));
+               return -EUCLEAN;
+       }
+       /*
+        * Remaining members like io_align/type/gen/dev_group aren't really
+        * utilized.  Skip them to make later usage of them easier.
+        */
+       return 0;
+}
+
+/* Inode item error output has the same format as dir_item_err() */
+#define inode_item_err(fs_info, eb, slot, fmt, ...)                    \
+       dir_item_err(eb, slot, fmt, __VA_ARGS__)
+
+static int check_inode_item(struct btrfs_fs_info *fs_info,
+                           struct extent_buffer *leaf,
+                           struct btrfs_key *key, int slot)
+{
+       struct btrfs_inode_item *iitem;
+       u64 super_gen = btrfs_super_generation(fs_info->super_copy);
+       u32 valid_mask = (S_IFMT | S_ISUID | S_ISGID | S_ISVTX | 0777);
+       u32 mode;
+
+       if ((key->objectid < BTRFS_FIRST_FREE_OBJECTID ||
+            key->objectid > BTRFS_LAST_FREE_OBJECTID) &&
+           key->objectid != BTRFS_ROOT_TREE_DIR_OBJECTID &&
+           key->objectid != BTRFS_FREE_INO_OBJECTID) {
+               generic_err(leaf, slot,
+       "invalid key objectid: has %llu expect %llu or [%llu, %llu] or %llu",
+                           key->objectid, BTRFS_ROOT_TREE_DIR_OBJECTID,
+                           BTRFS_FIRST_FREE_OBJECTID,
+                           BTRFS_LAST_FREE_OBJECTID,
+                           BTRFS_FREE_INO_OBJECTID);
+               return -EUCLEAN;
+       }
+       if (key->offset != 0) {
+               inode_item_err(fs_info, leaf, slot,
+                       "invalid key offset: has %llu expect 0",
+                       key->offset);
+               return -EUCLEAN;
+       }
+       iitem = btrfs_item_ptr(leaf, slot, struct btrfs_inode_item);
+
+       /* Here we use super block generation + 1 to handle log tree */
+       if (btrfs_inode_generation(leaf, iitem) > super_gen + 1) {
+               inode_item_err(fs_info, leaf, slot,
+                       "invalid inode generation: has %llu expect (0, %llu]",
+                              btrfs_inode_generation(leaf, iitem),
+                              super_gen + 1);
+               return -EUCLEAN;
+       }
+       /* Note for ROOT_TREE_DIR_ITEM, mkfs could set its transid 0 */
+       if (btrfs_inode_transid(leaf, iitem) > super_gen + 1) {
+               inode_item_err(fs_info, leaf, slot,
+                       "invalid inode generation: has %llu expect [0, %llu]",
+                              btrfs_inode_transid(leaf, iitem), super_gen + 1);
+               return -EUCLEAN;
+       }
+
+       /*
+        * For size and nbytes it's better not to be too strict, as for dir
+        * item its size/nbytes can easily get wrong, but doesn't affect
+        * anything in the fs. So here we skip the check.
+        */
+       mode = btrfs_inode_mode(leaf, iitem);
+       if (mode & ~valid_mask) {
+               inode_item_err(fs_info, leaf, slot,
+                              "unknown mode bit detected: 0x%x",
+                              mode & ~valid_mask);
+               return -EUCLEAN;
+       }
+
+       /*
+        * S_IFMT is not bit mapped so we can't completely rely on is_power_of_2,
+        * but is_power_of_2() can save us from checking FIFO/CHR/DIR/REG.
+        * Only needs to check BLK, LNK and SOCKS
+        */
+       if (!is_power_of_2(mode & S_IFMT)) {
+               if (!S_ISLNK(mode) && !S_ISBLK(mode) && !S_ISSOCK(mode)) {
+                       inode_item_err(fs_info, leaf, slot,
+                       "invalid mode: has 0%o expect valid S_IF* bit(s)",
+                                      mode & S_IFMT);
+                       return -EUCLEAN;
+               }
+       }
+       if (S_ISDIR(mode) && btrfs_inode_nlink(leaf, iitem) > 1) {
+               inode_item_err(fs_info, leaf, slot,
+                      "invalid nlink: has %u expect no more than 1 for dir",
+                       btrfs_inode_nlink(leaf, iitem));
+               return -EUCLEAN;
+       }
+       if (btrfs_inode_flags(leaf, iitem) & ~BTRFS_INODE_FLAG_MASK) {
+               inode_item_err(fs_info, leaf, slot,
+                              "unknown flags detected: 0x%llx",
+                              btrfs_inode_flags(leaf, iitem) &
+                              ~BTRFS_INODE_FLAG_MASK);
+               return -EUCLEAN;
+       }
+       return 0;
+}
+
 /*
  * Common point to switch the item-specific validation.
  */
@@ -456,21 +776,33 @@ static int check_leaf_item(struct btrfs_fs_info *fs_info,
                           struct btrfs_key *key, int slot)
 {
        int ret = 0;
+       struct btrfs_chunk *chunk;
 
        switch (key->type) {
        case BTRFS_EXTENT_DATA_KEY:
-               ret = check_extent_data_item(fs_info, leaf, key, slot);
+               ret = check_extent_data_item(leaf, key, slot);
                break;
        case BTRFS_EXTENT_CSUM_KEY:
-               ret = check_csum_item(fs_info, leaf, key, slot);
+               ret = check_csum_item(leaf, key, slot);
                break;
        case BTRFS_DIR_ITEM_KEY:
        case BTRFS_DIR_INDEX_KEY:
        case BTRFS_XATTR_ITEM_KEY:
-               ret = check_dir_item(fs_info, leaf, key, slot);
+               ret = check_dir_item(leaf, key, slot);
                break;
        case BTRFS_BLOCK_GROUP_ITEM_KEY:
-               ret = check_block_group_item(fs_info, leaf, key, slot);
+               ret = check_block_group_item(leaf, key, slot);
+               break;
+       case BTRFS_CHUNK_ITEM_KEY:
+               chunk = btrfs_item_ptr(leaf, slot, struct btrfs_chunk);
+               ret = btrfs_check_chunk_valid(fs_info, leaf, chunk,
+                                             key->offset);
+               break;
+       case BTRFS_DEV_ITEM_KEY:
+               ret = check_dev_item(fs_info, leaf, key, slot);
+               break;
+       case BTRFS_INODE_ITEM_KEY:
+               ret = check_inode_item(fs_info, leaf, key, slot);
                break;
        }
        return ret;
@@ -486,7 +818,7 @@ static int check_leaf(struct btrfs_fs_info *fs_info, struct extent_buffer *leaf,
        int slot;
 
        if (btrfs_header_level(leaf) != 0) {
-               generic_err(fs_info, leaf, 0,
+               generic_err(leaf, 0,
                        "invalid level for leaf, have %d expect 0",
                        btrfs_header_level(leaf));
                return -EUCLEAN;
@@ -511,7 +843,7 @@ static int check_leaf(struct btrfs_fs_info *fs_info, struct extent_buffer *leaf,
                    owner == BTRFS_DEV_TREE_OBJECTID ||
                    owner == BTRFS_FS_TREE_OBJECTID ||
                    owner == BTRFS_DATA_RELOC_TREE_OBJECTID) {
-                       generic_err(fs_info, leaf, 0,
+                       generic_err(leaf, 0,
                        "invalid root, root %llu must never be empty",
                                    owner);
                        return -EUCLEAN;
@@ -531,7 +863,7 @@ static int check_leaf(struct btrfs_fs_info *fs_info, struct extent_buffer *leaf,
                        eb = btrfs_root_node(check_root);
                        /* if leaf is the root, then it's fine */
                        if (leaf != eb) {
-                               generic_err(fs_info, leaf, 0,
+                               generic_err(leaf, 0,
                "invalid nritems, have %u should not be 0 for non-root leaf",
                                        nritems);
                                free_extent_buffer(eb);
@@ -564,7 +896,7 @@ static int check_leaf(struct btrfs_fs_info *fs_info, struct extent_buffer *leaf,
 
                /* Make sure the keys are in the right order */
                if (btrfs_comp_cpu_keys(&prev_key, &key) >= 0) {
-                       generic_err(fs_info, leaf, slot,
+                       generic_err(leaf, slot,
        "bad key order, prev (%llu %u %llu) current (%llu %u %llu)",
                                prev_key.objectid, prev_key.type,
                                prev_key.offset, key.objectid, key.type,
@@ -583,7 +915,7 @@ static int check_leaf(struct btrfs_fs_info *fs_info, struct extent_buffer *leaf,
                        item_end_expected = btrfs_item_offset_nr(leaf,
                                                                 slot - 1);
                if (btrfs_item_end_nr(leaf, slot) != item_end_expected) {
-                       generic_err(fs_info, leaf, slot,
+                       generic_err(leaf, slot,
                                "unexpected item end, have %u expect %u",
                                btrfs_item_end_nr(leaf, slot),
                                item_end_expected);
@@ -597,7 +929,7 @@ static int check_leaf(struct btrfs_fs_info *fs_info, struct extent_buffer *leaf,
                 */
                if (btrfs_item_end_nr(leaf, slot) >
                    BTRFS_LEAF_DATA_SIZE(fs_info)) {
-                       generic_err(fs_info, leaf, slot,
+                       generic_err(leaf, slot,
                        "slot end outside of leaf, have %u expect range [0, %u]",
                                btrfs_item_end_nr(leaf, slot),
                                BTRFS_LEAF_DATA_SIZE(fs_info));
@@ -607,7 +939,7 @@ static int check_leaf(struct btrfs_fs_info *fs_info, struct extent_buffer *leaf,
                /* Also check if the item pointer overlaps with btrfs item. */
                if (btrfs_item_nr_offset(slot) + sizeof(struct btrfs_item) >
                    btrfs_item_ptr_offset(leaf, slot)) {
-                       generic_err(fs_info, leaf, slot,
+                       generic_err(leaf, slot,
                "slot overlaps with its data, item end %lu data start %lu",
                                btrfs_item_nr_offset(slot) +
                                sizeof(struct btrfs_item),
@@ -655,7 +987,7 @@ int btrfs_check_node(struct btrfs_fs_info *fs_info, struct extent_buffer *node)
        int ret = 0;
 
        if (level <= 0 || level >= BTRFS_MAX_LEVEL) {
-               generic_err(fs_info, node, 0,
+               generic_err(node, 0,
                        "invalid level for node, have %d expect [1, %d]",
                        level, BTRFS_MAX_LEVEL - 1);
                return -EUCLEAN;
@@ -675,13 +1007,13 @@ int btrfs_check_node(struct btrfs_fs_info *fs_info, struct extent_buffer *node)
                btrfs_node_key_to_cpu(node, &next_key, slot + 1);
 
                if (!bytenr) {
-                       generic_err(fs_info, node, slot,
+                       generic_err(node, slot,
                                "invalid NULL node pointer");
                        ret = -EUCLEAN;
                        goto out;
                }
                if (!IS_ALIGNED(bytenr, fs_info->sectorsize)) {
-                       generic_err(fs_info, node, slot,
+                       generic_err(node, slot,
                        "unaligned pointer, have %llu should be aligned to %u",
                                bytenr, fs_info->sectorsize);
                        ret = -EUCLEAN;
@@ -689,7 +1021,7 @@ int btrfs_check_node(struct btrfs_fs_info *fs_info, struct extent_buffer *node)
                }
 
                if (btrfs_comp_cpu_keys(&key, &next_key) >= 0) {
-                       generic_err(fs_info, node, slot,
+                       generic_err(node, slot,
        "bad key order, current (%llu %u %llu) next (%llu %u %llu)",
                                key.objectid, key.type, key.offset,
                                next_key.objectid, next_key.type,