summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQu Wenruo <quwenruo.btrfs@gmx.com>2017-08-23 16:57:56 +0900
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-12-08 13:05:11 +0100
commitb0a67e43dc53407cb03095232eb7c65a6d46e477 (patch)
tree1e178fa958491186f3083c3007c04be64a734a56
parent23eb2f435a07e1e09d48ea10c4a22bc96e16fde6 (diff)
btrfs: Refactor check_leaf function for later expansion
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> [bwh: Backported to 4.9: - BTRFS_LEAF_DATA_SIZE() takes a root rather than an fs_info - Adjust context] Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--fs/btrfs/disk-io.c50
1 files changed, 27 insertions, 23 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 875715769a29..be41bf2a3819 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -547,8 +547,9 @@ static int check_tree_block_fsid(struct btrfs_fs_info *fs_info,
static noinline int check_leaf(struct btrfs_root *root,
struct extent_buffer *leaf)
{
+ /* 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;
@@ -581,7 +582,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);
}
@@ -591,28 +592,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(root)) {
- 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;
}
/*
@@ -620,10 +616,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(root);
+ 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;
}
/*
@@ -634,8 +634,12 @@ static noinline int check_leaf(struct btrfs_root *root,
if (btrfs_item_end_nr(leaf, slot) >
BTRFS_LEAF_DATA_SIZE(root)) {
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;