aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/ctree.h6
-rw-r--r--fs/btrfs/ioctl.c22
-rw-r--r--fs/btrfs/send.c54
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
1819struct btrfs_ioctl_defrag_range_args { 1825struct 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
4942out: 4977out:
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