Re: [PATCH] vfs: Don't reject unknown parameters
From: Al Viro
Date: Thu Dec 12 2019 - 16:36:20 EST
On Thu, Dec 12, 2019 at 03:01:56PM -0500, Laura Abbott wrote:
> +static const struct fs_parameter_spec no_opt_fs_param_specs[] = {
> + fsparam_string ("source", NO_OPT_SOURCE),
> + {}
> +};
> +
> +const struct fs_parameter_description no_opt_fs_parameters = {
> + .name = "no_opt_fs",
> + .specs = no_opt_fs_param_specs,
> +};
> +
> +int fs_no_opt_parse_param(struct fs_context *fc, struct fs_parameter *param)
> +{
> + struct fs_parse_result result;
> + int opt;
> +
> + opt = fs_parse(fc, &no_opt_fs_parameters, param, &result);
> + if (opt < 0) {
> + /* Just log an error for backwards compatibility */
> + errorf(fc, "%s: Unknown parameter '%s'",
> + fc->fs_type->name, param->key);
> + return 0;
> + }
> +
> + switch (opt) {
> + case NO_OPT_SOURCE:
> + if (param->type != fs_value_is_string)
> + return invalf(fc, "%s: Non-string source",
> + fc->fs_type->name);
> + if (fc->source)
> + return invalf(fc, "%s: Multiple sources specified",
> + fc->fs_type->name);
> + fc->source = param->string;
> + param->string = NULL;
> + break;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(fs_no_opt_parse_param);
Yecchhhh... Could you explain why do you want to bother with fs_parse()
here? Seriously, look at it.
{
const struct fs_parameter_spec *p;
const struct fs_parameter_enum *e;
int ret = -ENOPARAM, b;
result->has_value = !!param->string;
result->negated = false;
result->uint_64 = 0;
p = fs_lookup_key(desc, param->key);
OK, that's
if (strcmp(param->key, "source") == 0)
p = no_opt_fs_param_specs;
else
p = NULL;
if (!p) {
not "source"
/* If we didn't find something that looks like "noxxx", see if
* "xxx" takes the "no"-form negative - but only if there
* wasn't an value.
*/
if (result->has_value)
goto unknown_parameter;
if param->string is non-NULL - piss off
if (param->key[0] != 'n' || param->key[1] != 'o' || !param->key[2])
goto unknown_parameter;
if not "no"<something> - ditto
p = fs_lookup_key(desc, param->key + 2);
if (!p)
goto unknown_parameter;
if not "nosource" - ditto
if (!(p->flags & fs_param_neg_with_no))
goto unknown_parameter;
... and since ->flags doesn't have that bit, the same for "nosource" anyway.
result->boolean = false;
result->negated = true;
won't get here
}
OK, so the above is simply 'piss off unless param->key is "source"'. And
p is no_opt_fs_param_specs.
if (p->flags & fs_param_deprecated)
nope
warnf(fc, "%s: Deprecated parameter '%s'",
desc->name, param->key);
if (result->negated)
goto okay;
nope - set to false, never changed
/* Certain parameter types only take a string and convert it. */
switch (p->type) {
that'd be fs_param_is_string
...
case fs_param_is_string:
if (param->type != fs_value_is_string)
goto bad_value;
if (!result->has_value) {
if param->string is NULL...
if (p->flags & fs_param_v_optional)
nope
goto okay;
goto bad_value;
}
/* Fall through */
default:
break;
}
/* Try to turn the type we were given into the type desired by the
* parameter and give an error if we can't.
*/
switch (p->type) {
again, fs_param_is_string
...
case fs_param_is_string:
goto okay;
...
okay:
return p->opt;
bad_value:
return invalf(fc, "%s: Bad value for '%s'", desc->name, param->key);
unknown_parameter:
return -ENOPARAM;
In other words, that thing is equivalent to
if (strcmp(param->key, "source")) {
errorf(fc, "%s: Unknown parameter '%s'",
fc->fs_type->name, param->key);
return 0;
}
if (param->type != fs_value_is_string || !param->value) {
invalf(fc, "%s: Bad value for '%s'", fc->fs_type->name, param->key);
errorf(fc, "%s: Unknown parameter '%s'",
fc->fs_type->name, param->key);
return 0; // almost certainly not what you intended for that case
}
if (fc->source)
return invalf(fc, "%s: Multiple sources specified", fc->fs_type->name);
fc->source = param->string;
param->string = NULL;
return 0;
And that - without the boilerplate from hell. But if you look at the caller of
that method, you'll see this:
if (fc->ops->parse_param) {
ret = fc->ops->parse_param(fc, param);
if (ret != -ENOPARAM)
return ret;
}
/* If the filesystem doesn't take any arguments, give it the
* default handling of source.
*/
if (strcmp(param->key, "source") == 0) {
if (param->type != fs_value_is_string)
return invalf(fc, "VFS: Non-string source");
if (fc->source)
return invalf(fc, "VFS: Multiple sources");
fc->source = param->string;
param->string = NULL;
return 0;
}
return invalf(fc, "%s: Unknown parameter '%s'",
fc->fs_type->name, param->key);
}
So you could bloody well just leave recognition (and handling) of "source"
to the caller, leaving you with just this:
if (strcmp(param->key, "source") == 0)
return -ENOPARAM;
/* Just log an error for backwards compatibility */
errorf(fc, "%s: Unknown parameter '%s'", fc->fs_type->name, param->key);
return 0;
and that's it.