Commit 6eda71d0 authored by Liu Bo's avatar Liu Bo Committed by Chris Mason

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: default avatarKonstantinos Skarlatos <k.skarlatos@gmail.com>
Signed-off-by: default avatarLiu Bo <bo.li.liu@oracle.com>
Signed-off-by: default avatarChris Mason <clm@fb.com>
parent 7ffbb598
...@@ -1409,9 +1409,10 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical, ...@@ -1409,9 +1409,10 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
* returns <0 on error * returns <0 on error
*/ */
static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb, static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb,
struct btrfs_extent_item *ei, u32 item_size, struct btrfs_key *key,
struct btrfs_extent_inline_ref **out_eiref, struct btrfs_extent_item *ei, u32 item_size,
int *out_type) struct btrfs_extent_inline_ref **out_eiref,
int *out_type)
{ {
unsigned long end; unsigned long end;
u64 flags; u64 flags;
...@@ -1421,9 +1422,16 @@ static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb, ...@@ -1421,9 +1422,16 @@ static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb,
/* first call */ /* first call */
flags = btrfs_extent_flags(eb, ei); flags = btrfs_extent_flags(eb, ei);
if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
info = (struct btrfs_tree_block_info *)(ei + 1); if (key->type == BTRFS_METADATA_ITEM_KEY) {
*out_eiref = /* a skinny metadata extent */
(struct btrfs_extent_inline_ref *)(info + 1); *out_eiref =
(struct btrfs_extent_inline_ref *)(ei + 1);
} else {
WARN_ON(key->type != BTRFS_EXTENT_ITEM_KEY);
info = (struct btrfs_tree_block_info *)(ei + 1);
*out_eiref =
(struct btrfs_extent_inline_ref *)(info + 1);
}
} else { } else {
*out_eiref = (struct btrfs_extent_inline_ref *)(ei + 1); *out_eiref = (struct btrfs_extent_inline_ref *)(ei + 1);
} }
...@@ -1433,7 +1441,7 @@ static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb, ...@@ -1433,7 +1441,7 @@ static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb,
} }
end = (unsigned long)ei + item_size; end = (unsigned long)ei + item_size;
*out_eiref = (struct btrfs_extent_inline_ref *)*ptr; *out_eiref = (struct btrfs_extent_inline_ref *)(*ptr);
*out_type = btrfs_extent_inline_ref_type(eb, *out_eiref); *out_type = btrfs_extent_inline_ref_type(eb, *out_eiref);
*ptr += btrfs_extent_inline_ref_size(*out_type); *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,8 +1460,8 @@ static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb,
* <0 on error. * <0 on error.
*/ */
int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb, int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
struct btrfs_extent_item *ei, u32 item_size, struct btrfs_key *key, struct btrfs_extent_item *ei,
u64 *out_root, u8 *out_level) u32 item_size, u64 *out_root, u8 *out_level)
{ {
int ret; int ret;
int type; int type;
...@@ -1464,8 +1472,8 @@ int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb, ...@@ -1464,8 +1472,8 @@ int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
return 1; return 1;
while (1) { while (1) {
ret = __get_extent_inline_ref(ptr, eb, ei, item_size, ret = __get_extent_inline_ref(ptr, eb, key, ei, item_size,
&eiref, &type); &eiref, &type);
if (ret < 0) if (ret < 0)
return ret; return ret;
......
...@@ -40,8 +40,8 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical, ...@@ -40,8 +40,8 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
u64 *flags); u64 *flags);
int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb, int tree_backref_for_extent(unsigned long *ptr, struct extent_buffer *eb,
struct btrfs_extent_item *ei, u32 item_size, struct btrfs_key *key, struct btrfs_extent_item *ei,
u64 *out_root, u8 *out_level); u32 item_size, u64 *out_root, u8 *out_level);
int iterate_extent_inodes(struct btrfs_fs_info *fs_info, int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
u64 extent_item_objectid, u64 extent_item_objectid,
......
...@@ -588,8 +588,9 @@ static void scrub_print_warning(const char *errstr, struct scrub_block *sblock) ...@@ -588,8 +588,9 @@ static void scrub_print_warning(const char *errstr, struct scrub_block *sblock)
if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
do { do {
ret = tree_backref_for_extent(&ptr, eb, ei, item_size, ret = tree_backref_for_extent(&ptr, eb, &found_key, ei,
&ref_root, &ref_level); item_size, &ref_root,
&ref_level);
printk_in_rcu(KERN_WARNING printk_in_rcu(KERN_WARNING
"BTRFS: %s at logical %llu on dev %s, " "BTRFS: %s at logical %llu on dev %s, "
"sector %llu: metadata %s (level %d) in tree " "sector %llu: metadata %s (level %d) in tree "
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment