btrfs: Refactor check_leaf function for later expansion
authorQu Wenruo <quwenruo.btrfs@gmx.com>
Wed, 23 Aug 2017 07:57:56 +0000 (16:57 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 5 Dec 2018 18:41:11 +0000 (19:41 +0100)
commit c3267bbaa9cae09b62960eafe33ad19196803285 upstream.

Current check_leaf() function does a good job checking key order and
item offset/size.

However it only checks from slot 0 to the last but one slot, this is
good but makes later expansion hard.

So this refactoring iterates from slot 0 to the last slot.
For key comparison, it uses a key with all 0 as initial key, so all
valid keys should be larger than that.

And for item size/offset checks, it compares current item end with
previous item offset.
For slot 0, use leaf end as a special case.

This makes later item/key offset checks and item size checks easier to
be implemented.

Also, makes check_leaf() to return -EUCLEAN other than -EIO to indicate
error.

Signed-off-by: Qu Wenruo <quwenruo.btrfs@gmx.com>
Reviewed-by: Nikolay Borisov <nborisov@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/btrfs/disk-io.c

index 0e67cee..4a1e63d 100644 (file)
@@ -554,8 +554,9 @@ static noinline int check_leaf(struct btrfs_root *root,
                               struct extent_buffer *leaf)
 {
        struct btrfs_fs_info *fs_info = root->fs_info;
+       /* No valid key type is 0, so all key should be larger than this key */
+       struct btrfs_key prev_key = {0, 0, 0};
        struct btrfs_key key;
-       struct btrfs_key leaf_key;
        u32 nritems = btrfs_header_nritems(leaf);
        int slot;
 
@@ -588,7 +589,7 @@ static noinline int check_leaf(struct btrfs_root *root,
                                CORRUPT("non-root leaf's nritems is 0",
                                        leaf, check_root, 0);
                                free_extent_buffer(eb);
-                               return -EIO;
+                               return -EUCLEAN;
                        }
                        free_extent_buffer(eb);
                }
@@ -598,28 +599,23 @@ static noinline int check_leaf(struct btrfs_root *root,
        if (nritems == 0)
                return 0;
 
-       /* Check the 0 item */
-       if (btrfs_item_offset_nr(leaf, 0) + btrfs_item_size_nr(leaf, 0) !=
-           BTRFS_LEAF_DATA_SIZE(fs_info)) {
-               CORRUPT("invalid item offset size pair", leaf, root, 0);
-               return -EIO;
-       }
-
        /*
-        * Check to make sure each items keys are in the correct order and their
-        * offsets make sense.  We only have to loop through nritems-1 because
-        * we check the current slot against the next slot, which verifies the
-        * next slot's offset+size makes sense and that the current's slot
-        * offset is correct.
+        * Check the following things to make sure this is a good leaf, and
+        * leaf users won't need to bother with similar sanity checks:
+        *
+        * 1) key order
+        * 2) item offset and size
+        *    No overlap, no hole, all inside the leaf.
         */
-       for (slot = 0; slot < nritems - 1; slot++) {
-               btrfs_item_key_to_cpu(leaf, &leaf_key, slot);
-               btrfs_item_key_to_cpu(leaf, &key, slot + 1);
+       for (slot = 0; slot < nritems; slot++) {
+               u32 item_end_expected;
+
+               btrfs_item_key_to_cpu(leaf, &key, slot);
 
                /* Make sure the keys are in the right order */
-               if (btrfs_comp_cpu_keys(&leaf_key, &key) >= 0) {
+               if (btrfs_comp_cpu_keys(&prev_key, &key) >= 0) {
                        CORRUPT("bad key order", leaf, root, slot);
-                       return -EIO;
+                       return -EUCLEAN;
                }
 
                /*
@@ -627,10 +623,14 @@ static noinline int check_leaf(struct btrfs_root *root,
                 * item data starts at the end of the leaf and grows towards the
                 * front.
                 */
-               if (btrfs_item_offset_nr(leaf, slot) !=
-                       btrfs_item_end_nr(leaf, slot + 1)) {
+               if (slot == 0)
+                       item_end_expected = BTRFS_LEAF_DATA_SIZE(fs_info);
+               else
+                       item_end_expected = btrfs_item_offset_nr(leaf,
+                                                                slot - 1);
+               if (btrfs_item_end_nr(leaf, slot) != item_end_expected) {
                        CORRUPT("slot offset bad", leaf, root, slot);
-                       return -EIO;
+                       return -EUCLEAN;
                }
 
                /*
@@ -641,8 +641,12 @@ static noinline int check_leaf(struct btrfs_root *root,
                if (btrfs_item_end_nr(leaf, slot) >
                    BTRFS_LEAF_DATA_SIZE(fs_info)) {
                        CORRUPT("slot end outside of leaf", leaf, root, slot);
-                       return -EIO;
+                       return -EUCLEAN;
                }
+
+               prev_key.objectid = key.objectid;
+               prev_key.type = key.type;
+               prev_key.offset = key.offset;
        }
 
        return 0;