aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@fusionio.com>2013-05-20 11:26:50 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-07-21 21:21:31 -0400
commit8049d11b3af40dc43b45017a1646ee6bd02287a1 (patch)
tree5b4c90975c09de9b6ff26e840b067041ecb43b21 /fs
parentd2f5933432d677d224f83cc865c52f14a4785eec (diff)
Btrfs: fix estale with btrfs send
commit 139f807a1eba1e484941a98fb93ee32ad859a6a1 upstream. This fixes bugzilla 57491. If we take a snapshot of a fs with a unlink ongoing and then try to send that root we will run into problems. When comparing with a parent root we will search the parents and the send roots commit_root, which if we've just created the snapshot will include the file that needs to be evicted by the orphan cleanup. So when we find a changed extent we will try and copy that info into the send stream, but when we lookup the inode we use the normal root, which no longer has the inode because the orphan cleanup deleted it. The best solution I have for this is to check our otransid with the generation of the commit root and if they match just commit the transaction again, that way we get the changes from the orphan cleanup. With this patch the reproducer I made for this bugzilla no longer returns ESTALE when trying to do the send. Thanks, Reported-by: Chris Wilson <jakdaw@gmail.com> Signed-off-by: Josef Bacik <jbacik@fusionio.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/send.c35
1 files changed, 35 insertions, 0 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index ff40f1c00ce3..ba9690b9ae24 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -4579,6 +4579,41 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
4579 send_root = BTRFS_I(file_inode(mnt_file))->root; 4579 send_root = BTRFS_I(file_inode(mnt_file))->root;
4580 fs_info = send_root->fs_info; 4580 fs_info = send_root->fs_info;
4581 4581
4582 /*
4583 * This is done when we lookup the root, it should already be complete
4584 * by the time we get here.
4585 */
4586 WARN_ON(send_root->orphan_cleanup_state != ORPHAN_CLEANUP_DONE);
4587
4588 /*
4589 * If we just created this root we need to make sure that the orphan
4590 * cleanup has been done and committed since we search the commit root,
4591 * so check its commit root transid with our otransid and if they match
4592 * commit the transaction to make sure everything is updated.
4593 */
4594 down_read(&send_root->fs_info->extent_commit_sem);
4595 if (btrfs_header_generation(send_root->commit_root) ==
4596 btrfs_root_otransid(&send_root->root_item)) {
4597 struct btrfs_trans_handle *trans;
4598
4599 up_read(&send_root->fs_info->extent_commit_sem);
4600
4601 trans = btrfs_attach_transaction_barrier(send_root);
4602 if (IS_ERR(trans)) {
4603 if (PTR_ERR(trans) != -ENOENT) {
4604 ret = PTR_ERR(trans);
4605 goto out;
4606 }
4607 /* ENOENT means theres no transaction */
4608 } else {
4609 ret = btrfs_commit_transaction(trans, send_root);
4610 if (ret)
4611 goto out;
4612 }
4613 } else {
4614 up_read(&send_root->fs_info->extent_commit_sem);
4615 }
4616
4582 arg = memdup_user(arg_, sizeof(*arg)); 4617 arg = memdup_user(arg_, sizeof(*arg));
4583 if (IS_ERR(arg)) { 4618 if (IS_ERR(arg)) {
4584 ret = PTR_ERR(arg); 4619 ret = PTR_ERR(arg);