aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Sterba <dsterba@suse.cz>2013-12-16 11:34:17 -0500
committerChris Mason <clm@fb.com>2014-01-28 16:20:01 -0500
commit2c68653787f91c62f8891209dc1f617088c822e4 (patch)
tree4f17ea8a804b6174de028c85aa3037c6ec6d28de
parenta8d89f5ba0e17622cde8f5ac48ef745a9fb1e13b (diff)
btrfs: Check read-only status of roots during send
All the subvolues that are involved in send must be read-only during the whole operation. The ioctl SUBVOL_SETFLAGS could be used to change the status to read-write and the result of send stream is undefined if the data change unexpectedly. Fix that by adding a refcount for all involved roots and verify that there's no send in progress during SUBVOL_SETFLAGS ioctl call that does read-only -> read-write transition. We need refcounts because there are no restrictions on number of send parallel operations currently run on a single subvolume, be it source, parent or one of the multiple clone sources. Kernel is silent when the RO checks fail and returns EPERM. The same set of checks is done already in userspace before send starts. Signed-off-by: David Sterba <dsterba@suse.cz> Signed-off-by: Josef Bacik <jbacik@fb.com> Signed-off-by: Chris Mason <clm@fb.com>
-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