diff mbox

[RFC,04/16] btrfs-progs: fsck: Introduce function to check referencer of a backref

Message ID 1461642543-4621-5-git-send-email-quwenruo@cn.fujitsu.com (mailing list archive)
State New, archived
Headers show

Commit Message

Qu Wenruo April 26, 2016, 3:48 a.m. UTC
From: Lu Fengqi <lufq.fnst@cn.fujitsu.com>

Introduce a new function check_tree_block_backref() to check if a
backref points to correct referencer.

Signed-off-by: Lu Fengqi <lufq.fnst@cn.fujitsu.com>
Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
---
 cmds-check.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)

Comments

Josef Bacik April 28, 2016, 2:17 p.m. UTC | #1
On 04/25/2016 11:48 PM, Qu Wenruo wrote:
> From: Lu Fengqi <lufq.fnst@cn.fujitsu.com>
>
> Introduce a new function check_tree_block_backref() to check if a
> backref points to correct referencer.
>
> Signed-off-by: Lu Fengqi <lufq.fnst@cn.fujitsu.com>
> Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
> ---
>  cmds-check.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 95 insertions(+)
>
> diff --git a/cmds-check.c b/cmds-check.c
> index 6633b6e..81dd4f3 100644
> --- a/cmds-check.c
> +++ b/cmds-check.c
> @@ -323,6 +323,8 @@ struct root_item_info {
>  #define MISSING_BACKREF	(1 << 0) /* Completely no backref in extent tree */
>  #define BAD_BACKREF	(1 << 1) /* Backref mismatch */
>  #define UNALIGNED_BYTES	(1 << 2) /* Some bytes are not aligned */
> +#define MISSING_REFERENCER (1 << 3) /* Referencer not found */
> +#define BAD_REFERENCER	(1 << 4) /* Referencer found, but not mismatch */
>
>  static void *print_status_check(void *p)
>  {
> @@ -8736,6 +8738,99 @@ out:
>  	return ret;
>  }
>
> +/*
> + * Check if a tree block backref is valid (points to valid tree block)
> + * if level == -1, level will be resolved
> + */
> +static int check_tree_block_backref(struct btrfs_fs_info *fs_info, u64 root_id,
> +				    u64 bytenr, int level)
> +{
> +	struct btrfs_root *root;
> +	struct btrfs_key key;
> +	struct btrfs_path path;
> +	struct extent_buffer *eb;
> +	struct extent_buffer *node;
> +	u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
> +	int err = 0;
> +	int ret;
> +
> +	/* Query level for level == -1 special case */
> +	if (level == -1)
> +		level = query_tree_block_level(fs_info, bytenr);
> +	if (level < 0) {
> +		err = MISSING_REFERENCER;
> +		goto out;
> +	}
> +
> +	key.objectid = root_id;
> +	key.type = BTRFS_ROOT_ITEM_KEY;
> +	key.offset = (u64)-1;
> +
> +	root = btrfs_read_fs_root(fs_info, &key);
> +	if (IS_ERR(root)) {
> +		err |= MISSING_REFERENCER;
> +		goto out;
> +	}
> +
> +	/* Read out the tree block to get item/node key */
> +	eb = read_tree_block(root, bytenr, root->nodesize, 0);
> +	/* Impossible, as tree block query has read out the tree block */
> +	if (!extent_buffer_uptodate(eb)) {
> +		err |= MISSING_REFERENCER;
> +		free_extent_buffer(eb);
> +		goto out;
> +	}
> +
> +	/* Empty tree, no need to check key */
> +	if (!btrfs_header_nritems(eb) && !level) {
> +		free_extent_buffer(eb);
> +		goto out;
> +	}

Actually this is interesting, because we don't actually catch where we 
would have btrfs_header_nritems(eb) == 0 and !level.  So maybe integrate 
that check into check_block, to make sure we have items in node level 
extent buffers.  Thanks,

Josef
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Josef Bacik April 28, 2016, 2:31 p.m. UTC | #2
On 04/25/2016 11:48 PM, Qu Wenruo wrote:
> From: Lu Fengqi <lufq.fnst@cn.fujitsu.com>
>
> Introduce a new function check_tree_block_backref() to check if a
> backref points to correct referencer.
>
> Signed-off-by: Lu Fengqi <lufq.fnst@cn.fujitsu.com>
> Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
> ---
>  cmds-check.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 95 insertions(+)
>
> diff --git a/cmds-check.c b/cmds-check.c
> index 6633b6e..81dd4f3 100644
> --- a/cmds-check.c
> +++ b/cmds-check.c
> @@ -323,6 +323,8 @@ struct root_item_info {
>  #define MISSING_BACKREF	(1 << 0) /* Completely no backref in extent tree */
>  #define BAD_BACKREF	(1 << 1) /* Backref mismatch */
>  #define UNALIGNED_BYTES	(1 << 2) /* Some bytes are not aligned */
> +#define MISSING_REFERENCER (1 << 3) /* Referencer not found */
> +#define BAD_REFERENCER	(1 << 4) /* Referencer found, but not mismatch */
>
>  static void *print_status_check(void *p)
>  {
> @@ -8736,6 +8738,99 @@ out:
>  	return ret;
>  }
>
> +/*
> + * Check if a tree block backref is valid (points to valid tree block)
> + * if level == -1, level will be resolved
> + */
> +static int check_tree_block_backref(struct btrfs_fs_info *fs_info, u64 root_id,
> +				    u64 bytenr, int level)
> +{
> +	struct btrfs_root *root;
> +	struct btrfs_key key;
> +	struct btrfs_path path;
> +	struct extent_buffer *eb;
> +	struct extent_buffer *node;
> +	u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
> +	int err = 0;
> +	int ret;
> +
> +	/* Query level for level == -1 special case */
> +	if (level == -1)
> +		level = query_tree_block_level(fs_info, bytenr);
> +	if (level < 0) {
> +		err = MISSING_REFERENCER;
> +		goto out;
> +	}
> +
> +	key.objectid = root_id;
> +	key.type = BTRFS_ROOT_ITEM_KEY;
> +	key.offset = (u64)-1;
> +
> +	root = btrfs_read_fs_root(fs_info, &key);
> +	if (IS_ERR(root)) {
> +		err |= MISSING_REFERENCER;
> +		goto out;
> +	}
> +
> +	/* Read out the tree block to get item/node key */
> +	eb = read_tree_block(root, bytenr, root->nodesize, 0);
> +	/* Impossible, as tree block query has read out the tree block */
> +	if (!extent_buffer_uptodate(eb)) {
> +		err |= MISSING_REFERENCER;
> +		free_extent_buffer(eb);
> +		goto out;
> +	}
> +
> +	/* Empty tree, no need to check key */
> +	if (!btrfs_header_nritems(eb) && !level) {
> +		free_extent_buffer(eb);
> +		goto out;
> +	}
> +
> +	if (level)
> +		btrfs_node_key_to_cpu(eb, &key, 0);
> +	else
> +		btrfs_item_key_to_cpu(eb, &key, 0);
> +
> +	free_extent_buffer(eb);
> +
> +	btrfs_init_path(&path);
> +	/* Search with the first key, to ensure we can reach it */
> +	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
> +	if (ret) {
> +		err |= MISSING_REFERENCER;
> +		goto release_out;
> +	}
> +
> +	node = path.nodes[level];
> +	if (btrfs_header_bytenr(node) != bytenr) {
> +		error("Extent [%llu %d] referencer bytenr mismatch, wanted: %llu, have: %llu",
> +		      bytenr, nodesize, bytenr,
> +		      btrfs_header_bytenr(node));
> +		err |= BAD_REFERENCER;
> +	}
> +	if (btrfs_header_level(node) != level) {
> +		error("Extent [%llu %d] referencer level mismatch, wanted: %d, have: %d",
> +		      bytenr, nodesize, level,
> +		      btrfs_header_level(node));
> +		err |= BAD_REFERENCER;
> +	}
> +
> +release_out:
> +	btrfs_release_path(&path);
> +out:
> +	if (err & MISSING_REFERENCER) {
> +		if (level < 0)
> +			error("Extent [%llu %d] lost referencer(owner: %llu)",
> +			       bytenr, nodesize, root_id);
> +		else
> +			error("Extent [%llu %d] lost referencer(owner: %llu, level: %u)",
> +			       bytenr, nodesize, root_id, level);
> +	}
> +
> +	return -err;

Sigh I missed this at first, if you are doing flags don't return 
negative numbers.  Fix this and then you can add my reviewed-by.  Thanks,

Josef

--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Qu Wenruo April 29, 2016, 5:31 a.m. UTC | #3
Josef Bacik wrote on 2016/04/28 10:17 -0400:
> On 04/25/2016 11:48 PM, Qu Wenruo wrote:
>> From: Lu Fengqi <lufq.fnst@cn.fujitsu.com>
>>
>> Introduce a new function check_tree_block_backref() to check if a
>> backref points to correct referencer.
>>
>> Signed-off-by: Lu Fengqi <lufq.fnst@cn.fujitsu.com>
>> Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
>> ---
>>  cmds-check.c | 95
>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 95 insertions(+)
>>
>> diff --git a/cmds-check.c b/cmds-check.c
>> index 6633b6e..81dd4f3 100644
>> --- a/cmds-check.c
>> +++ b/cmds-check.c
>> @@ -323,6 +323,8 @@ struct root_item_info {
>>  #define MISSING_BACKREF    (1 << 0) /* Completely no backref in
>> extent tree */
>>  #define BAD_BACKREF    (1 << 1) /* Backref mismatch */
>>  #define UNALIGNED_BYTES    (1 << 2) /* Some bytes are not aligned */
>> +#define MISSING_REFERENCER (1 << 3) /* Referencer not found */
>> +#define BAD_REFERENCER    (1 << 4) /* Referencer found, but not
>> mismatch */
>>
>>  static void *print_status_check(void *p)
>>  {
>> @@ -8736,6 +8738,99 @@ out:
>>      return ret;
>>  }
>>
>> +/*
>> + * Check if a tree block backref is valid (points to valid tree block)
>> + * if level == -1, level will be resolved
>> + */
>> +static int check_tree_block_backref(struct btrfs_fs_info *fs_info,
>> u64 root_id,
>> +                    u64 bytenr, int level)
>> +{
>> +    struct btrfs_root *root;
>> +    struct btrfs_key key;
>> +    struct btrfs_path path;
>> +    struct extent_buffer *eb;
>> +    struct extent_buffer *node;
>> +    u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
>> +    int err = 0;
>> +    int ret;
>> +
>> +    /* Query level for level == -1 special case */
>> +    if (level == -1)
>> +        level = query_tree_block_level(fs_info, bytenr);
>> +    if (level < 0) {
>> +        err = MISSING_REFERENCER;
>> +        goto out;
>> +    }
>> +
>> +    key.objectid = root_id;
>> +    key.type = BTRFS_ROOT_ITEM_KEY;
>> +    key.offset = (u64)-1;
>> +
>> +    root = btrfs_read_fs_root(fs_info, &key);
>> +    if (IS_ERR(root)) {
>> +        err |= MISSING_REFERENCER;
>> +        goto out;
>> +    }
>> +
>> +    /* Read out the tree block to get item/node key */
>> +    eb = read_tree_block(root, bytenr, root->nodesize, 0);
>> +    /* Impossible, as tree block query has read out the tree block */
>> +    if (!extent_buffer_uptodate(eb)) {
>> +        err |= MISSING_REFERENCER;
>> +        free_extent_buffer(eb);
>> +        goto out;
>> +    }
>> +
>> +    /* Empty tree, no need to check key */
>> +    if (!btrfs_header_nritems(eb) && !level) {
>> +        free_extent_buffer(eb);
>> +        goto out;
>> +    }
>
> Actually this is interesting, because we don't actually catch where we
> would have btrfs_header_nritems(eb) == 0 and !level.  So maybe integrate
> that check into check_block, to make sure we have items in node level
> extent buffers.  Thanks,
>
> Josef
>
>
Unfortunately, we can't integrate it into check_tree_block() (or caller 
read_tree_block() and its variants).

As this is a valid case.
(And that's why we don't report error here)

For trees like csum tree and uuid tree, they can be empty, and it 
doesn't break any thing.

So it's a not something we need to integrate into check_tree_block().

And if you mean check_block() in old fsck codes, we won't use it in new 
fsck, so still not a big problem.

Thanks,
Qu


--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Josef Bacik April 29, 2016, 1:52 p.m. UTC | #4
On 04/29/2016 01:31 AM, Qu Wenruo wrote:
>
>
> Josef Bacik wrote on 2016/04/28 10:17 -0400:
>> On 04/25/2016 11:48 PM, Qu Wenruo wrote:
>>> From: Lu Fengqi <lufq.fnst@cn.fujitsu.com>
>>>
>>> Introduce a new function check_tree_block_backref() to check if a
>>> backref points to correct referencer.
>>>
>>> Signed-off-by: Lu Fengqi <lufq.fnst@cn.fujitsu.com>
>>> Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
>>> ---
>>>  cmds-check.c | 95
>>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>  1 file changed, 95 insertions(+)
>>>
>>> diff --git a/cmds-check.c b/cmds-check.c
>>> index 6633b6e..81dd4f3 100644
>>> --- a/cmds-check.c
>>> +++ b/cmds-check.c
>>> @@ -323,6 +323,8 @@ struct root_item_info {
>>>  #define MISSING_BACKREF    (1 << 0) /* Completely no backref in
>>> extent tree */
>>>  #define BAD_BACKREF    (1 << 1) /* Backref mismatch */
>>>  #define UNALIGNED_BYTES    (1 << 2) /* Some bytes are not aligned */
>>> +#define MISSING_REFERENCER (1 << 3) /* Referencer not found */
>>> +#define BAD_REFERENCER    (1 << 4) /* Referencer found, but not
>>> mismatch */
>>>
>>>  static void *print_status_check(void *p)
>>>  {
>>> @@ -8736,6 +8738,99 @@ out:
>>>      return ret;
>>>  }
>>>
>>> +/*
>>> + * Check if a tree block backref is valid (points to valid tree block)
>>> + * if level == -1, level will be resolved
>>> + */
>>> +static int check_tree_block_backref(struct btrfs_fs_info *fs_info,
>>> u64 root_id,
>>> +                    u64 bytenr, int level)
>>> +{
>>> +    struct btrfs_root *root;
>>> +    struct btrfs_key key;
>>> +    struct btrfs_path path;
>>> +    struct extent_buffer *eb;
>>> +    struct extent_buffer *node;
>>> +    u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
>>> +    int err = 0;
>>> +    int ret;
>>> +
>>> +    /* Query level for level == -1 special case */
>>> +    if (level == -1)
>>> +        level = query_tree_block_level(fs_info, bytenr);
>>> +    if (level < 0) {
>>> +        err = MISSING_REFERENCER;
>>> +        goto out;
>>> +    }
>>> +
>>> +    key.objectid = root_id;
>>> +    key.type = BTRFS_ROOT_ITEM_KEY;
>>> +    key.offset = (u64)-1;
>>> +
>>> +    root = btrfs_read_fs_root(fs_info, &key);
>>> +    if (IS_ERR(root)) {
>>> +        err |= MISSING_REFERENCER;
>>> +        goto out;
>>> +    }
>>> +
>>> +    /* Read out the tree block to get item/node key */
>>> +    eb = read_tree_block(root, bytenr, root->nodesize, 0);
>>> +    /* Impossible, as tree block query has read out the tree block */
>>> +    if (!extent_buffer_uptodate(eb)) {
>>> +        err |= MISSING_REFERENCER;
>>> +        free_extent_buffer(eb);
>>> +        goto out;
>>> +    }
>>> +
>>> +    /* Empty tree, no need to check key */
>>> +    if (!btrfs_header_nritems(eb) && !level) {
>>> +        free_extent_buffer(eb);
>>> +        goto out;
>>> +    }
>>
>> Actually this is interesting, because we don't actually catch where we
>> would have btrfs_header_nritems(eb) == 0 and !level.  So maybe integrate
>> that check into check_block, to make sure we have items in node level
>> extent buffers.  Thanks,
>>
>> Josef
>>
>>
> Unfortunately, we can't integrate it into check_tree_block() (or caller
> read_tree_block() and its variants).
>
> As this is a valid case.
> (And that's why we don't report error here)
>
> For trees like csum tree and uuid tree, they can be empty, and it
> doesn't break any thing.
>
> So it's a not something we need to integrate into check_tree_block().
>
> And if you mean check_block() in old fsck codes, we won't use it in new
> fsck, so still not a big problem.
>

But empty trees the root is still level 0, we shouldn't have a node that 
is empty, it should just be removed, right?  Thanks,

Josef

--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Qu Wenruo May 3, 2016, 12:56 a.m. UTC | #5
Josef Bacik wrote on 2016/04/29 09:52 -0400:
> On 04/29/2016 01:31 AM, Qu Wenruo wrote:
>>
>>
>> Josef Bacik wrote on 2016/04/28 10:17 -0400:
>>> On 04/25/2016 11:48 PM, Qu Wenruo wrote:
>>>> From: Lu Fengqi <lufq.fnst@cn.fujitsu.com>
>>>>
>>>> Introduce a new function check_tree_block_backref() to check if a
>>>> backref points to correct referencer.
>>>>
>>>> Signed-off-by: Lu Fengqi <lufq.fnst@cn.fujitsu.com>
>>>> Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
>>>> ---
>>>>  cmds-check.c | 95
>>>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>>  1 file changed, 95 insertions(+)
>>>>
>>>> diff --git a/cmds-check.c b/cmds-check.c
>>>> index 6633b6e..81dd4f3 100644
>>>> --- a/cmds-check.c
>>>> +++ b/cmds-check.c
>>>> @@ -323,6 +323,8 @@ struct root_item_info {
>>>>  #define MISSING_BACKREF    (1 << 0) /* Completely no backref in
>>>> extent tree */
>>>>  #define BAD_BACKREF    (1 << 1) /* Backref mismatch */
>>>>  #define UNALIGNED_BYTES    (1 << 2) /* Some bytes are not aligned */
>>>> +#define MISSING_REFERENCER (1 << 3) /* Referencer not found */
>>>> +#define BAD_REFERENCER    (1 << 4) /* Referencer found, but not
>>>> mismatch */
>>>>
>>>>  static void *print_status_check(void *p)
>>>>  {
>>>> @@ -8736,6 +8738,99 @@ out:
>>>>      return ret;
>>>>  }
>>>>
>>>> +/*
>>>> + * Check if a tree block backref is valid (points to valid tree block)
>>>> + * if level == -1, level will be resolved
>>>> + */
>>>> +static int check_tree_block_backref(struct btrfs_fs_info *fs_info,
>>>> u64 root_id,
>>>> +                    u64 bytenr, int level)
>>>> +{
>>>> +    struct btrfs_root *root;
>>>> +    struct btrfs_key key;
>>>> +    struct btrfs_path path;
>>>> +    struct extent_buffer *eb;
>>>> +    struct extent_buffer *node;
>>>> +    u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
>>>> +    int err = 0;
>>>> +    int ret;
>>>> +
>>>> +    /* Query level for level == -1 special case */
>>>> +    if (level == -1)
>>>> +        level = query_tree_block_level(fs_info, bytenr);
>>>> +    if (level < 0) {
>>>> +        err = MISSING_REFERENCER;
>>>> +        goto out;
>>>> +    }
>>>> +
>>>> +    key.objectid = root_id;
>>>> +    key.type = BTRFS_ROOT_ITEM_KEY;
>>>> +    key.offset = (u64)-1;
>>>> +
>>>> +    root = btrfs_read_fs_root(fs_info, &key);
>>>> +    if (IS_ERR(root)) {
>>>> +        err |= MISSING_REFERENCER;
>>>> +        goto out;
>>>> +    }
>>>> +
>>>> +    /* Read out the tree block to get item/node key */
>>>> +    eb = read_tree_block(root, bytenr, root->nodesize, 0);
>>>> +    /* Impossible, as tree block query has read out the tree block */
>>>> +    if (!extent_buffer_uptodate(eb)) {
>>>> +        err |= MISSING_REFERENCER;
>>>> +        free_extent_buffer(eb);
>>>> +        goto out;
>>>> +    }
>>>> +
>>>> +    /* Empty tree, no need to check key */
>>>> +    if (!btrfs_header_nritems(eb) && !level) {
>>>> +        free_extent_buffer(eb);
>>>> +        goto out;
>>>> +    }
>>>
>>> Actually this is interesting, because we don't actually catch where we
>>> would have btrfs_header_nritems(eb) == 0 and !level.  So maybe integrate
>>> that check into check_block, to make sure we have items in node level
>>> extent buffers.  Thanks,
>>>
>>> Josef
>>>
>>>
>> Unfortunately, we can't integrate it into check_tree_block() (or caller
>> read_tree_block() and its variants).
>>
>> As this is a valid case.
>> (And that's why we don't report error here)
>>
>> For trees like csum tree and uuid tree, they can be empty, and it
>> doesn't break any thing.
>>
>> So it's a not something we need to integrate into check_tree_block().
>>
>> And if you mean check_block() in old fsck codes, we won't use it in new
>> fsck, so still not a big problem.
>>
>
> But empty trees the root is still level 0, we shouldn't have a node that
> is empty, it should just be removed, right?  Thanks,
>
> Josef
>
>
>
Oh, You mean any tree level higher than 0 shouldn't have nritems == 0.

That makes sense, I'll send a independent patch for that.

Thanks,
Qu


--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/cmds-check.c b/cmds-check.c
index 6633b6e..81dd4f3 100644
--- a/cmds-check.c
+++ b/cmds-check.c
@@ -323,6 +323,8 @@  struct root_item_info {
 #define MISSING_BACKREF	(1 << 0) /* Completely no backref in extent tree */
 #define BAD_BACKREF	(1 << 1) /* Backref mismatch */
 #define UNALIGNED_BYTES	(1 << 2) /* Some bytes are not aligned */
+#define MISSING_REFERENCER (1 << 3) /* Referencer not found */
+#define BAD_REFERENCER	(1 << 4) /* Referencer found, but not mismatch */
 
 static void *print_status_check(void *p)
 {
@@ -8736,6 +8738,99 @@  out:
 	return ret;
 }
 
+/*
+ * Check if a tree block backref is valid (points to valid tree block)
+ * if level == -1, level will be resolved
+ */
+static int check_tree_block_backref(struct btrfs_fs_info *fs_info, u64 root_id,
+				    u64 bytenr, int level)
+{
+	struct btrfs_root *root;
+	struct btrfs_key key;
+	struct btrfs_path path;
+	struct extent_buffer *eb;
+	struct extent_buffer *node;
+	u32 nodesize = btrfs_super_nodesize(fs_info->super_copy);
+	int err = 0;
+	int ret;
+
+	/* Query level for level == -1 special case */
+	if (level == -1)
+		level = query_tree_block_level(fs_info, bytenr);
+	if (level < 0) {
+		err = MISSING_REFERENCER;
+		goto out;
+	}
+
+	key.objectid = root_id;
+	key.type = BTRFS_ROOT_ITEM_KEY;
+	key.offset = (u64)-1;
+
+	root = btrfs_read_fs_root(fs_info, &key);
+	if (IS_ERR(root)) {
+		err |= MISSING_REFERENCER;
+		goto out;
+	}
+
+	/* Read out the tree block to get item/node key */
+	eb = read_tree_block(root, bytenr, root->nodesize, 0);
+	/* Impossible, as tree block query has read out the tree block */
+	if (!extent_buffer_uptodate(eb)) {
+		err |= MISSING_REFERENCER;
+		free_extent_buffer(eb);
+		goto out;
+	}
+
+	/* Empty tree, no need to check key */
+	if (!btrfs_header_nritems(eb) && !level) {
+		free_extent_buffer(eb);
+		goto out;
+	}
+
+	if (level)
+		btrfs_node_key_to_cpu(eb, &key, 0);
+	else
+		btrfs_item_key_to_cpu(eb, &key, 0);
+
+	free_extent_buffer(eb);
+
+	btrfs_init_path(&path);
+	/* Search with the first key, to ensure we can reach it */
+	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
+	if (ret) {
+		err |= MISSING_REFERENCER;
+		goto release_out;
+	}
+
+	node = path.nodes[level];
+	if (btrfs_header_bytenr(node) != bytenr) {
+		error("Extent [%llu %d] referencer bytenr mismatch, wanted: %llu, have: %llu",
+		      bytenr, nodesize, bytenr,
+		      btrfs_header_bytenr(node));
+		err |= BAD_REFERENCER;
+	}
+	if (btrfs_header_level(node) != level) {
+		error("Extent [%llu %d] referencer level mismatch, wanted: %d, have: %d",
+		      bytenr, nodesize, level,
+		      btrfs_header_level(node));
+		err |= BAD_REFERENCER;
+	}
+
+release_out:
+	btrfs_release_path(&path);
+out:
+	if (err & MISSING_REFERENCER) {
+		if (level < 0)
+			error("Extent [%llu %d] lost referencer(owner: %llu)",
+			       bytenr, nodesize, root_id);
+		else
+			error("Extent [%llu %d] lost referencer(owner: %llu, level: %u)",
+			       bytenr, nodesize, root_id, level);
+	}
+
+	return -err;
+}
+
 static int btrfs_fsck_reinit_root(struct btrfs_trans_handle *trans,
 			   struct btrfs_root *root, int overwrite)
 {