aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLiu Bo <bo.li.liu@oracle.com>2014-06-08 22:54:07 -0400
committerChris Mason <clm@fb.com>2014-06-09 20:21:17 -0400
commit6eda71d0c030af0fc2f68aaa676e6d445600855b (patch)
treef649b36ac2cf1762e3ac5a8d4bd6b1cb26d99963 /fs
parent7ffbb598a059b73487909619d73150f99b50337a (diff)
Btrfs: fix scrub_print_warning to handle skinny metadata extents
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>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/backref.c30
-rw-r--r--fs/btrfs/backref.h4
-rw-r--r--fs/btrfs/scrub.c5
3 files changed, 24 insertions, 15 deletions
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index 00f9d9f911ca..e25564bfcb46 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -1409,9 +1409,10 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
1409 * returns <0 on error 1409 * returns <0 on error
1410 */ 1410 */
1411static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb, 1411static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb,
1412 struct btrfs_extent_item *ei, u32 item_size, 1412 struct btrfs_key *key,
1413 struct btrfs_extent_inline_ref **out_eiref, 1413 struct btrfs_extent_item *ei, u32 item_size,
1414 int *out_type) 1414 struct btrfs_extent_inline_ref **out_eiref,
1415 int *out_type)
1415{ 1416{
1416 unsigned long end; 1417 unsigned long end;
1417 u64 flags; 1418 u64 flags;
@@ -1421,9 +1422,16 @@ static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb,
1421 /* first call */ 1422 /* first call */
1422 flags = btrfs_extent_flags(eb, ei); 1423 flags = btrfs_extent_flags(eb, ei);
1423 if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { 1424 if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
1424 info = (struct btrfs_tree_block_info *)(ei + 1); 1425 if (key->type == BTRFS_METADATA_ITEM_KEY) {
1425 *out_eiref = 1426 /* a skinny metadata extent */
1426 (struct btrfs_extent_inline_ref *)(info + 1); 1427 *out_eiref =
1428 (struct btrfs_extent_inline_ref *)(ei + 1);
1429 } else {
1430 WARN_ON(key->type != BTRFS_EXTENT_ITEM_KEY);
1431 info = (struct btrfs_tree_block_info *)(ei + 1);
1432 *out_eiref =
1433 (struct btrfs_extent_inline_ref *)(info + 1);
1434 }
1427 } else { 1435 } else {
1428 *out_eiref = (struct btrfs_extent_inline_ref *)(ei + 1); 1436 *out_eiref = (struct btrfs_extent_inline_ref *)(ei + 1);
1429 } 1437 }
@@ -1433,7 +1441,7 @@ static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb,
1433 } 1441 }
1434 1442
1435 end = (unsigned long)ei + item_size; 1443 end = (unsigned long)ei + item_size;
1436 *out_eiref = (struct btrfs_extent_inline_ref *)*ptr; 1444 *out_eiref = (struct btrfs_extent_inline_ref *)(*ptr);
1437 *out_type = btrfs_extent_inline_ref_type(eb, *out_eiref); 1445 *out_type = btrfs_extent_inline_ref_type(eb, *out_eiref);
1438 1446
1439 *ptr += btrfs_extent_inline_ref_size(*out_type); 1447 *ptr += btrfs_extent_inline_ref_size(*out_type);
@@ -1452,8 +1460,8 @@ static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb,
1452 * <0 on error. 1460 * <0 on error.
1453 */ 1461 */
1454int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb, 1462int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
1455 struct btrfs_extent_item *ei, u32 item_size, 1463 struct btrfs_key *key, struct btrfs_extent_item *ei,
1456 u64 *out_root, u8 *out_level) 1464 u32 item_size, u64 *out_root, u8 *out_level)
1457{ 1465{
1458 int ret; 1466 int ret;
1459 int type; 1467 int type;
@@ -1464,8 +1472,8 @@ int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
1464 return 1; 1472 return 1;
1465 1473
1466 while (1) { 1474 while (1) {
1467 ret = __get_extent_inline_ref(ptr, eb, ei, item_size, 1475 ret = __get_extent_inline_ref(ptr, eb, key, ei, item_size,
1468 &eiref, &type); 1476 &eiref, &type);
1469 if (ret < 0) 1477 if (ret < 0)
1470 return ret; 1478 return ret;
1471 1479
diff --git a/fs/btrfs/backref.h b/fs/btrfs/backref.h
index 94e94429f3e9..86fc20fec282 100644
--- a/fs/btrfs/backref.h
+++ b/fs/btrfs/backref.h
@@ -40,8 +40,8 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
40 u64 *flags); 40 u64 *flags);
41 41
42int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb, 42int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
43 struct btrfs_extent_item *ei, u32 item_size, 43 struct btrfs_key *key, struct btrfs_extent_item *ei,
44 u64 *out_root, u8 *out_level); 44 u32 item_size, u64 *out_root, u8 *out_level);
45 45
46int iterate_extent_inodes(struct btrfs_fs_info *fs_info, 46int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
47 u64 extent_item_objectid, 47 u64 extent_item_objectid,
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index bd850b599a99..ac80188eec88 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -588,8 +588,9 @@ static void scrub_print_warning(const char *errstr, struct scrub_block *sblock)
588 588
589 if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { 589 if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
590 do { 590 do {
591 ret = tree_backref_for_extent(&ptr, eb, ei, item_size, 591 ret = tree_backref_for_extent(&ptr, eb, &found_key, ei,
592 &ref_root, &ref_level); 592 item_size, &ref_root,
593 &ref_level);
593 printk_in_rcu(KERN_WARNING 594 printk_in_rcu(KERN_WARNING
594 "BTRFS: %s at logical %llu on dev %s, " 595 "BTRFS: %s at logical %llu on dev %s, "
595 "sector %llu: metadata %s (level %d) in tree " 596 "sector %llu: metadata %s (level %d) in tree "