[PATCH] btrfs: allow more subvol= option

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Goffredo Baroncelli <kreijack@xxxxxxxxx>

When more than one subvol= options are passed, btrfs try to mount
each subvolume until the first one succeed. Up to 5 subvol= options
can be passed.

Signed-off-by: Goffredo Baroncelli <kreijack@xxxxxxxxx>

---
 fs/btrfs/super.c | 71 ++++++++++++++++++++++++++++++------------------
 1 file changed, 45 insertions(+), 26 deletions(-)

diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index bc73fd670702..12d066e8d52c 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -52,6 +52,8 @@
 #define CREATE_TRACE_POINTS
 #include <trace/events/btrfs.h>
 
+#define SUBVOL_NAMES_COUNT 5
+
 static const struct super_operations btrfs_super_ops;
 
 /*
@@ -974,12 +976,13 @@ static int btrfs_parse_device_options(const char *options, fmode_t flags,
  *
  * The value is later passed to mount_subvol()
  */
-static int btrfs_parse_subvol_options(const char *options, char **subvol_name,
-		u64 *subvol_objectid)
+static int btrfs_parse_subvol_options(const char *options, char **subvol_names,
+					u64 *subvol_objectid)
 {
 	substring_t args[MAX_OPT_ARGS];
 	char *opts, *orig, *p;
 	int error = 0;
+	int svi = 0;
 	u64 subvolid;
 
 	if (!options)
@@ -1002,12 +1005,17 @@ static int btrfs_parse_subvol_options(const char *options, char **subvol_name,
 		token = match_token(p, tokens, args);
 		switch (token) {
 		case Opt_subvol:
-			kfree(*subvol_name);
-			*subvol_name = match_strdup(&args[0]);
-			if (!*subvol_name) {
+			if (svi >= SUBVOL_NAMES_COUNT) {
+				pr_err("BTRFS: too much 'subvol=' mount options\n");
+				error = -E2BIG;
+				goto out;
+			}
+			subvol_names[svi] = match_strdup(&args[0]);
+			if (!subvol_names[svi]) {
 				error = -ENOMEM;
 				goto out;
 			}
+			svi++;
 			break;
 		case Opt_subvolid:
 			error = match_u64(&args[0], &subvolid);
@@ -1429,13 +1437,16 @@ static inline int is_subvolume_inode(struct inode *inode)
 	return 0;
 }
 
-static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid,
+static struct dentry *mount_subvol(char **subvol_names,
+				   u64 subvol_objectid,
 				   struct vfsmount *mnt)
 {
 	struct dentry *root;
 	int ret;
+	const char *sv;
+	int i;
 
-	if (!subvol_name) {
+	if (!subvol_names[0]) {
 		if (!subvol_objectid) {
 			ret = get_default_subvol_objectid(btrfs_sb(mnt->mnt_sb),
 							  &subvol_objectid);
@@ -1444,17 +1455,27 @@ static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid,
 				goto out;
 			}
 		}
-		subvol_name = btrfs_get_subvol_name_from_objectid(
+		subvol_names[0] = btrfs_get_subvol_name_from_objectid(
 					btrfs_sb(mnt->mnt_sb), subvol_objectid);
-		if (IS_ERR(subvol_name)) {
-			root = ERR_CAST(subvol_name);
-			subvol_name = NULL;
+		if (IS_ERR(subvol_names[0])) {
+			root = ERR_CAST(subvol_names[0]);
+			subvol_names[0] = NULL;
 			goto out;
 		}
 
 	}
 
-	root = mount_subtree(mnt, subvol_name);
+	for (i = 0 ; i < SUBVOL_NAMES_COUNT ; i++) {
+		if (!subvol_names[i])
+			break;
+
+		root = mount_subtree(mnt, subvol_names[i]);
+		if (!IS_ERR(root)) {
+			sv = subvol_names[i];
+			break;
+		}
+	}
+
 	/* mount_subtree() drops our reference on the vfsmount. */
 	mnt = NULL;
 
@@ -1466,8 +1487,7 @@ static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid,
 
 		ret = 0;
 		if (!is_subvolume_inode(root_inode)) {
-			btrfs_err(fs_info, "'%s' is not a valid subvolume",
-			       subvol_name);
+			btrfs_err(fs_info, "'%s' is not a valid subvolume", sv);
 			ret = -EINVAL;
 		}
 		if (subvol_objectid && root_objectid != subvol_objectid) {
@@ -1478,7 +1498,7 @@ static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid,
 			 */
 			btrfs_err(fs_info,
 				  "subvol '%s' does not match subvolid %llu",
-				  subvol_name, subvol_objectid);
+				  sv, subvol_objectid);
 			ret = -EINVAL;
 		}
 		if (ret) {
@@ -1490,7 +1510,6 @@ static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid,
 
 out:
 	mntput(mnt);
-	kfree(subvol_name);
 	return root;
 }
 
@@ -1636,15 +1655,16 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
 {
 	struct vfsmount *mnt_root;
 	struct dentry *root;
-	char *subvol_name = NULL;
+	int i;
+	char *subvol_names[SUBVOL_NAMES_COUNT] = {0,};
 	u64 subvol_objectid = 0;
 	int error = 0;
 
-	error = btrfs_parse_subvol_options(data, &subvol_name,
-					&subvol_objectid);
+	error = btrfs_parse_subvol_options(data, subvol_names,
+				&subvol_objectid);
 	if (error) {
-		kfree(subvol_name);
-		return ERR_PTR(error);
+		root = ERR_PTR(error);
+		goto out;
 	}
 
 	/* mount device's root (/) */
@@ -1658,7 +1678,6 @@ 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);
-				kfree(subvol_name);
 				goto out;
 			}
 
@@ -1668,21 +1687,21 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
 			if (error < 0) {
 				root = ERR_PTR(error);
 				mntput(mnt_root);
-				kfree(subvol_name);
 				goto out;
 			}
 		}
 	}
 	if (IS_ERR(mnt_root)) {
 		root = ERR_CAST(mnt_root);
-		kfree(subvol_name);
 		goto out;
 	}
 
-	/* mount_subvol() will free subvol_name and mnt_root */
-	root = mount_subvol(subvol_name, subvol_objectid, mnt_root);
+	/* mount_subvol() will free mnt_root */
+	root = mount_subvol(subvol_names, subvol_objectid, mnt_root);
 
 out:
+	for (i = 0 ; i < SUBVOL_NAMES_COUNT ; i++)
+		kfree(subvol_names[i]);
 	return root;
 }
 
-- 
2.28.0.rc1




[Index of Archives]     [Linux Filesystem Development]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux