diff options
author | Qu Wenruo <wqu@suse.com> | 2018-07-31 22:37:17 -0400 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2018-08-06 07:13:03 -0400 |
commit | 7ef49515fa6727cb4b6f2f5b0ffbc5fc20a9f8c6 (patch) | |
tree | 2013a3d439113c58479b799561e07dfb5d072b02 | |
parent | 514c7dca85a0bf40be984dab0b477403a6db901f (diff) |
btrfs: Verify that every chunk has corresponding block group at mount time
If a crafted image has missing block group items, it could cause
unexpected behavior and breaks the assumption of 1:1 chunk<->block group
mapping.
Although we have the block group -> chunk mapping check, we still need
chunk -> block group mapping check.
This patch will do extra check to ensure each chunk has its
corresponding block group.
Link: https://bugzilla.kernel.org/show_bug.cgi?id=199847
Reported-by: Xu Wen <wen.xu@gatech.edu>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: Gu Jinxiang <gujx@cn.fujitsu.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r-- | fs/btrfs/extent-tree.c | 58 |
1 files changed, 57 insertions, 1 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 54a0cfef5c5c..de6f75f5547b 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -9844,6 +9844,62 @@ btrfs_create_block_group_cache(struct btrfs_fs_info *fs_info, | |||
9844 | return cache; | 9844 | return cache; |
9845 | } | 9845 | } |
9846 | 9846 | ||
9847 | |||
9848 | /* | ||
9849 | * Iterate all chunks and verify that each of them has the corresponding block | ||
9850 | * group | ||
9851 | */ | ||
9852 | static int check_chunk_block_group_mappings(struct btrfs_fs_info *fs_info) | ||
9853 | { | ||
9854 | struct btrfs_mapping_tree *map_tree = &fs_info->mapping_tree; | ||
9855 | struct extent_map *em; | ||
9856 | struct btrfs_block_group_cache *bg; | ||
9857 | u64 start = 0; | ||
9858 | int ret = 0; | ||
9859 | |||
9860 | while (1) { | ||
9861 | read_lock(&map_tree->map_tree.lock); | ||
9862 | /* | ||
9863 | * lookup_extent_mapping will return the first extent map | ||
9864 | * intersecting the range, so setting @len to 1 is enough to | ||
9865 | * get the first chunk. | ||
9866 | */ | ||
9867 | em = lookup_extent_mapping(&map_tree->map_tree, start, 1); | ||
9868 | read_unlock(&map_tree->map_tree.lock); | ||
9869 | if (!em) | ||
9870 | break; | ||
9871 | |||
9872 | bg = btrfs_lookup_block_group(fs_info, em->start); | ||
9873 | if (!bg) { | ||
9874 | btrfs_err(fs_info, | ||
9875 | "chunk start=%llu len=%llu doesn't have corresponding block group", | ||
9876 | em->start, em->len); | ||
9877 | ret = -EUCLEAN; | ||
9878 | free_extent_map(em); | ||
9879 | break; | ||
9880 | } | ||
9881 | if (bg->key.objectid != em->start || | ||
9882 | bg->key.offset != em->len || | ||
9883 | (bg->flags & BTRFS_BLOCK_GROUP_TYPE_MASK) != | ||
9884 | (em->map_lookup->type & BTRFS_BLOCK_GROUP_TYPE_MASK)) { | ||
9885 | btrfs_err(fs_info, | ||
9886 | "chunk start=%llu len=%llu flags=0x%llx doesn't match block group start=%llu len=%llu flags=0x%llx", | ||
9887 | em->start, em->len, | ||
9888 | em->map_lookup->type & BTRFS_BLOCK_GROUP_TYPE_MASK, | ||
9889 | bg->key.objectid, bg->key.offset, | ||
9890 | bg->flags & BTRFS_BLOCK_GROUP_TYPE_MASK); | ||
9891 | ret = -EUCLEAN; | ||
9892 | free_extent_map(em); | ||
9893 | btrfs_put_block_group(bg); | ||
9894 | break; | ||
9895 | } | ||
9896 | start = em->start + em->len; | ||
9897 | free_extent_map(em); | ||
9898 | btrfs_put_block_group(bg); | ||
9899 | } | ||
9900 | return ret; | ||
9901 | } | ||
9902 | |||
9847 | int btrfs_read_block_groups(struct btrfs_fs_info *info) | 9903 | int btrfs_read_block_groups(struct btrfs_fs_info *info) |
9848 | { | 9904 | { |
9849 | struct btrfs_path *path; | 9905 | struct btrfs_path *path; |
@@ -10010,7 +10066,7 @@ int btrfs_read_block_groups(struct btrfs_fs_info *info) | |||
10010 | 10066 | ||
10011 | btrfs_add_raid_kobjects(info); | 10067 | btrfs_add_raid_kobjects(info); |
10012 | init_global_block_rsv(info); | 10068 | init_global_block_rsv(info); |
10013 | ret = 0; | 10069 | ret = check_chunk_block_group_mappings(info); |
10014 | error: | 10070 | error: |
10015 | btrfs_free_path(path); | 10071 | btrfs_free_path(path); |
10016 | return ret; | 10072 | return ret; |