Btrfs: tree-checker: detect file extent items with overlapping ranges
Having file extent items with ranges that overlap each other is a serious issue that leads to all sorts of corruptions and crashes (like a BUG_ON() during the course of __btrfs_drop_extents() when it traims file extent items). Therefore teach the tree checker to detect such cases. This is motivated by a recently fixed bug (race between ranged full fsync and writeback or adjacent ranges). Reviewed-by: Josef Bacik <josef@toxicpanda.com> Reviewed-by: Qu Wenruo <wqu@suse.com> Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
0c713cbab6
commit
4e9845eff5
|
@ -107,8 +107,26 @@ static void file_extent_err(const struct extent_buffer *eb, int slot,
|
||||||
(!IS_ALIGNED(btrfs_file_extent_##name((leaf), (fi)), (alignment))); \
|
(!IS_ALIGNED(btrfs_file_extent_##name((leaf), (fi)), (alignment))); \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
static u64 file_extent_end(struct extent_buffer *leaf,
|
||||||
|
struct btrfs_key *key,
|
||||||
|
struct btrfs_file_extent_item *extent)
|
||||||
|
{
|
||||||
|
u64 end;
|
||||||
|
u64 len;
|
||||||
|
|
||||||
|
if (btrfs_file_extent_type(leaf, extent) == BTRFS_FILE_EXTENT_INLINE) {
|
||||||
|
len = btrfs_file_extent_ram_bytes(leaf, extent);
|
||||||
|
end = ALIGN(key->offset + len, leaf->fs_info->sectorsize);
|
||||||
|
} else {
|
||||||
|
len = btrfs_file_extent_num_bytes(leaf, extent);
|
||||||
|
end = key->offset + len;
|
||||||
|
}
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
static int check_extent_data_item(struct extent_buffer *leaf,
|
static int check_extent_data_item(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;
|
struct btrfs_fs_info *fs_info = leaf->fs_info;
|
||||||
struct btrfs_file_extent_item *fi;
|
struct btrfs_file_extent_item *fi;
|
||||||
|
@ -188,6 +206,28 @@ static int check_extent_data_item(struct extent_buffer *leaf,
|
||||||
CHECK_FE_ALIGNED(leaf, slot, fi, offset, sectorsize) ||
|
CHECK_FE_ALIGNED(leaf, slot, fi, offset, sectorsize) ||
|
||||||
CHECK_FE_ALIGNED(leaf, slot, fi, num_bytes, sectorsize))
|
CHECK_FE_ALIGNED(leaf, slot, fi, num_bytes, sectorsize))
|
||||||
return -EUCLEAN;
|
return -EUCLEAN;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check that no two consecutive file extent items, in the same leaf,
|
||||||
|
* present ranges that overlap each other.
|
||||||
|
*/
|
||||||
|
if (slot > 0 &&
|
||||||
|
prev_key->objectid == key->objectid &&
|
||||||
|
prev_key->type == BTRFS_EXTENT_DATA_KEY) {
|
||||||
|
struct btrfs_file_extent_item *prev_fi;
|
||||||
|
u64 prev_end;
|
||||||
|
|
||||||
|
prev_fi = btrfs_item_ptr(leaf, slot - 1,
|
||||||
|
struct btrfs_file_extent_item);
|
||||||
|
prev_end = file_extent_end(leaf, prev_key, prev_fi);
|
||||||
|
if (prev_end > key->offset) {
|
||||||
|
file_extent_err(leaf, slot - 1,
|
||||||
|
"file extent end range (%llu) goes beyond start offset (%llu) of the next file extent",
|
||||||
|
prev_end, key->offset);
|
||||||
|
return -EUCLEAN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -774,14 +814,15 @@ static int check_inode_item(struct extent_buffer *leaf,
|
||||||
* Common point to switch the item-specific validation.
|
* Common point to switch the item-specific validation.
|
||||||
*/
|
*/
|
||||||
static int check_leaf_item(struct extent_buffer *leaf,
|
static int check_leaf_item(struct extent_buffer *leaf,
|
||||||
struct btrfs_key *key, int slot)
|
struct btrfs_key *key, int slot,
|
||||||
|
struct btrfs_key *prev_key)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct btrfs_chunk *chunk;
|
struct btrfs_chunk *chunk;
|
||||||
|
|
||||||
switch (key->type) {
|
switch (key->type) {
|
||||||
case BTRFS_EXTENT_DATA_KEY:
|
case BTRFS_EXTENT_DATA_KEY:
|
||||||
ret = check_extent_data_item(leaf, key, slot);
|
ret = check_extent_data_item(leaf, key, slot, prev_key);
|
||||||
break;
|
break;
|
||||||
case BTRFS_EXTENT_CSUM_KEY:
|
case BTRFS_EXTENT_CSUM_KEY:
|
||||||
ret = check_csum_item(leaf, key, slot);
|
ret = check_csum_item(leaf, key, slot);
|
||||||
|
@ -928,7 +969,7 @@ static int check_leaf(struct extent_buffer *leaf, bool check_item_data)
|
||||||
* Check if the item size and content meet other
|
* Check if the item size and content meet other
|
||||||
* criteria
|
* criteria
|
||||||
*/
|
*/
|
||||||
ret = check_leaf_item(leaf, &key, slot);
|
ret = check_leaf_item(leaf, &key, slot, &prev_key);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue