btrfs-progs: dump-super: check array_size in print_sys_chunk_array
[platform/upstream/btrfs-progs.git] / cmds-check.c
index 1f75157..17b7efb 100644 (file)
@@ -1868,6 +1868,11 @@ static int update_nodes_refs(struct btrfs_root *root, u64 bytenr,
 static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
                            unsigned int ext_ref);
 
+/*
+ * Returns >0  Found error, not fatal, should continue
+ * Returns <0  Fatal error, must exit the whole check
+ * Returns 0   No errors found
+ */
 static int process_one_leaf_v2(struct btrfs_root *root, struct btrfs_path *path,
                               struct node_refs *nrefs, int *level, int ext_ref)
 {
@@ -1937,13 +1942,8 @@ again:
        }
 out:
        err &= ~LAST_ITEM;
-       /*
-        * Convert any error bitmap to -EIO, as we should avoid
-        * mixing positive and negative return value to represent
-        * error
-        */
        if (err && !ret)
-               ret = -EIO;
+               ret = err;
        return ret;
 }
 
@@ -2212,6 +2212,11 @@ out:
 static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path,
                            unsigned int ext_ref);
 
+/*
+ * Returns >0  Found error, should continue
+ * Returns <0  Fatal error, must exit the whole check
+ * Returns 0   No errors found
+ */
 static int walk_down_tree_v2(struct btrfs_root *root, struct btrfs_path *path,
                             int *level, struct node_refs *nrefs, int ext_ref)
 {
@@ -4711,17 +4716,28 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
 
        fi = btrfs_item_ptr(node, slot, struct btrfs_file_extent_item);
 
+       /* Check inline extent */
        extent_type = btrfs_file_extent_type(node, fi);
-       /* Skip if file extent is inline */
        if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
                struct btrfs_item *e = btrfs_item_nr(slot);
                u32 item_inline_len;
 
                item_inline_len = btrfs_file_extent_inline_item_len(node, e);
                extent_num_bytes = btrfs_file_extent_inline_len(node, slot, fi);
-               if (extent_num_bytes == 0 ||
-                   extent_num_bytes != item_inline_len)
+               compressed = btrfs_file_extent_compression(node, fi);
+               if (extent_num_bytes == 0) {
+                       error(
+               "root %llu EXTENT_DATA[%llu %llu] has empty inline extent",
+                               root->objectid, fkey->objectid, fkey->offset);
+                       err |= FILE_EXTENT_ERROR;
+               }
+               if (!compressed && extent_num_bytes != item_inline_len) {
+                       error(
+               "root %llu EXTENT_DATA[%llu %llu] wrong inline size, have: %llu, expected: %u",
+                               root->objectid, fkey->objectid, fkey->offset,
+                               extent_num_bytes, item_inline_len);
                        err |= FILE_EXTENT_ERROR;
+               }
                *size += extent_num_bytes;
                return err;
        }
@@ -4986,6 +5002,8 @@ static int check_fs_first_inode(struct btrfs_root *root, unsigned int ext_ref)
        if (ret > 0) {
                ret = 0;
                err |= INODE_ITEM_MISSING;
+               error("first inode item of root %llu is missing",
+                     root->objectid);
        }
 
        err |= check_inode_item(root, &path, ext_ref);
@@ -5011,8 +5029,9 @@ static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref)
        struct btrfs_path path;
        struct node_refs nrefs;
        struct btrfs_root_item *root_item = &root->root_item;
-       int ret, wret;
+       int ret;
        int level;
+       int err = 0;
 
        /*
         * We need to manually check the first inode item(256)
@@ -5046,17 +5065,21 @@ static int check_fs_root_v2(struct btrfs_root *root, unsigned int ext_ref)
        }
 
        while (1) {
-               wret = walk_down_tree_v2(root, &path, &level, &nrefs, ext_ref);
-               if (wret < 0)
-                       ret = wret;
-               if (wret != 0)
+               ret = walk_down_tree_v2(root, &path, &level, &nrefs, ext_ref);
+               err |= !!ret;
+
+               /* if ret is negative, walk shall stop */
+               if (ret < 0) {
+                       ret = err;
                        break;
+               }
 
