btrfs-progs: dump-super: check array_size in print_sys_chunk_array
[platform/upstream/btrfs-progs.git] / cmds-check.c
index c94216b..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)
 {
@@ -4699,25 +4704,40 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
        u64 disk_bytenr;
        u64 disk_num_bytes;
        u64 extent_num_bytes;
-       u64 found;
+       u64 extent_offset;
+       u64 csum_found;         /* In byte size, sectorsize aligned */
+       u64 search_start;       /* Logical range start we search for csum */
+       u64 search_len;         /* Logical range len we search for csum */
        unsigned int extent_type;
        unsigned int is_hole;
+       int compressed = 0;
        int ret;
        int err = 0;
 
        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;
        }
@@ -4735,24 +4755,45 @@ static int check_file_extent(struct btrfs_root *root, struct btrfs_key *fkey,
        disk_bytenr = btrfs_file_extent_disk_bytenr(node, fi);
        disk_num_bytes = btrfs_file_extent_disk_num_bytes(node, fi);
        extent_num_bytes = btrfs_file_extent_num_bytes(node, fi);
+       extent_offset = btrfs_file_extent_offset(node, fi);
+       compressed = btrfs_file_extent_compression(node, fi);
        is_hole = (disk_bytenr == 0) && (disk_num_bytes == 0);
 
-       /* Check EXTENT_DATA datasum */
-       ret = count_csum_range(root, disk_bytenr, disk_num_bytes, &found);
-       if (found > 0 && nodatasum) {
+       /*
+        * Check EXTENT_DATA csum
+        *
+        * For plain (uncompressed) extent, we should only check the range
+        * we're referring to, as it's possible that part of prealloc extent
+        * has been written, and has csum:
+        *
+        * |<--- Original large preallocated extent A ---->|
+        * |<- Prealloc File Extent ->|<- Regular Extent ->|
+        *      No csum                         Has csum
+        *
+        * For compressed extent, we should check the whole range.
+        */
+       if (!compressed) {
+               search_start = disk_bytenr + extent_offset;
+               search_len = extent_num_bytes;
+       } else {
+               search_start = disk_bytenr;
+               search_len = disk_num_bytes;
+       }
+       ret = count_csum_range(root, search_start, search_len, &csum_found);
+       if (csum_found > 0 && nodatasum) {
                err |= ODD_CSUM_ITEM;
                error("root %llu EXTENT_DATA[%llu %llu] nodatasum shouldn't have datasum",
                      root->objectid, fkey->objectid, fkey->offset);
        } else if (extent_type == BTRFS_FILE_EXTENT_REG && !nodatasum &&
-                  !is_hole &&
-                  (ret < 0 || found == 0 || found < disk_num_bytes)) {
+                  !is_hole && (ret < 0 || csum_found < search_len)) {
                err |= CSUM_ITEM_MISSING;
-               error("root %llu EXTENT_DATA[%llu %llu] datasum missing",
-                     root->objectid, fkey->objectid, fkey->offset);
-       } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC && found > 0) {
+               error("root %llu EXTENT_DATA[%llu %llu] csum missing, have: %llu, expected: %llu",
+                     root->objectid, fkey->objectid, fkey->offset,
+                     csum_found, search_len);
+       } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC && csum_found > 0) {
                err |= ODD_CSUM_ITEM;
-               error("root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have datasum",
-                     root->objectid, fkey->objectid, fkey->offset);
+               error("root %llu EXTENT_DATA[%llu %llu] prealloc shouldn't have csum, but has: %llu",
+                     root->objectid, fkey->objectid, fkey->offset, csum_found);
        }
 
        /* Check EXTENT_DATA hole */
@@ -4961,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);
@@ -4986,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)
@@ -5021,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:
@@ -10181,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];
@@ -10225,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,
@@ -10237,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",
@@ -10718,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);
@@ -10761,8 +10832,7 @@ next:
        }
 
        ptr += btrfs_extent_inline_ref_size(type);
-       if (ptr < end)
-               goto next;
+       goto next;
 
 out:
        return err;