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);