144 lines
3.8 KiB
Diff
144 lines
3.8 KiB
Diff
|
From: Lucas De Marchi <lucas.demarchi@intel.com>
|
||
|
Date: Fri, 12 Feb 2021 01:45:21 -0800
|
||
|
Subject: libkmod-config: revamp kcmdline parsing into a state machine
|
||
|
Git-repo: git://git.kernel.org/pub/scm/utils/kernel/kmod/kmod.git
|
||
|
Git-commit: 01ed9af61e239b40514edf527ac87c79377266ac
|
||
|
Patch-mainline: v29
|
||
|
References: bsc#1181111
|
||
|
|
||
|
The handling of spaces and quotes is becoming hard to maintain. Convert
|
||
|
the parser into a state machine so we can check all the states. This
|
||
|
should make it easier to fix a corner case we have right now:
|
||
|
The kernel also accepts a quote before the module name instead of the
|
||
|
value. But this additional is left for later. This is purely an
|
||
|
algorithm change with no behavior change.
|
||
|
|
||
|
Tested-by: Jessica Yu <jeyu@kernel.org>
|
||
|
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
|
||
|
---
|
||
|
libkmod/libkmod-config.c | 86 ++++++++++++++++++++++++----------------
|
||
|
1 file changed, 52 insertions(+), 34 deletions(-)
|
||
|
|
||
|
diff --git a/libkmod/libkmod-config.c b/libkmod/libkmod-config.c
|
||
|
index 971f20b8a352..d3cd10d42a10 100644
|
||
|
--- a/libkmod/libkmod-config.c
|
||
|
+++ b/libkmod/libkmod-config.c
|
||
|
@@ -499,7 +499,14 @@ static int kmod_config_parse_kcmdline(struct kmod_config *config)
|
||
|
char buf[KCMD_LINE_SIZE];
|
||
|
int fd, err;
|
||
|
char *p, *modname, *param = NULL, *value = NULL;
|
||
|
- bool is_quoted = false, is_module = true;
|
||
|
+ bool is_quoted = false, iter = true;
|
||
|
+ enum state {
|
||
|
+ STATE_IGNORE,
|
||
|
+ STATE_MODNAME,
|
||
|
+ STATE_PARAM,
|
||
|
+ STATE_VALUE,
|
||
|
+ STATE_COMPLETE,
|
||
|
+ } state;
|
||
|
|
||
|
fd = open("/proc/cmdline", O_RDONLY|O_CLOEXEC);
|
||
|
if (fd < 0) {
|
||
|
@@ -516,54 +523,65 @@ static int kmod_config_parse_kcmdline(struct kmod_config *config)
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
- for (p = buf, modname = buf; *p != '\0' && *p != '\n'; p++) {
|
||
|
- if (*p == '"') {
|
||
|
+ state = STATE_MODNAME;
|
||
|
+ for (p = buf, modname = buf; iter; p++) {
|
||
|
+ switch (*p) {
|
||
|
+ case '"':
|
||
|
is_quoted = !is_quoted;
|
||
|
-
|
||
|
- if (is_quoted) {
|
||
|
- /* don't consider a module until closing quotes */
|
||
|
- is_module = false;
|
||
|
- } else if (param != NULL && value != NULL) {
|
||
|
+ break;
|
||
|
+ case '\0':
|
||
|
+ case '\n':
|
||
|
+ /* Stop iterating on new chars */
|
||
|
+ iter = false;
|
||
|
+ /* fall-through */
|
||
|
+ case ' ':
|
||
|
+ if (is_quoted && state == STATE_VALUE) {
|
||
|
+ /* no state change*/;
|
||
|
+ } else if (is_quoted) {
|
||
|
+ /* spaces are only allowed in the value part */
|
||
|
+ state = STATE_IGNORE;
|
||
|
+ } else if (state == STATE_VALUE || state == STATE_PARAM) {
|
||
|
+ *p = '\0';
|
||
|
+ state = STATE_COMPLETE;
|
||
|
+ } else {
|
||
|
/*
|
||
|
- * If we are indeed expecting a value and
|
||
|
- * closing quotes, then this can be considered
|
||
|
- * a valid option for a module
|
||
|
+ * go to next option, ignoring any possible
|
||
|
+ * partial match we have
|
||
|
*/
|
||
|
- is_module = true;
|
||
|
+ modname = p + 1;
|
||
|
+ state = STATE_MODNAME;
|
||
|
}
|
||
|
-
|
||
|
- continue;
|
||
|
- }
|
||
|
- if (is_quoted)
|
||
|
- continue;
|
||
|
-
|
||
|
- switch (*p) {
|
||
|
- case ' ':
|
||
|
- *p = '\0';
|
||
|
- if (is_module)
|
||
|
- kcmdline_parse_result(config, modname, param, value);
|
||
|
- param = value = NULL;
|
||
|
- modname = p + 1;
|
||
|
- is_module = true;
|
||
|
break;
|
||
|
case '.':
|
||
|
- if (param == NULL) {
|
||
|
+ if (state == STATE_MODNAME) {
|
||
|
*p = '\0';
|
||
|
param = p + 1;
|
||
|
+ state = STATE_PARAM;
|
||
|
+ } else if (state == STATE_PARAM) {
|
||
|
+ state = STATE_IGNORE;
|
||
|
}
|
||
|
break;
|
||
|
case '=':
|
||
|
- if (param != NULL)
|
||
|
+ if (state == STATE_PARAM) {
|
||
|
+ /*
|
||
|
+ * Don't set *p to '\0': the value var shadows
|
||
|
+ * param
|
||
|
+ */
|
||
|
value = p + 1;
|
||
|
- else
|
||
|
- is_module = false;
|
||
|
+ state = STATE_VALUE;
|
||
|
+ } else if (state == STATE_MODNAME) {
|
||
|
+ state = STATE_IGNORE;
|
||
|
+ }
|
||
|
break;
|
||
|
}
|
||
|
- }
|
||
|
|
||
|
- *p = '\0';
|
||
|
- if (is_module)
|
||
|
- kcmdline_parse_result(config, modname, param, value);
|
||
|
+ if (state == STATE_COMPLETE) {
|
||
|
+ kcmdline_parse_result(config, modname, param, value);
|
||
|
+ /* start over on next iteration */
|
||
|
+ modname = p + 1;
|
||
|
+ state = STATE_MODNAME;
|
||
|
+ }
|
||
|
+ }
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
--
|
||
|
2.30.1
|
||
|
|