btrfs-progs: Handle error properly in btrfs_commit_transaction()
[BUG] When running fuzz-tests/003 and fuzz-tests/009, btrfs-progs will crash due to BUG_ON(). [CAUSE] We abused BUG_ON() in btrfs_commit_transaction(), which is one of the most error prone function for fuzzed images. Currently to cleanup the aborted transaction, we only need to clean up the only per-transaction data: delayed refs. This patch will introduce a new function, btrfs_destroy_delayed_refs() to cleanup delayed refs when we failed to commit transaction. With that function, we will gently destroy per-trans delayed ref, and remove the BUG_ON()s in btrfs_commit_transaction(). Reviewed-by: Nikolay Borisov <nborisov@suse.com> Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
45e58a1acf
commit
5672a69639
|
@ -605,3 +605,27 @@ free_ref:
|
|||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void btrfs_destroy_delayed_refs(struct btrfs_trans_handle *trans)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = trans->fs_info;
|
||||
struct rb_node *node;
|
||||
struct btrfs_delayed_ref_root *delayed_refs;
|
||||
|
||||
delayed_refs = &trans->delayed_refs;
|
||||
if (RB_EMPTY_ROOT(&delayed_refs->href_root))
|
||||
return;
|
||||
while ((node = rb_first(&delayed_refs->href_root)) != NULL) {
|
||||
struct btrfs_delayed_ref_head *head;
|
||||
struct btrfs_delayed_ref_node *ref;
|
||||
struct rb_node *n;
|
||||
|
||||
head = rb_entry(node, struct btrfs_delayed_ref_head, href_node);
|
||||
while ((n = rb_first(&head->ref_tree)) != NULL) {
|
||||
ref = rb_entry(n, struct btrfs_delayed_ref_node,
|
||||
ref_node);
|
||||
drop_delayed_ref(trans, delayed_refs, head, ref);
|
||||
}
|
||||
ASSERT(cleanup_ref_head(trans, fs_info, head) == 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -205,4 +205,10 @@ btrfs_delayed_node_to_tree_ref(struct btrfs_delayed_ref_node *node)
|
|||
{
|
||||
return container_of(node, struct btrfs_delayed_tree_ref, node);
|
||||
}
|
||||
|
||||
int cleanup_ref_head(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_delayed_ref_head *head);
|
||||
void btrfs_destroy_delayed_refs(struct btrfs_trans_handle *trans);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4085,9 +4085,9 @@ static void unselect_delayed_ref_head(struct btrfs_delayed_ref_root *delayed_ref
|
|||
delayed_refs->num_heads_ready++;
|
||||
}
|
||||
|
||||
static int cleanup_ref_head(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_delayed_ref_head *head)
|
||||
int cleanup_ref_head(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_delayed_ref_head *head)
|
||||
{
|
||||
struct btrfs_delayed_ref_root *delayed_refs;
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "kerncompat.h"
|
||||
#include "disk-io.h"
|
||||
#include "transaction.h"
|
||||
#include "delayed-ref.h"
|
||||
|
||||
#include "messages.h"
|
||||
|
||||
|
@ -165,7 +166,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|||
* consistent
|
||||
*/
|
||||
ret = btrfs_run_delayed_refs(trans, -1);
|
||||
BUG_ON(ret);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
if (root->commit_root == root->node)
|
||||
goto commit_tree;
|
||||
|
@ -182,21 +184,24 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|||
root->root_item.level = btrfs_header_level(root->node);
|
||||
ret = btrfs_update_root(trans, root->fs_info->tree_root,
|
||||
&root->root_key, &root->root_item);
|
||||
BUG_ON(ret);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
commit_tree:
|
||||
ret = commit_tree_roots(trans, fs_info);
|
||||
BUG_ON(ret);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
/*
|
||||
* Ensure that all committed roots are properly accounted in the
|
||||
* extent tree
|
||||
*/
|
||||
ret = btrfs_run_delayed_refs(trans, -1);
|
||||
BUG_ON(ret);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
btrfs_write_dirty_block_groups(trans);
|
||||
__commit_transaction(trans, root);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
goto error;
|
||||
ret = write_ctree_super(trans);
|
||||
btrfs_finish_extent_commit(trans);
|
||||
kfree(trans);
|
||||
|
@ -204,7 +209,10 @@ commit_tree:
|
|||
root->commit_root = NULL;
|
||||
fs_info->running_transaction = NULL;
|
||||
fs_info->last_trans_committed = transid;
|
||||
out:
|
||||
return ret;
|
||||
error:
|
||||
btrfs_destroy_delayed_refs(trans);
|
||||
free(trans);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue