diff options
Diffstat (limited to 'fs/btrfs/volumes.c')
-rw-r--r-- | fs/btrfs/volumes.c | 82 |
1 files changed, 67 insertions, 15 deletions
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index fd5c9e69894a..74507b05061b 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c | |||
@@ -6251,27 +6251,23 @@ struct btrfs_device *btrfs_alloc_device(struct btrfs_fs_info *fs_info, | |||
6251 | return dev; | 6251 | return dev; |
6252 | } | 6252 | } |
6253 | 6253 | ||
6254 | static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key, | 6254 | /* Return -EIO if any error, otherwise return 0. */ |
6255 | struct extent_buffer *leaf, | 6255 | static int btrfs_check_chunk_valid(struct btrfs_root *root, |
6256 | struct btrfs_chunk *chunk) | 6256 | struct extent_buffer *leaf, |
6257 | struct btrfs_chunk *chunk, u64 logical) | ||
6257 | { | 6258 | { |
6258 | struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree; | ||
6259 | struct map_lookup *map; | ||
6260 | struct extent_map *em; | ||
6261 | u64 logical; | ||
6262 | u64 length; | 6259 | u64 length; |
6263 | u64 stripe_len; | 6260 | u64 stripe_len; |
6264 | u64 devid; | 6261 | u16 num_stripes; |
6265 | u8 uuid[BTRFS_UUID_SIZE]; | 6262 | u16 sub_stripes; |
6266 | int num_stripes; | 6263 | u64 type; |
6267 | int ret; | ||
6268 | int i; | ||
6269 | 6264 | ||
6270 | logical = key->offset; | ||
6271 | length = btrfs_chunk_length(leaf, chunk); | 6265 | length = btrfs_chunk_length(leaf, chunk); |
6272 | stripe_len = btrfs_chunk_stripe_len(leaf, chunk); | 6266 | stripe_len = btrfs_chunk_stripe_len(leaf, chunk); |
6273 | num_stripes = btrfs_chunk_num_stripes(leaf, chunk); | 6267 | num_stripes = btrfs_chunk_num_stripes(leaf, chunk); |
6274 | /* Validation check */ | 6268 | sub_stripes = btrfs_chunk_sub_stripes(leaf, chunk); |
6269 | type = btrfs_chunk_type(leaf, chunk); | ||
6270 | |||
6275 | if (!num_stripes) { | 6271 | if (!num_stripes) { |
6276 | btrfs_err(root->fs_info, "invalid chunk num_stripes: %u", | 6272 | btrfs_err(root->fs_info, "invalid chunk num_stripes: %u", |
6277 | num_stripes); | 6273 | num_stripes); |
@@ -6282,6 +6278,11 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key, | |||
6282 | "invalid chunk logical %llu", logical); | 6278 | "invalid chunk logical %llu", logical); |
6283 | return -EIO; | 6279 | return -EIO; |
6284 | } | 6280 | } |
6281 | if (btrfs_chunk_sector_size(leaf, chunk) != root->sectorsize) { | ||
6282 | btrfs_err(root->fs_info, "invalid chunk sectorsize %u", | ||
6283 | btrfs_chunk_sector_size(leaf, chunk)); | ||
6284 | return -EIO; | ||
6285 | } | ||
6285 | if (!length || !IS_ALIGNED(length, root->sectorsize)) { | 6286 | if (!length || !IS_ALIGNED(length, root->sectorsize)) { |
6286 | btrfs_err(root->fs_info, | 6287 | btrfs_err(root->fs_info, |
6287 | "invalid chunk length %llu", length); | 6288 | "invalid chunk length %llu", length); |
@@ -6293,13 +6294,54 @@ static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key, | |||
6293 | return -EIO; | 6294 | return -EIO; |
6294 | } | 6295 | } |
6295 | if (~(BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_BLOCK_GROUP_PROFILE_MASK) & | 6296 | if (~(BTRFS_BLOCK_GROUP_TYPE_MASK | BTRFS_BLOCK_GROUP_PROFILE_MASK) & |
6296 | btrfs_chunk_type(leaf, chunk)) { | 6297 | type) { |
6297 | btrfs_err(root->fs_info, "unrecognized chunk type: %llu", | 6298 | btrfs_err(root->fs_info, "unrecognized chunk type: %llu", |
6298 | ~(BTRFS_BLOCK_GROUP_TYPE_MASK | | 6299 | ~(BTRFS_BLOCK_GROUP_TYPE_MASK | |
6299 | BTRFS_BLOCK_GROUP_PROFILE_MASK) & | 6300 | BTRFS_BLOCK_GROUP_PROFILE_MASK) & |
6300 | btrfs_chunk_type(leaf, chunk)); | 6301 | btrfs_chunk_type(leaf, chunk)); |
6301 | return -EIO; | 6302 | return -EIO; |
6302 | } | 6303 | } |
6304 | if ((type & BTRFS_BLOCK_GROUP_RAID10 && sub_stripes != 2) || | ||
6305 | (type & BTRFS_BLOCK_GROUP_RAID1 && num_stripes < 1) || | ||
6306 | (type & BTRFS_BLOCK_GROUP_RAID5 && num_stripes < 2) || | ||
6307 | (type & BTRFS_BLOCK_GROUP_RAID6 && num_stripes < 3) || | ||
6308 | (type & BTRFS_BLOCK_GROUP_DUP && num_stripes > 2) || | ||
6309 | ((type & BTRFS_BLOCK_GROUP_PROFILE_MASK) == 0 && | ||
6310 | num_stripes != 1)) { | ||
6311 | btrfs_err(root->fs_info, | ||
6312 | "invalid num_stripes:sub_stripes %u:%u for profile %llu", | ||
6313 | num_stripes, sub_stripes, | ||
6314 | type & BTRFS_BLOCK_GROUP_PROFILE_MASK); | ||
6315 | return -EIO; | ||
6316 | } | ||
6317 | |||
6318 | return 0; | ||
6319 | } | ||
6320 | |||
6321 | static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key, | ||
6322 | struct extent_buffer *leaf, | ||
6323 | struct btrfs_chunk *chunk) | ||
6324 | { | ||
6325 | struct btrfs_mapping_tree *map_tree = &root->fs_info->mapping_tree; | ||
6326 | struct map_lookup *map; | ||
6327 | struct extent_map *em; | ||
6328 | u64 logical; | ||
6329 | u64 length; | ||
6330 | u64 stripe_len; | ||
6331 | u64 devid; | ||
6332 | u8 uuid[BTRFS_UUID_SIZE]; | ||
6333 | int num_stripes; | ||
6334 | int ret; | ||
6335 | int i; | ||
6336 | |||
6337 | logical = key->offset; | ||
6338 | length = btrfs_chunk_length(leaf, chunk); | ||
6339 | stripe_len = btrfs_chunk_stripe_len(leaf, chunk); | ||
6340 | num_stripes = btrfs_chunk_num_stripes(leaf, chunk); | ||
6341 | |||
6342 | ret = btrfs_check_chunk_valid(root, leaf, chunk, logical); | ||
6343 | if (ret) | ||
6344 | return ret; | ||
6303 | 6345 | ||
6304 | read_lock(&map_tree->map_tree.lock); | 6346 | read_lock(&map_tree->map_tree.lock); |
6305 | em = lookup_extent_mapping(&map_tree->map_tree, logical, 1); | 6347 | em = lookup_extent_mapping(&map_tree->map_tree, logical, 1); |
@@ -6547,6 +6589,7 @@ int btrfs_read_sys_array(struct btrfs_root *root) | |||
6547 | u32 array_size; | 6589 | u32 array_size; |
6548 | u32 len = 0; | 6590 | u32 len = 0; |
6549 | u32 cur_offset; | 6591 | u32 cur_offset; |
6592 | u64 type; | ||
6550 | struct btrfs_key key; | 6593 | struct btrfs_key key; |
6551 | 6594 | ||
6552 | ASSERT(BTRFS_SUPER_INFO_SIZE <= root->nodesize); | 6595 | ASSERT(BTRFS_SUPER_INFO_SIZE <= root->nodesize); |
@@ -6613,6 +6656,15 @@ int btrfs_read_sys_array(struct btrfs_root *root) | |||
6613 | break; | 6656 | break; |
6614 | } | 6657 | } |
6615 | 6658 | ||
6659 | type = btrfs_chunk_type(sb, chunk); | ||
6660 | if ((type & BTRFS_BLOCK_GROUP_SYSTEM) == 0) { | ||
6661 | btrfs_err(root->fs_info, | ||
6662 | "invalid chunk type %llu in sys_array at offset %u", | ||
6663 | type, cur_offset); | ||
6664 | ret = -EIO; | ||
6665 | break; | ||
6666 | } | ||
6667 | |||
6616 | len = btrfs_chunk_item_size(num_stripes); | 6668 | len = btrfs_chunk_item_size(num_stripes); |
6617 | if (cur_offset + len > array_size) | 6669 | if (cur_offset + len > array_size) |
6618 | goto out_short_read; | 6670 | goto out_short_read; |