diff options
author | Lucas De Marchi <lucas.demarchi@intel.com> | 2021-02-12 01:45:21 -0800 |
---|---|---|
committer | Lucas De Marchi <lucas.demarchi@intel.com> | 2021-02-15 11:53:38 -0800 |
commit | 01ed9af61e239b40514edf527ac87c79377266ac (patch) | |
tree | dd616f3b372100e46fb4eea067ca4ab4d2c5e714 | |
parent | fd71604da9d9bab91f270deacceecc0d5c0fcffd (diff) | |
download | kmod-01ed9af61e239b40514edf527ac87c79377266ac.tar.gz kmod-01ed9af61e239b40514edf527ac87c79377266ac.tar.bz2 kmod-01ed9af61e239b40514edf527ac87c79377266ac.zip |
libkmod-config: revamp kcmdline parsing into a state machine
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>
-rw-r--r-- | libkmod/libkmod-config.c | 86 |
1 files changed, 52 insertions, 34 deletions
diff --git a/libkmod/libkmod-config.c b/libkmod/libkmod-config.c index 971f20b..d3cd10d 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; } |