Commit eed31172a351856ad18081f501946e1670b6a1f6
Committed by
Greg Kroah-Hartman
1 parent
6d2a63f3d1
Btrfs: fix scrub_print_warning to handle skinny metadata extents
commit 6eda71d0c030af0fc2f68aaa676e6d445600855b upstream. The skinny extents are intepreted incorrectly in scrub_print_warning(), and end up hitting the BUG() in btrfs_extent_inline_ref_size. Reported-by: Konstantinos Skarlatos <k.skarlatos@gmail.com> Signed-off-by: Liu Bo <bo.li.liu@oracle.com> Signed-off-by: Chris Mason <clm@fb.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Showing 3 changed files with 24 additions and 15 deletions Side-by-side Diff
fs/btrfs/backref.c
| ... | ... | @@ -1405,9 +1405,10 @@ |
| 1405 | 1405 | * returns <0 on error |
| 1406 | 1406 | */ |
| 1407 | 1407 | static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb, |
| 1408 | - struct btrfs_extent_item *ei, u32 item_size, | |
| 1409 | - struct btrfs_extent_inline_ref **out_eiref, | |
| 1410 | - int *out_type) | |
| 1408 | + struct btrfs_key *key, | |
| 1409 | + struct btrfs_extent_item *ei, u32 item_size, | |
| 1410 | + struct btrfs_extent_inline_ref **out_eiref, | |
| 1411 | + int *out_type) | |
| 1411 | 1412 | { |
| 1412 | 1413 | unsigned long end; |
| 1413 | 1414 | u64 flags; |
| ... | ... | @@ -1417,9 +1418,16 @@ |
| 1417 | 1418 | /* first call */ |
| 1418 | 1419 | flags = btrfs_extent_flags(eb, ei); |
| 1419 | 1420 | if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { |
| 1420 | - info = (struct btrfs_tree_block_info *)(ei + 1); | |
| 1421 | - *out_eiref = | |
| 1422 | - (struct btrfs_extent_inline_ref *)(info + 1); | |
| 1421 | + if (key->type == BTRFS_METADATA_ITEM_KEY) { | |
| 1422 | + /* a skinny metadata extent */ | |
| 1423 | + *out_eiref = | |
| 1424 | + (struct btrfs_extent_inline_ref *)(ei + 1); | |
| 1425 | + } else { | |
| 1426 | + WARN_ON(key->type != BTRFS_EXTENT_ITEM_KEY); | |
| 1427 | + info = (struct btrfs_tree_block_info *)(ei + 1); | |
| 1428 | + *out_eiref = | |
| 1429 | + (struct btrfs_extent_inline_ref *)(info + 1); | |
| 1430 | + } | |
| 1423 | 1431 | } else { |
| 1424 | 1432 | *out_eiref = (struct btrfs_extent_inline_ref *)(ei + 1); |
| 1425 | 1433 | } |
| ... | ... | @@ -1429,7 +1437,7 @@ |
| 1429 | 1437 | } |
| 1430 | 1438 | |
| 1431 | 1439 | end = (unsigned long)ei + item_size; |
| 1432 | - *out_eiref = (struct btrfs_extent_inline_ref *)*ptr; | |
| 1440 | + *out_eiref = (struct btrfs_extent_inline_ref *)(*ptr); | |
| 1433 | 1441 | *out_type = btrfs_extent_inline_ref_type(eb, *out_eiref); |
| 1434 | 1442 | |
| 1435 | 1443 | *ptr += btrfs_extent_inline_ref_size(*out_type); |
| ... | ... | @@ -1448,8 +1456,8 @@ |
| 1448 | 1456 | * <0 on error. |
| 1449 | 1457 | */ |
| 1450 | 1458 | int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb, |
| 1451 | - struct btrfs_extent_item *ei, u32 item_size, | |
| 1452 | - u64 *out_root, u8 *out_level) | |
| 1459 | + struct btrfs_key *key, struct btrfs_extent_item *ei, | |
| 1460 | + u32 item_size, u64 *out_root, u8 *out_level) | |
| 1453 | 1461 | { |
| 1454 | 1462 | int ret; |
| 1455 | 1463 | int type; |
| ... | ... | @@ -1460,8 +1468,8 @@ |
| 1460 | 1468 | return 1; |
| 1461 | 1469 | |
| 1462 | 1470 | while (1) { |
| 1463 | - ret = __get_extent_inline_ref(ptr, eb, ei, item_size, | |
| 1464 | - &eiref, &type); | |
| 1471 | + ret = __get_extent_inline_ref(ptr, eb, key, ei, item_size, | |
| 1472 | + &eiref, &type); | |
| 1465 | 1473 | if (ret < 0) |
| 1466 | 1474 | return ret; |
| 1467 | 1475 |
fs/btrfs/backref.h
| ... | ... | @@ -40,8 +40,8 @@ |
| 40 | 40 | u64 *flags); |
| 41 | 41 | |
| 42 | 42 | int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb, |
| 43 | - struct btrfs_extent_item *ei, u32 item_size, | |
| 44 | - u64 *out_root, u8 *out_level); | |
| 43 | + struct btrfs_key *key, struct btrfs_extent_item *ei, | |
| 44 | + u32 item_size, u64 *out_root, u8 *out_level); | |
| 45 | 45 | |
| 46 | 46 | int iterate_extent_inodes(struct btrfs_fs_info *fs_info, |
| 47 | 47 | u64 extent_item_objectid, |
fs/btrfs/scrub.c
| ... | ... | @@ -588,8 +588,9 @@ |
| 588 | 588 | |
| 589 | 589 | if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { |
| 590 | 590 | do { |
| 591 | - ret = tree_backref_for_extent(&ptr, eb, ei, item_size, | |
| 592 | - &ref_root, &ref_level); | |
| 591 | + ret = tree_backref_for_extent(&ptr, eb, &found_key, ei, | |
| 592 | + item_size, &ref_root, | |
| 593 | + &ref_level); | |
| 593 | 594 | printk_in_rcu(KERN_WARNING |
| 594 | 595 | "BTRFS: %s at logical %llu on dev %s, " |
| 595 | 596 | "sector %llu: metadata %s (level %d) in tree " |