Blob Blame History Raw
From: Al Viro <viro@zeniv.linux.org.uk>
Date: Tue, 17 Dec 2019 14:15:04 -0500
Subject: [PATCH] Pass consistent param->type to fs_parse()
Git-commit: 0f89589a8c6f1033cb847a606517998efb0da8ee
References: bsc#1192606
Patch-mainline: v5.6-rc1

As it is, vfs_parse_fs_string() makes "foo" and "foo=" indistinguishable;
both get fs_value_is_string for ->type and NULL for ->string.  To make
it even more unpleasant, that combination is impossible to produce with
fsconfig().

Much saner rules would be
        "foo"           => fs_value_is_flag, NULL
	"foo="          => fs_value_is_string, ""
	"foo=bar"       => fs_value_is_string, "bar"
All cases are distinguishable, all results are expressable by fsconfig(),
->has_value checks are much simpler that way (to the point of the field
being useless) and quite a few regressions go away (gfs2 has no business
accepting -o nodebug=, for example).

Partially based upon patches from Miklos.

[ ematsumiya: drop the case fs_param_is_fd, and leave .has_value in struct
  fs_parse_result so it doesn't break kabi ]

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Acked-by: Enzo Matsumiya <ematsumiya@suse.de>
---
 drivers/block/rbd.c |    7 ++-----
 fs/fs_context.c     |    5 +++--
 fs/fs_parser.c      |   19 +++++++++++--------
 3 files changed, 16 insertions(+), 15 deletions(-)

--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -6433,7 +6433,7 @@ static int rbd_parse_options(char *optio
 		if (*key) {
 			struct fs_parameter param = {
 				.key	= key,
-				.type	= fs_value_is_string,
+				.type	= fs_value_is_flag,
 			};
 			char *value = strchr(key, '=');
 			size_t v_len = 0;
@@ -6443,14 +6443,11 @@ static int rbd_parse_options(char *optio
 					continue;
 				*value++ = 0;
 				v_len = strlen(value);
-			}
-
-
-			if (v_len > 0) {
 				param.string = kmemdup_nul(value, v_len,
 							   GFP_KERNEL);
 				if (!param.string)
 					return -ENOMEM;
+				param.type = fs_value_is_string;
 			}
 			param.size = v_len;
 
--- a/fs/fs_context.c
+++ b/fs/fs_context.c
@@ -175,14 +175,15 @@ int vfs_parse_fs_string(struct fs_contex
 
 	struct fs_parameter param = {
 		.key	= key,
-		.type	= fs_value_is_string,
+		.type	= fs_value_is_flag,
 		.size	= v_size,
 	};
 
-	if (v_size > 0) {
+	if (value) {
 		param.string = kmemdup_nul(value, v_size, GFP_KERNEL);
 		if (!param.string)
 			return -ENOMEM;
+		param.type = fs_value_is_string;
 	}
 
 	ret = vfs_parse_fs_param(fc, &param);
--- a/fs/fs_parser.c
+++ b/fs/fs_parser.c
@@ -95,7 +95,7 @@ int fs_parse(struct fs_context *fc,
 		 * "xxx" takes the "no"-form negative - but only if there
 		 * wasn't an value.
 		 */
-		if (result->has_value)
+		if (param->type != fs_value_is_flag)
 			goto unknown_parameter;
 		if (param->key[0] != 'n' || param->key[1] != 'o' || !param->key[2])
 			goto unknown_parameter;
@@ -127,14 +127,18 @@ int fs_parse(struct fs_context *fc,
 	case fs_param_is_u64:
 	case fs_param_is_enum:
 	case fs_param_is_string:
-		if (param->type != fs_value_is_string)
-			goto bad_value;
-		if (!result->has_value) {
+		if (param->type == fs_value_is_string) {
+			if (p->flags & fs_param_v_optional)
+				break;
+			if (!*param->string)
+				goto bad_value;
+			break;
+		}
+		if (param->type == fs_value_is_flag) {
 			if (p->flags & fs_param_v_optional)
 				goto okay;
-			goto bad_value;
 		}
-		/* Fall through */
+		goto bad_value;
 	default:
 		break;
 	}
@@ -144,8 +148,7 @@ int fs_parse(struct fs_context *fc,
 	 */
 	switch (p->type) {
 	case fs_param_is_flag:
-		if (param->type != fs_value_is_flag &&
-		    (param->type != fs_value_is_string || result->has_value))
+		if (param->type != fs_value_is_flag)
 			return invalf(fc, "%s: Unexpected value for '%s'",
 				      desc->name, param->key);
 		result->boolean = true;