-               wret = walk_up_tree_v2(root, &path, &level);
-               if (wret < 0)
-                       ret = wret;
-               if (wret != 0)
+               ret = walk_up_tree_v2(root, &path, &level);
+               if (ret != 0) {
+                       /* Normal exit, reset ret to err */
+                       ret = err;
                        break;
+               }
        }
 
 out:
@@ -10206,10 +10229,8 @@ static int check_extent_data_item(struct btrfs_root *root,
        dbref_key.offset = btrfs_file_extent_disk_num_bytes(eb, fi);
 
        ret = btrfs_search_slot(NULL, extent_root, &dbref_key, &path, 0, 0);
-       if (ret) {
-               err |= BACKREF_MISSING;
-               goto error;
-       }
+       if (ret)
+               goto out;
 
        leaf = path.nodes[0];
        slot = path.slots[0];
@@ -10250,11 +10271,10 @@ static int check_extent_data_item(struct btrfs_root *root,
                ptr += btrfs_extent_inline_ref_size(type);
        }
 
-       /* Didn't found inlined data backref, try EXTENT_DATA_REF_KEY */
        if (!found_dbackref) {
                btrfs_release_path(&path);
 
-               btrfs_init_path(&path);
+               /* Didn't find inlined data backref, try EXTENT_DATA_REF_KEY */
                dbref_key.objectid = btrfs_file_extent_disk_bytenr(eb, fi);
                dbref_key.type = BTRFS_EXTENT_DATA_REF_KEY;
                dbref_key.offset = hash_extent_data_ref(root->objectid,
@@ -10262,13 +10282,32 @@ static int check_extent_data_item(struct btrfs_root *root,
 
                ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
                                        &dbref_key, &path, 0, 0);
-               if (!ret)
+               if (!ret) {
+                       found_dbackref = 1;
+                       goto out;
+               }
+
+               btrfs_release_path(&path);
+
+               /*
+                * Neither inlined nor EXTENT_DATA_REF found, try
+                * SHARED_DATA_REF as last chance.
+                */
+               dbref_key.objectid = disk_bytenr;
+               dbref_key.type = BTRFS_SHARED_DATA_REF_KEY;
+               dbref_key.offset = eb->start;
+
+               ret = btrfs_search_slot(NULL, root->fs_info->extent_root,
+                                       &dbref_key, &path, 0, 0);
+               if (!ret) {
                        found_dbackref = 1;
+                       goto out;
+               }
        }
 
+out:
        if (!found_dbackref)
                err |= BACKREF_MISSING;
-error:
        btrfs_release_path(&path);
        if (err & BACKREF_MISSING) {
                error("data extent[%llu %llu] backref lost",
@@ -10743,13 +10782,20 @@ static int check_extent_item(struct btrfs_fs_info *fs_info,
        }
        end = (unsigned long)ei + item_size;
 
-       if (ptr >= end) {
+next:
+       /* Reached extent item end normally */
+       if (ptr == end)
+               goto out;
+
+       /* Beyond extent item end, wrong item size */
+       if (ptr > end) {
                err |= ITEM_SIZE_MISMATCH;
+               error("extent item at bytenr %llu slot %d has wrong size",
+                       eb->start, slot);
                goto out;
        }
 
        /* Now check every backref in this extent item */
-next:
        iref = (struct btrfs_extent_inline_ref *)ptr;
        type = btrfs_extent_inline_ref_type(eb, iref);
        offset = btrfs_extent_inline_ref_offset(eb, iref);
@@ -10786,8 +10832,7 @@ next:
        }
 
        ptr += btrfs_extent_inline_ref_size(type);
-       if (ptr < end)
-               goto next;
+       goto next;
 
 out:
        return err;