btrfs: fix invalid removal of root ref
authorJosef Bacik <josef@toxicpanda.com>
Wed, 18 Dec 2019 22:20:28 +0000 (17:20 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 23 Jan 2020 07:22:40 +0000 (08:22 +0100)
commitd5e34783c8536fac6e760e687a01abd112a651fa
treecf05408d6288655fa554fc0ab5f7d67f534f11ed
parenta8ac2da72f4f4bab26ad87d2042197bf3181529f
btrfs: fix invalid removal of root ref

commit d49d3287e74ffe55ae7430d1e795e5f9bf7359ea upstream.

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.

CC: stable@vger.kernel.org # 4.4+
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/btrfs/inode.c