diff options
Diffstat (limited to 'fs/btrfs/send.c')
-rw-r--r-- | fs/btrfs/send.c | 49 |
1 files changed, 49 insertions, 0 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index 874828dd0a86..804432dbc351 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c | |||
@@ -5507,6 +5507,51 @@ out: | |||
5507 | return ret; | 5507 | return ret; |
5508 | } | 5508 | } |
5509 | 5509 | ||
5510 | /* | ||
5511 | * If orphan cleanup did remove any orphans from a root, it means the tree | ||
5512 | * was modified and therefore the commit root is not the same as the current | ||
5513 | * root anymore. This is a problem, because send uses the commit root and | ||
5514 | * therefore can see inode items that don't exist in the current root anymore, | ||
5515 | * and for example make calls to btrfs_iget, which will do tree lookups based | ||
5516 | * on the current root and not on the commit root. Those lookups will fail, | ||
5517 | * returning a -ESTALE error, and making send fail with that error. So make | ||
5518 | * sure a send does not see any orphans we have just removed, and that it will | ||
5519 | * see the same inodes regardless of whether a transaction commit happened | ||
5520 | * before it started (meaning that the commit root will be the same as the | ||
5521 | * current root) or not. | ||
5522 | */ | ||
5523 | static int ensure_commit_roots_uptodate(struct send_ctx *sctx) | ||
5524 | { | ||
5525 | int i; | ||
5526 | struct btrfs_trans_handle *trans = NULL; | ||
5527 | |||
5528 | again: | ||
5529 | if (sctx->parent_root && | ||
5530 | sctx->parent_root->node != sctx->parent_root->commit_root) | ||
5531 | goto commit_trans; | ||
5532 | |||
5533 | for (i = 0; i < sctx->clone_roots_cnt; i++) | ||
5534 | if (sctx->clone_roots[i].root->node != | ||
5535 | sctx->clone_roots[i].root->commit_root) | ||
5536 | goto commit_trans; | ||
5537 | |||
5538 | if (trans) | ||
5539 | return btrfs_end_transaction(trans, sctx->send_root); | ||
5540 | |||
5541 | return 0; | ||
5542 | |||
5543 | commit_trans: | ||
5544 | /* Use any root, all fs roots will get their commit roots updated. */ | ||
5545 | if (!trans) { | ||
5546 | trans = btrfs_join_transaction(sctx->send_root); | ||
5547 | if (IS_ERR(trans)) | ||
5548 | return PTR_ERR(trans); | ||
5549 | goto again; | ||
5550 | } | ||
5551 | |||
5552 | return btrfs_commit_transaction(trans, sctx->send_root); | ||
5553 | } | ||
5554 | |||
5510 | static void btrfs_root_dec_send_in_progress(struct btrfs_root* root) | 5555 | static void btrfs_root_dec_send_in_progress(struct btrfs_root* root) |
5511 | { | 5556 | { |
5512 | spin_lock(&root->root_item_lock); | 5557 | spin_lock(&root->root_item_lock); |
@@ -5728,6 +5773,10 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) | |||
5728 | NULL); | 5773 | NULL); |
5729 | sort_clone_roots = 1; | 5774 | sort_clone_roots = 1; |
5730 | 5775 | ||
5776 | ret = ensure_commit_roots_uptodate(sctx); | ||
5777 | if (ret) | ||
5778 | goto out; | ||
5779 | |||
5731 | current->journal_info = BTRFS_SEND_TRANS_STUB; | 5780 | current->journal_info = BTRFS_SEND_TRANS_STUB; |
5732 | ret = send_subvol(sctx); | 5781 | ret = send_subvol(sctx); |
5733 | current->journal_info = NULL; | 5782 | current->journal_info = NULL; |