Blob Blame History Raw
From: David Sterba <dsterba@suse.com>
Date: Thu, 2 Jun 2016 13:50:44 +0200
Subject: btrfs: serialize subvolume mounts with potentially mismatching rw flags
Patch-mainline: Never, ugly <dsterba: ugly but necessary, upstream fix will be more involved>
References: bsc#951844 bsc#1024015 bsc#1099745

Racing subvolume mounts with mixed ro/rw flags can fail if the mount and
remount are not done atomically.

Signed-off-by: David Sterba <dsterba@suse.com>
---
 fs/btrfs/super.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1607,6 +1607,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
 	char *subvol_name = NULL;
 	u64 subvol_objectid = 0;
 	int error = 0;
+	static DEFINE_MUTEX(subvol_lock);
 
 	error = btrfs_parse_subvol_options(data, &subvol_name,
 					&subvol_objectid);
@@ -1615,6 +1616,24 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
 		return ERR_PTR(error);
 	}
 
+	/*
+	 * Protect against racing mounts of subvolumes with different RO/RW
+	 * flags.  The first vfs_kern_mount could fail with -EBUSY if the rw
+	 * flags do not match with the first and the currently mounted
+	 * subvolume.
+	 *
+	 * To resolve that, we adjust the rw flags and do remount. If another
+	 * mounts goes through the same path and hits the window between the
+	 * adjusted vfs_kern_mount and btrfs_remount, it will fail because of
+	 * the ro/rw mismatch in btrfs_mount.
+	 *
+	 * If the mounts do not race and are serialized externally, everything
+	 * works fine.  The function-local mutex enforces the serialization but
+	 * is otherwise only an ugly workaround due to lack of better
+	 * solutions.
+	 */
+	mutex_lock(&subvol_lock);
+
 	/* mount device's root (/) */
 	mnt_root = vfs_kern_mount(&btrfs_root_fs_type, flags, device_name, data);
 	if (PTR_ERR_OR_ZERO(mnt_root) == -EBUSY) {
@@ -1626,6 +1645,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
 				flags | SB_RDONLY, device_name, data);
 			if (IS_ERR(mnt_root)) {
 				root = ERR_CAST(mnt_root);
+				mutex_unlock(&subvol_lock);
 				kfree(subvol_name);
 				goto out;
 			}
@@ -1636,11 +1656,14 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
 			if (error < 0) {
 				root = ERR_PTR(error);
 				mntput(mnt_root);
+				mutex_unlock(&subvol_lock);
 				kfree(subvol_name);
 				goto out;
 			}
 		}
 	}
+	mutex_unlock(&subvol_lock);
+
 	if (IS_ERR(mnt_root)) {
 		root = ERR_CAST(mnt_root);
 		kfree(subvol_name);