btrfs: replace many BUG_ONs with proper error handling

 btrfs currently handles most errors with BUG_ON. This patch is a work-in-
 progress but aims to handle most errors other than internal logic
 errors and ENOMEM more gracefully.

 This iteration prevents most crashes but can run into lockups with
 the page lock on occasion when the timing "works out."

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 5a4999a..63f835a 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -949,18 +949,19 @@
 				dentry->d_name.name, dentry->d_name.len,
 				parent_inode, &key,
 				BTRFS_FT_DIR, index);
-	if (ret) {
+	if (ret == -EEXIST) {
 		pending->error = -EEXIST;
 		dput(parent);
 		goto fail;
-	} else if (ret)
-		goto abort_trans;
+	} else if (ret) {
+		goto abort_trans_dput;
+	}
 
 	btrfs_i_size_write(parent_inode, parent_inode->i_size +
 					 dentry->d_name.len * 2);
 	ret = btrfs_update_inode(trans, parent_root, parent_inode);
 	if (ret)
-		goto abort_trans;
+		goto abort_trans_dput;
 
 	/*
 	 * pull in the delayed directory update
@@ -969,8 +970,10 @@
 	 * snapshot
 	 */
 	ret = btrfs_run_delayed_items(trans, root);
-	if (ret) /* Transaction aborted */
+	if (ret) { /* Transaction aborted */
+		dput(parent);
 		goto fail;
+	}
 
 	record_root_in_trans(trans, root);
 	btrfs_set_root_last_snapshot(&root->root_item, trans->transid);
@@ -986,17 +989,20 @@
 
 	old = btrfs_lock_root_node(root);
 	ret = btrfs_cow_block(trans, root, old, NULL, 0, &old);
-	if (ret)
-		goto abort_trans;
+	if (ret) {
+		btrfs_tree_unlock(old);
+		free_extent_buffer(old);
+		goto abort_trans_dput;
+	}
 
 	btrfs_set_lock_blocking(old);
 
 	ret = btrfs_copy_root(trans, root, old, &tmp, objectid);
-	if (ret)
-		goto abort_trans;
-
+	/* clean up in any case */
 	btrfs_tree_unlock(old);
 	free_extent_buffer(old);
+	if (ret)
+		goto abort_trans_dput;
 
 	/* see comments in should_cow_block() */
 	root->force_cow = 1;
@@ -1009,7 +1015,7 @@
 	btrfs_tree_unlock(tmp);
 	free_extent_buffer(tmp);
 	if (ret)
-		goto abort_trans;
+		goto abort_trans_dput;
 
 	/*
 	 * insert root back/forward references
@@ -1018,14 +1024,16 @@
 				 parent_root->root_key.objectid,
 				 btrfs_ino(parent_inode), index,
 				 dentry->d_name.name, dentry->d_name.len);
+	dput(parent);
 	if (ret)
 		goto fail;
-	dput(parent);
 
 	key.offset = (u64)-1;
 	pending->snap = btrfs_read_fs_root_no_name(root->fs_info, &key);
-	if (IS_ERR(pending->snap))
+	if (IS_ERR(pending->snap)) {
+		ret = PTR_ERR(pending->snap);
 		goto abort_trans;
+	}
 
 	ret = btrfs_reloc_post_snapshot(trans, pending);
 	if (ret)
@@ -1037,6 +1045,8 @@
 	btrfs_block_rsv_release(root, &pending->block_rsv, (u64)-1);
 	return ret;
 
+abort_trans_dput:
+	dput(parent);
 abort_trans:
 	btrfs_abort_transaction(trans, root, ret);
 	goto fail;