diff options
Diffstat (limited to 'fs/btrfs')
-rw-r--r-- | fs/btrfs/ctree.h | 6 | ||||
-rw-r--r-- | fs/btrfs/ioctl.c | 22 | ||||
-rw-r--r-- | fs/btrfs/send.c | 54 |
3 files changed, 79 insertions, 3 deletions
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 3c9053a153b1..9318c7520f50 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h | |||
@@ -1814,6 +1814,12 @@ struct btrfs_root { | |||
1814 | struct list_head ordered_extents; | 1814 | struct list_head ordered_extents; |
1815 | struct list_head ordered_root; | 1815 | struct list_head ordered_root; |
1816 | u64 nr_ordered_extents; | 1816 | u64 nr_ordered_extents; |
1817 | |||
1818 | /* | ||
1819 | * Number of currently running SEND ioctls to prevent | ||
1820 | * manipulation with the read-only status via SUBVOL_SETFLAGS | ||
1821 | */ | ||
1822 | int send_in_progress; | ||
1817 | }; | 1823 | }; |
1818 | 1824 | ||
1819 | struct btrfs_ioctl_defrag_range_args { | 1825 | struct btrfs_ioctl_defrag_range_args { |
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 89c2f6169b92..c0dc05467ce8 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -1706,12 +1706,28 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file, | |||
1706 | goto out_drop_sem; | 1706 | goto out_drop_sem; |
1707 | 1707 | ||
1708 | root_flags = btrfs_root_flags(&root->root_item); | 1708 | root_flags = btrfs_root_flags(&root->root_item); |
1709 | if (flags & BTRFS_SUBVOL_RDONLY) | 1709 | if (flags & BTRFS_SUBVOL_RDONLY) { |
1710 | btrfs_set_root_flags(&root->root_item, | 1710 | btrfs_set_root_flags(&root->root_item, |
1711 | root_flags | BTRFS_ROOT_SUBVOL_RDONLY); | 1711 | root_flags | BTRFS_ROOT_SUBVOL_RDONLY); |
1712 | else | 1712 | } else { |
1713 | btrfs_set_root_flags(&root->root_item, | 1713 | /* |
1714 | * Block RO -> RW transition if this subvolume is involved in | ||
1715 | * send | ||
1716 | */ | ||
1717 | spin_lock(&root->root_item_lock); | ||
1718 | if (root->send_in_progress == 0) { | ||
1719 | btrfs_set_root_flags(&root->root_item, | ||
1714 | root_flags & ~BTRFS_ROOT_SUBVOL_RDONLY); | 1720 | root_flags & ~BTRFS_ROOT_SUBVOL_RDONLY); |
1721 | spin_unlock(&root->root_item_lock); | ||
1722 | } else { | ||
1723 | spin_unlock(&root->root_item_lock); | ||
1724 | btrfs_warn(root->fs_info, | ||
1725 | "Attempt to set subvolume %llu read-write during send", | ||
1726 | root->root_key.objectid); | ||
1727 | ret = -EPERM; | ||
1728 | goto out_drop_sem; | ||
1729 | } | ||
1730 | } | ||
1715 | 1731 | ||
1716 | trans = btrfs_start_transaction(root, 1); | 1732 | trans = btrfs_start_transaction(root, 1); |
1717 | if (IS_ERR(trans)) { | 1733 | if (IS_ERR(trans)) { |
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c index e98c9bc003c8..572e8c758712 100644 --- a/fs/btrfs/send.c +++ b/fs/btrfs/send.c | |||
@@ -4769,6 +4769,7 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) | |||
4769 | struct send_ctx *sctx = NULL; | 4769 | struct send_ctx *sctx = NULL; |
4770 | u32 i; | 4770 | u32 i; |
4771 | u64 *clone_sources_tmp = NULL; | 4771 | u64 *clone_sources_tmp = NULL; |
4772 | int clone_sources_to_rollback = 0; | ||
4772 | 4773 | ||
4773 | if (!capable(CAP_SYS_ADMIN)) | 4774 | if (!capable(CAP_SYS_ADMIN)) |
4774 | return -EPERM; | 4775 | return -EPERM; |
@@ -4777,6 +4778,14 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) | |||
4777 | fs_info = send_root->fs_info; | 4778 | fs_info = send_root->fs_info; |
4778 | 4779 | ||
4779 | /* | 4780 | /* |
4781 | * The subvolume must remain read-only during send, protect against | ||
4782 | * making it RW. | ||
4783 | */ | ||
4784 | spin_lock(&send_root->root_item_lock); | ||
4785 | send_root->send_in_progress++; | ||
4786 | spin_unlock(&send_root->root_item_lock); | ||
4787 | |||
4788 | /* | ||
4780 | * This is done when we lookup the root, it should already be complete | 4789 | * This is done when we lookup the root, it should already be complete |
4781 | * by the time we get here. | 4790 | * by the time we get here. |
4782 | */ | 4791 | */ |
@@ -4811,6 +4820,15 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) | |||
4811 | up_read(&send_root->fs_info->extent_commit_sem); | 4820 | up_read(&send_root->fs_info->extent_commit_sem); |
4812 | } | 4821 | } |
4813 | 4822 | ||
4823 | /* | ||
4824 | * Userspace tools do the checks and warn the user if it's | ||
4825 | * not RO. | ||
4826 | */ | ||
4827 | if (!btrfs_root_readonly(send_root)) { | ||
4828 | ret = -EPERM; | ||
4829 | goto out; | ||
4830 | } | ||
4831 | |||
4814 | arg = memdup_user(arg_, sizeof(*arg)); | 4832 | arg = memdup_user(arg_, sizeof(*arg)); |
4815 | if (IS_ERR(arg)) { | 4833 | if (IS_ERR(arg)) { |
4816 | ret = PTR_ERR(arg); | 4834 | ret = PTR_ERR(arg); |
@@ -4897,6 +4915,15 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) | |||
4897 | ret = PTR_ERR(clone_root); | 4915 | ret = PTR_ERR(clone_root); |
4898 | goto out; | 4916 | goto out; |
4899 | } | 4917 | } |
4918 | clone_sources_to_rollback = i + 1; | ||
4919 | spin_lock(&clone_root->root_item_lock); | ||
4920 | clone_root->send_in_progress++; | ||
4921 | if (!btrfs_root_readonly(clone_root)) { | ||
4922 | spin_unlock(&clone_root->root_item_lock); | ||
4923 | ret = -EPERM; | ||
4924 | goto out; | ||
4925 | } | ||
4926 | spin_unlock(&clone_root->root_item_lock); | ||
4900 | sctx->clone_roots[i].root = clone_root; | 4927 | sctx->clone_roots[i].root = clone_root; |
4901 | } | 4928 | } |
4902 | vfree(clone_sources_tmp); | 4929 | vfree(clone_sources_tmp); |
@@ -4912,6 +4939,14 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) | |||
4912 | ret = PTR_ERR(sctx->parent_root); | 4939 | ret = PTR_ERR(sctx->parent_root); |
4913 | goto out; | 4940 | goto out; |
4914 | } | 4941 | } |
4942 | spin_lock(&sctx->parent_root->root_item_lock); | ||
4943 | sctx->parent_root->send_in_progress++; | ||
4944 | if (!btrfs_root_readonly(sctx->parent_root)) { | ||
4945 | spin_unlock(&sctx->parent_root->root_item_lock); | ||
4946 | ret = -EPERM; | ||
4947 | goto out; | ||
4948 | } | ||
4949 | spin_unlock(&sctx->parent_root->root_item_lock); | ||
4915 | } | 4950 | } |
4916 | 4951 | ||
4917 | /* | 4952 | /* |
@@ -4940,6 +4975,25 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) | |||
4940 | } | 4975 | } |
4941 | 4976 | ||
4942 | out: | 4977 | out: |
4978 | for (i = 0; sctx && i < clone_sources_to_rollback; i++) { | ||
4979 | struct btrfs_root *r = sctx->clone_roots[i].root; | ||
4980 | |||
4981 | spin_lock(&r->root_item_lock); | ||
4982 | r->send_in_progress--; | ||
4983 | spin_unlock(&r->root_item_lock); | ||
4984 | } | ||
4985 | if (sctx && !IS_ERR_OR_NULL(sctx->parent_root)) { | ||
4986 | struct btrfs_root *r = sctx->parent_root; | ||
4987 | |||
4988 | spin_lock(&r->root_item_lock); | ||
4989 | r->send_in_progress--; | ||
4990 | spin_unlock(&r->root_item_lock); | ||
4991 | } | ||
4992 | |||
4993 | spin_lock(&send_root->root_item_lock); | ||
4994 | send_root->send_in_progress--; | ||
4995 | spin_unlock(&send_root->root_item_lock); | ||
4996 | |||
4943 | kfree(arg); | 4997 | kfree(arg); |
4944 | vfree(clone_sources_tmp); | 4998 | vfree(clone_sources_tmp); |
4945 | 4999 | ||