aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/btrfs/tree-checker.c67
1 files changed, 60 insertions, 7 deletions
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index 5431f71e2a47..b7ff3e3d3a63 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -2150,7 +2150,8 @@ static int check_free_space_info(struct extent_buffer *leaf, struct btrfs_key *k
return 0;
}
-static int check_free_space_common_key(struct extent_buffer *leaf, struct btrfs_key *key, int slot)
+static int check_free_space_common_key(struct extent_buffer *leaf, struct btrfs_key *key, int slot,
+ struct btrfs_key *prev_key)
{
struct btrfs_fs_info *fs_info = leaf->fs_info;
const u32 blocksize = fs_info->sectorsize;
@@ -2179,14 +2180,65 @@ static int check_free_space_common_key(struct extent_buffer *leaf, struct btrfs_
type_str, key->objectid, key->offset);
return -EUCLEAN;
}
+ if (slot == 0)
+ return 0;
+
+ /*
+ * Make sure the current key is inside the block group, and matching
+ * the expected info type.
+ */
+ if (prev_key->type == BTRFS_FREE_SPACE_INFO_KEY) {
+ struct btrfs_free_space_info *fsi;
+ u32 info_flags;
+
+ if (unlikely(key->objectid < prev_key->objectid ||
+ key->objectid + key->offset > prev_key->objectid + prev_key->offset)) {
+ generic_err(leaf, slot,
+"free space %s is not inside the space info, prev key " BTRFS_KEY_FMT " current key " BTRFS_KEY_FMT,
+ type_str, BTRFS_KEY_FMT_VALUE(prev_key),
+ BTRFS_KEY_FMT_VALUE(key));
+ return -EUCLEAN;
+ }
+ fsi = btrfs_item_ptr(leaf, slot - 1, struct btrfs_free_space_info);
+ info_flags = btrfs_free_space_flags(leaf, fsi);
+ if (unlikely((info_flags == BTRFS_FREE_SPACE_USING_BITMAPS &&
+ key->type == BTRFS_FREE_SPACE_EXTENT_KEY) ||
+ (info_flags != BTRFS_FREE_SPACE_USING_BITMAPS &&
+ key->type == BTRFS_FREE_SPACE_BITMAP_KEY))) {
+ generic_err(leaf, slot,
+"free space %s key type is not matching the type of space info, key type %u space info flags %u",
+ type_str, key->type, info_flags);
+ return -EUCLEAN;
+ }
+ return 0;
+ }
+ /*
+ * Previous key should be either FREE_SPACE_EXTENT or FREE_SPACE_BITMAP.
+ * Inside the same block group the key type should match each other, and
+ * no overlaps.
+ */
+ if (unlikely(key->type != prev_key->type)) {
+ generic_err(leaf, slot,
+"free space %s key type is not matching the type of previous key, key type %u prev key type %u",
+ type_str, key->type, prev_key->type);
+ return -EUCLEAN;
+ }
+ if (unlikely(prev_key->objectid + prev_key->offset > key->objectid)) {
+ generic_err(leaf, slot,
+"free space %s key overlaps previous key, prev key " BTRFS_KEY_FMT " current key " BTRFS_KEY_FMT,
+ type_str, BTRFS_KEY_FMT_VALUE(prev_key),
+ BTRFS_KEY_FMT_VALUE(key));
+ return -EUCLEAN;
+ }
return 0;
}
-static int check_free_space_extent(struct extent_buffer *leaf, struct btrfs_key *key, int slot)
+static int check_free_space_extent(struct extent_buffer *leaf, struct btrfs_key *key, int slot,
+ struct btrfs_key *prev_key)
{
int ret;
- ret = check_free_space_common_key(leaf, key, slot);
+ ret = check_free_space_common_key(leaf, key, slot, prev_key);
if (unlikely(ret < 0))
return ret;
@@ -2200,13 +2252,14 @@ static int check_free_space_extent(struct extent_buffer *leaf, struct btrfs_key
}
static int check_free_space_bitmap(struct extent_buffer *leaf,
- struct btrfs_key *key, int slot)
+ struct btrfs_key *key, int slot,
+ struct btrfs_key *prev_key)
{
struct btrfs_fs_info *fs_info = leaf->fs_info;
u32 expected_item_size;
int ret;
- ret = check_free_space_common_key(leaf, key, slot);
+ ret = check_free_space_common_key(leaf, key, slot, prev_key);
if (unlikely(ret < 0))
return ret;
@@ -2298,10 +2351,10 @@ static enum btrfs_tree_block_status check_leaf_item(struct extent_buffer *leaf,
ret = check_free_space_info(leaf, key, slot);
break;
case BTRFS_FREE_SPACE_EXTENT_KEY:
- ret = check_free_space_extent(leaf, key, slot);
+ ret = check_free_space_extent(leaf, key, slot, prev_key);
break;
case BTRFS_FREE_SPACE_BITMAP_KEY:
- ret = check_free_space_bitmap(leaf, key, slot);
+ ret = check_free_space_bitmap(leaf, key, slot, prev_key);
break;
case BTRFS_IDENTITY_REMAP_KEY:
case BTRFS_REMAP_KEY: