Commit 48ce73b1 authored by Al Viro's avatar Al Viro

fs_parse: handle optional arguments sanely

Don't bother with "mixed" options that would allow both the
form with and without argument (i.e. both -o foo and -o foo=bar).
Rather than trying to shove both into a single fs_parameter_spec,
allow having with-argument and no-argument specs with the same
name and teach fs_parse to handle that.

There are very few options of that sort, and they are actually
easier to handle that way - callers end up with less postprocessing.
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent d7167b14
...@@ -179,8 +179,8 @@ static const struct fs_parameter_spec ceph_mount_parameters[] = { ...@@ -179,8 +179,8 @@ static const struct fs_parameter_spec ceph_mount_parameters[] = {
fsparam_flag_no ("copyfrom", Opt_copyfrom), fsparam_flag_no ("copyfrom", Opt_copyfrom),
fsparam_flag_no ("dcache", Opt_dcache), fsparam_flag_no ("dcache", Opt_dcache),
fsparam_flag_no ("dirstat", Opt_dirstat), fsparam_flag_no ("dirstat", Opt_dirstat),
__fsparam (fs_param_is_string, "fsc", Opt_fscache, fsparam_flag_no ("fsc", Opt_fscache), // fsc|nofsc
fs_param_neg_with_no | fs_param_v_optional, NULL), fsparam_string ("fsc", Opt_fscache), // fsc=...
fsparam_flag_no ("ino32", Opt_ino32), fsparam_flag_no ("ino32", Opt_ino32),
fsparam_string ("mds_namespace", Opt_mds_namespace), fsparam_string ("mds_namespace", Opt_mds_namespace),
fsparam_flag_no ("poolperm", Opt_poolperm), fsparam_flag_no ("poolperm", Opt_poolperm),
......
...@@ -46,19 +46,40 @@ int lookup_constant(const struct constant_table *tbl, const char *name, int not_ ...@@ -46,19 +46,40 @@ int lookup_constant(const struct constant_table *tbl, const char *name, int not_
} }
EXPORT_SYMBOL(lookup_constant); EXPORT_SYMBOL(lookup_constant);
static inline bool is_flag(const struct fs_parameter_spec *p)
{
return p->type == fs_param_is_flag;
}
static const struct fs_parameter_spec *fs_lookup_key( static const struct fs_parameter_spec *fs_lookup_key(
const struct fs_parameter_spec *desc, const struct fs_parameter_spec *desc,
const char *name) struct fs_parameter *param, bool *negated)
{ {
const struct fs_parameter_spec *p; const struct fs_parameter_spec *p, *other = NULL;
if (!desc) const char *name = param->key;
return NULL; bool want_flag = param->type == fs_value_is_flag;
for (p = desc; p->name; p++) *negated = false;
if (strcmp(p->name, name) == 0) for (p = desc; p->name; p++) {
if (strcmp(p->name, name) != 0)
continue;
if (likely(is_flag(p) == want_flag))
return p; return p;
other = p;
return NULL; }
if (want_flag) {
if (name[0] == 'n' && name[1] == 'o' && name[2]) {
for (p = desc; p->name; p++) {
if (strcmp(p->name, name + 2) != 0)
continue;
if (!(p->flags & fs_param_neg_with_no))
continue;
*negated = true;
return p;
}
}
}
return other;
} }
/* /*
...@@ -88,123 +109,77 @@ int __fs_parse(struct p_log *log, ...@@ -88,123 +109,77 @@ int __fs_parse(struct p_log *log,
const struct constant_table *e; const struct constant_table *e;
int ret = -ENOPARAM, b; int ret = -ENOPARAM, b;
result->negated = false;
result->uint_64 = 0; result->uint_64 = 0;
p = fs_lookup_key(desc, param->key); p = fs_lookup_key(desc, param, &result->negated);
if (!p) { if (!p)
/* If we didn't find something that looks like "noxxx", see if return -ENOPARAM;
* "xxx" takes the "no"-form negative - but only if there
* wasn't an 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;
p = fs_lookup_key(desc, param->key + 2);
if (!p)
goto unknown_parameter;
if (!(p->flags & fs_param_neg_with_no))
goto unknown_parameter;
result->boolean = false;
result->negated = true;
}
if (p->flags & fs_param_deprecated) if (p->flags & fs_param_deprecated)
warn_plog(log, "Deprecated parameter '%s'", param->key); warn_plog(log, "Deprecated parameter '%s'", param->key);
if (result->negated)
goto okay;
/* Certain parameter types only take a string and convert it. */
switch (p->type) {
case __fs_param_wasnt_defined:
return -EINVAL;
case fs_param_is_u32:
case fs_param_is_u32_octal:
case fs_param_is_u32_hex:
case fs_param_is_s32:
case fs_param_is_u64:
case fs_param_is_enum:
case fs_param_is_string:
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;
default:
break;
}
/* Try to turn the type we were given into the type desired by the /* Try to turn the type we were given into the type desired by the
* parameter and give an error if we can't. * parameter and give an error if we can't.
*/ */
switch (p->type) { switch (p->type) {
case __fs_param_wasnt_defined:
return -EINVAL;
case fs_param_is_flag: case fs_param_is_flag:
if (param->type != fs_value_is_flag) if (param->type != fs_value_is_flag)
return inval_plog(log, "Unexpected value for '%s'", return inval_plog(log, "Unexpected value for '%s'",
param->key); param->key);
result->boolean = true; result->boolean = !result->negated;
goto okay; goto okay;
case fs_param_is_bool: case fs_param_is_bool:
switch (param->type) { if (param->type != fs_value_is_string)
case fs_value_is_flag:
result->boolean = true;
goto okay;
case fs_value_is_string:
if (param->size == 0) {
result->boolean = true;
goto okay;
}
b = lookup_constant(bool_names, param->string, -1);
if (b == -1)
goto bad_value;
result->boolean = b;
goto okay;
default:
goto bad_value; goto bad_value;
} b = lookup_constant(bool_names, param->string, -1);
if (b == -1)
goto bad_value;
result->boolean = b;
goto okay;
case fs_param_is_u32: case fs_param_is_u32:
if (param->type != fs_value_is_string)
goto bad_value;
ret = kstrtouint(param->string, 0, &result->uint_32); ret = kstrtouint(param->string, 0, &result->uint_32);
goto maybe_okay; goto maybe_okay;
case fs_param_is_u32_octal: case fs_param_is_u32_octal:
if (param->type != fs_value_is_string)
goto bad_value;
ret = kstrtouint(param->string, 8, &result->uint_32); ret = kstrtouint(param->string, 8, &result->uint_32);
goto maybe_okay; goto maybe_okay;
case fs_param_is_u32_hex: case fs_param_is_u32_hex:
if (param->type != fs_value_is_string)
goto bad_value;
ret = kstrtouint(param->string, 16, &result->uint_32); ret = kstrtouint(param->string, 16, &result->uint_32);
goto maybe_okay; goto maybe_okay;
case fs_param_is_s32: case fs_param_is_s32:
if (param->type != fs_value_is_string)
goto bad_value;
ret = kstrtoint(param->string, 0, &result->int_32); ret = kstrtoint(param->string, 0, &result->int_32);
goto maybe_okay; goto maybe_okay;
case fs_param_is_u64: case fs_param_is_u64:
if (param->type != fs_value_is_string)
goto bad_value;
ret = kstrtoull(param->string, 0, &result->uint_64); ret = kstrtoull(param->string, 0, &result->uint_64);
goto maybe_okay; goto maybe_okay;
case fs_param_is_enum: case fs_param_is_enum:
if (param->type != fs_value_is_string)
goto bad_value;
e = __lookup_constant(p->data, param->string); e = __lookup_constant(p->data, param->string);
if (e) { if (e) {
result->uint_32 = e->value; result->uint_32 = e->value;
goto okay; goto okay;
} }
goto bad_value; goto bad_value;
case fs_param_is_string: case fs_param_is_string:
if (param->type != fs_value_is_string || !*param->string)
goto bad_value;
goto okay; goto okay;
case fs_param_is_blob: case fs_param_is_blob:
if (param->type != fs_value_is_blob) if (param->type != fs_value_is_blob)
goto bad_value; goto bad_value;
goto okay; goto okay;
case fs_param_is_fd: { case fs_param_is_fd: {
switch (param->type) { switch (param->type) {
case fs_value_is_string: case fs_value_is_string:
...@@ -221,7 +196,6 @@ int __fs_parse(struct p_log *log, ...@@ -221,7 +196,6 @@ int __fs_parse(struct p_log *log,
goto bad_value; goto bad_value;
goto maybe_okay; goto maybe_okay;
} }
case fs_param_is_blockdev: case fs_param_is_blockdev:
case fs_param_is_path: case fs_param_is_path:
goto okay; goto okay;
...@@ -237,8 +211,6 @@ int __fs_parse(struct p_log *log, ...@@ -237,8 +211,6 @@ int __fs_parse(struct p_log *log,
bad_value: bad_value:
return inval_plog(log, "Bad value for '%s'", param->key); return inval_plog(log, "Bad value for '%s'", param->key);
unknown_parameter:
return -ENOPARAM;
} }
EXPORT_SYMBOL(__fs_parse); EXPORT_SYMBOL(__fs_parse);
...@@ -382,6 +354,8 @@ bool fs_validate_description(const char *name, ...@@ -382,6 +354,8 @@ bool fs_validate_description(const char *name,
/* Check for duplicate parameter names */ /* Check for duplicate parameter names */
for (p2 = desc; p2 < param; p2++) { for (p2 = desc; p2 < param; p2++) {
if (strcmp(param->name, p2->name) == 0) { if (strcmp(param->name, p2->name) == 0) {
if (is_flag(param) != is_flag(p2))
continue;
pr_err("VALIDATE %s: PARAM[%s]: Duplicate\n", pr_err("VALIDATE %s: PARAM[%s]: Duplicate\n",
name, param->name); name, param->name);
good = false; good = false;
......
...@@ -1250,6 +1250,7 @@ enum gfs2_param { ...@@ -1250,6 +1250,7 @@ enum gfs2_param {
Opt_upgrade, Opt_upgrade,
Opt_acl, Opt_acl,
Opt_quota, Opt_quota,
Opt_quota_flag,
Opt_suiddir, Opt_suiddir,
Opt_data, Opt_data,
Opt_meta, Opt_meta,
...@@ -1264,26 +1265,13 @@ enum gfs2_param { ...@@ -1264,26 +1265,13 @@ enum gfs2_param {
Opt_loccookie, Opt_loccookie,
}; };
enum opt_quota {
Opt_quota_unset = 0,
Opt_quota_off,
Opt_quota_account,
Opt_quota_on,
};
static const struct constant_table gfs2_param_quota[] = { static const struct constant_table gfs2_param_quota[] = {
{"off", Opt_quota_off }, {"off", GFS2_QUOTA_OFF},
{"account", Opt_quota_account }, {"account", GFS2_QUOTA_ACCOUNT},
{"on", Opt_quota_on }, {"on", GFS2_QUOTA_ON},
{} {}
}; };
static const unsigned int opt_quota_values[] = {
[Opt_quota_off] = GFS2_QUOTA_OFF,
[Opt_quota_account] = GFS2_QUOTA_ACCOUNT,
[Opt_quota_on] = GFS2_QUOTA_ON,
};
enum opt_data { enum opt_data {
Opt_data_writeback = GFS2_DATA_WRITEBACK, Opt_data_writeback = GFS2_DATA_WRITEBACK,
Opt_data_ordered = GFS2_DATA_ORDERED, Opt_data_ordered = GFS2_DATA_ORDERED,
...@@ -1331,8 +1319,8 @@ static const struct fs_parameter_spec gfs2_fs_parameters[] = { ...@@ -1331,8 +1319,8 @@ static const struct fs_parameter_spec gfs2_fs_parameters[] = {
fsparam_flag_no("rgrplvb", Opt_rgrplvb), fsparam_flag_no("rgrplvb", Opt_rgrplvb),
fsparam_flag_no("loccookie", Opt_loccookie), fsparam_flag_no("loccookie", Opt_loccookie),
/* quota can be a flag or an enum so it gets special treatment */ /* quota can be a flag or an enum so it gets special treatment */
__fsparam(fs_param_is_enum, "quota", Opt_quota, fsparam_flag_no("quota", Opt_quota_flag),
fs_param_neg_with_no|fs_param_v_optional, gfs2_param_quota), fsparam_enum("quota", Opt_quota, gfs2_param_quota),
{} {}
}; };
...@@ -1380,17 +1368,11 @@ static int gfs2_parse_param(struct fs_context *fc, struct fs_parameter *param) ...@@ -1380,17 +1368,11 @@ static int gfs2_parse_param(struct fs_context *fc, struct fs_parameter *param)
case Opt_acl: case Opt_acl:
args->ar_posix_acl = result.boolean; args->ar_posix_acl = result.boolean;
break; break;
case Opt_quota_flag:
args->ar_quota = result.negated ? GFS2_QUOTA_OFF : GFS2_QUOTA_ON;
break;
case Opt_quota: case Opt_quota:
/* The quota option can be a flag or an enum. A non-zero int_32 args->ar_quota = result.int_32;
result means that we have an enum index. Otherwise we have
to rely on the 'negated' flag to tell us whether 'quota' or
'noquota' was specified. */
if (result.negated)
args->ar_quota = GFS2_QUOTA_OFF;
else if (result.int_32 > 0)
args->ar_quota = opt_quota_values[result.int_32];
else
args->ar_quota = GFS2_QUOTA_ON;
break; break;
case Opt_suiddir: case Opt_suiddir:
args->ar_suiddir = result.boolean; args->ar_suiddir = result.boolean;
......
...@@ -45,6 +45,7 @@ enum nfs_param { ...@@ -45,6 +45,7 @@ enum nfs_param {
Opt_cto, Opt_cto,
Opt_fg, Opt_fg,
Opt_fscache, Opt_fscache,
Opt_fscache_flag,
Opt_hard, Opt_hard,
Opt_intr, Opt_intr,
Opt_local_lock, Opt_local_lock,
...@@ -125,8 +126,8 @@ static const struct fs_parameter_spec nfs_fs_parameters[] = { ...@@ -125,8 +126,8 @@ static const struct fs_parameter_spec nfs_fs_parameters[] = {
fsparam_string("clientaddr", Opt_clientaddr), fsparam_string("clientaddr", Opt_clientaddr),
fsparam_flag_no("cto", Opt_cto), fsparam_flag_no("cto", Opt_cto),
fsparam_flag ("fg", Opt_fg), fsparam_flag ("fg", Opt_fg),
__fsparam(fs_param_is_string, "fsc", Opt_fscache, fsparam_flag_no("fsc", Opt_fscache_flag),
fs_param_neg_with_no|fs_param_v_optional, NULL), fsparam_string("fsc", Opt_fscache),
fsparam_flag ("hard", Opt_hard), fsparam_flag ("hard", Opt_hard),
__fsparam(fs_param_is_flag, "intr", Opt_intr, __fsparam(fs_param_is_flag, "intr", Opt_intr,
fs_param_neg_with_no|fs_param_deprecated, NULL), fs_param_neg_with_no|fs_param_deprecated, NULL),
...@@ -537,14 +538,19 @@ static int nfs_fs_context_parse_param(struct fs_context *fc, ...@@ -537,14 +538,19 @@ static int nfs_fs_context_parse_param(struct fs_context *fc,
else else
ctx->flags &= ~NFS_MOUNT_NORESVPORT; ctx->flags &= ~NFS_MOUNT_NORESVPORT;
break; break;
case Opt_fscache: case Opt_fscache_flag:
kfree(ctx->fscache_uniq);
ctx->fscache_uniq = param->string;
param->string = NULL;
if (result.negated) if (result.negated)
ctx->options &= ~NFS_OPTION_FSCACHE; ctx->options &= ~NFS_OPTION_FSCACHE;
else else
ctx->options |= NFS_OPTION_FSCACHE; ctx->options |= NFS_OPTION_FSCACHE;
kfree(ctx->fscache_uniq);
ctx->fscache_uniq = NULL;
break;
case Opt_fscache:
ctx->options |= NFS_OPTION_FSCACHE;
kfree(ctx->fscache_uniq);
ctx->fscache_uniq = param->string;
param->string = NULL;
break; break;
case Opt_migration: case Opt_migration:
if (result.negated) if (result.negated)
......
...@@ -49,7 +49,6 @@ struct fs_parameter_spec { ...@@ -49,7 +49,6 @@ struct fs_parameter_spec {
u8 opt; /* Option number (returned by fs_parse()) */ u8 opt; /* Option number (returned by fs_parse()) */
enum fs_parameter_type type:8; /* The desired parameter type */ enum fs_parameter_type type:8; /* The desired parameter type */
unsigned short flags; unsigned short flags;
#define fs_param_v_optional 0x0001 /* The value is optional */
#define fs_param_neg_with_no 0x0002 /* "noxxx" is negative param */ #define fs_param_neg_with_no 0x0002 /* "noxxx" is negative param */
#define fs_param_neg_with_empty 0x0004 /* "xxx=" is negative param */ #define fs_param_neg_with_empty 0x0004 /* "xxx=" is negative param */
#define fs_param_deprecated 0x0008 /* The param is deprecated */ #define fs_param_deprecated 0x0008 /* The param is deprecated */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment