Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+19 −11
Original line number Original line Diff line number Diff line
@@ -1409,6 +1409,7 @@ 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_key *key,
				   struct btrfs_extent_item *ei, u32 item_size,
				   struct btrfs_extent_item *ei, u32 item_size,
				   struct btrfs_extent_inline_ref **out_eiref,
				   struct btrfs_extent_inline_ref **out_eiref,
				   int *out_type)
				   int *out_type)
@@ -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) {
			if (key->type == BTRFS_METADATA_ITEM_KEY) {
				/* a skinny metadata extent */
				*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);
				info = (struct btrfs_tree_block_info *)(ei + 1);
				*out_eiref =
				*out_eiref =
				   (struct btrfs_extent_inline_ref *)(info + 1);
				   (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,
	}
	}


	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,
 * <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,7 +1472,7 @@ 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;
+2 −2
Original line number Original line Diff line number Diff line
@@ -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,
+3 −2
Original line number Original line Diff line number Diff line
@@ -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 "