FAILED: patch "[PATCH] btrfs: fix invalid removal of root ref" failed to apply to 4.14-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 4.14-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From d49d3287e74ffe55ae7430d1e795e5f9bf7359ea Mon Sep 17 00:00:00 2001
From: Josef Bacik <[email protected]>
Date: Wed, 18 Dec 2019 17:20:28 -0500
Subject: [PATCH] btrfs: fix invalid removal of root ref
If we have the following sequence of events
btrfs sub create A
btrfs sub create A/B
btrfs sub snap A C
mkdir C/foo
mv A/B C/foo
rm -rf *
We will end up with a transaction abort.
The reason for this is because we create a root ref for B pointing to A. When we create a snapshot of C we still have B in our tree, but because the root ref points to A and not C we will make it appear to be empty.
The problem happens when we move B into C. This removes the root ref for B pointing to A and adds a ref of B pointing to C. When we rmdir C we'll see that we have a ref to our root and remove the root ref, despite not actually matching our reference name.
Now btrfs_del_root_ref() allowing this to work is a bug as well, however we know that this inode does not actually point to a root ref in the first place, so we shouldn't be calling btrfs_del_root_ref() in the first place and instead simply look up our dir index for this item and do the rest of the removal.
fs/btrfs/inode.c +19 -8
@@ -4283,13 +4283,16 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, }btrfs_release_path (path); - ret =btrfs_del_root_ref (trans, objectid, root->root_key.objectid, - dir_ino, &index, name, name_len); -if (ret <0 ) { -if (ret != -ENOENT ) { -btrfs_abort_transaction (trans, ret); - goto out; - } +/* +* This is a placeholder inode for a subvolume we didn't have a +* reference to at the time of the snapshot creation. In the meantime +* we could have renamed the real subvol link into our snapshot, so +* depending on btrfs_del_root_ref to return -ENOENT here is incorret. +* Instead simply lookup the dir_index_item for this entry so we can +* remove it. Otherwise we know we have a ref to the root and we can +* call btrfs_del_root_ref, and it _shouldn't_ fail. +*/ +if (btrfs_ino (inode) ==BTRFS_EMPTY_SUBVOL_DIR_OBJECTID ) { di =btrfs_search_dir_index_item (root, path, dir_ino, name, name_len);if (IS_ERR_OR_NULL (di)) {
@@ -4304,8 +4307,16 @@ static int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, leaf = path->nodes[0 ];btrfs_item_key_to_cpu (leaf, &key, path->slots[0 ]); index = key.offset; +btrfs_release_path (path); + }else { + ret =btrfs_del_root_ref (trans, objectid, + root->root_key.objectid, dir_ino, + &index, name, name_len); +if (ret) { +btrfs_abort_transaction (trans, ret); + goto out; + } } -btrfs_release_path (path); ret =btrfs_delete_delayed_dir_index (trans,BTRFS_I (dir), index);if (ret) {