From: Karel Zak Date: Mon, 27 Feb 2023 10:33:54 +0000 (+0100) Subject: libmount: make mnt_match_options() more robust X-Git-Tag: v2.39-rc1~53 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=06ee5267516761721ebfbdfa313980cef8e54c66;p=thirdparty%2Futil-linux.git libmount: make mnt_match_options() more robust * don't allocate and use a buffer for the pattern if the pattern is empty * make it more obvious how empty pattern and empty optstr is evaluated * add regression test Reported-by: Sören Tempel Signed-off-by: Karel Zak --- diff --git a/libmount/src/optstr.c b/libmount/src/optstr.c index a8b56e212f..4fbbb08595 100644 --- a/libmount/src/optstr.c +++ b/libmount/src/optstr.c @@ -58,6 +58,8 @@ static int mnt_optstr_locate_option(char *optstr, const char *name, assert(name); namesz = strlen(name); + if (!namesz) + return 1; do { rc = ul_optstr_next(&optstr, &n, &nsz, @@ -808,6 +810,8 @@ err: * The "no" prefix interpretation could be disabled by the "+" prefix, for example * "+noauto" matches if @optstr literally contains the "noauto" string. * + * The alone "no" is error and all matching ends with False. + * * "xxx,yyy,zzz" : "nozzz" -> False * * "xxx,yyy,zzz" : "xxx,noeee" -> True @@ -820,6 +824,17 @@ err: * * "bar,zzz" : "+nofoo" -> False (does not contain "nofoo") * + * "bar,zzz" : "" or "+" -> True (empty pattern is matching) + * + * "" : "" -> True + * + * "" : "foo" -> False + * + * "" : "nofoo" -> True + * + * "" : "no,foo" -> False (alone "no" is error) + * + * "no" : "+no" -> True ("no" is an option due to "+") * * Returns: 1 if pattern is matching, else 0. This function also returns 0 * if @pattern is NULL and @optstr is non-NULL. @@ -827,35 +842,50 @@ err: int mnt_match_options(const char *optstr, const char *pattern) { char *name, *pat = (char *) pattern; - char *buf, *patval; + char *buf = NULL, *patval; size_t namesz = 0, patvalsz = 0; int match = 1; if (!pattern && !optstr) return 1; + if (pattern && optstr && !*pattern && !*optstr) + return 1; if (!pattern) return 0; - buf = malloc(strlen(pattern) + 1); - if (!buf) - return 0; - /* walk on pattern string */ while (match && !mnt_optstr_next_option(&pat, &name, &namesz, &patval, &patvalsz)) { char *val; - size_t sz; + size_t sz = 0; int no = 0, rc; if (*name == '+') name++, namesz--; - else if ((no = (startswith(name, "no") != NULL))) + else if ((no = (startswith(name, "no") != NULL))) { name += 2, namesz -= 2; + if (!*name) { + match = 0; + break; /* alone "no" keyword is error */ + } + } - xstrncpy(buf, name, namesz + 1); + if (optstr && *optstr && *name) { + if (!buf) { + buf = malloc(strlen(pattern) + 1); + if (!buf) + return 0; + } - rc = mnt_optstr_get_option(optstr, buf, &val, &sz); + xstrncpy(buf, name, namesz + 1); + rc = mnt_optstr_get_option(optstr, buf, &val, &sz); + + } else if (!*name) { + rc = 0; /* empty pattern matches */ + } else { + rc = 1; /* not found in empty string */ + } /* check also value (if the pattern is "foo=value") */ if (rc == 0 && patvalsz > 0 && @@ -873,7 +903,6 @@ int mnt_match_options(const char *optstr, const char *pattern) match = 0; break; } - } free(buf); @@ -1091,6 +1120,21 @@ static int test_dedup(struct libmnt_test *ts, int argc, char *argv[]) return rc; } +static int test_match(struct libmnt_test *ts, int argc, char *argv[]) +{ + char *optstr, *pattern; + + if (argc < 3) + return -EINVAL; + + optstr = argv[1]; + pattern = argv[2]; + printf("%-6s: \"%s\"\t:\t\"%s\"\n", + mnt_match_options(optstr, pattern) == 1 ? "true" : "false", + optstr, pattern); + return 0; +} + int main(int argc, char *argv[]) { struct libmnt_test tss[] = { @@ -1100,6 +1144,7 @@ int main(int argc, char *argv[]) { "--get", test_get, " search name in optstr" }, { "--remove", test_remove, " remove name in optstr" }, { "--dedup", test_dedup, " deduplicate name in optstr" }, + { "--match", test_match, " compare optstr with pattern" }, { "--split", test_split, " split into FS, VFS and userspace" }, { "--flags", test_flags, " convert options to MS_* flags" }, { "--apply", test_apply, "--{linux,user} apply mask to optstr" }, diff --git a/tests/expected/libmount/optstr-match b/tests/expected/libmount/optstr-match new file mode 100644 index 0000000000..1fc862fff8 --- /dev/null +++ b/tests/expected/libmount/optstr-match @@ -0,0 +1,13 @@ +false : "xxx,yyy,zzz" : "nozzz" +true : "xxx,yyy,zzz" : "xxx,noeee" +true : "bar,zzz" : "nofoo" +true : "nofoo,bar" : "nofoo" +true : "nofoo,bar" : "+nofoo" +false : "bar,zzz" : "+nofoo" +true : "bar,zzz" : "" +true : "bar,zzz" : "+" +true : "" : "" +false : "" : "foo" +true : "" : "nofoo" +false : "" : "no,foo" +true : "no" : "+no" diff --git a/tests/ts/libmount/optstr b/tests/ts/libmount/optstr index 65fa8a31ac..26d9b73f4a 100755 --- a/tests/ts/libmount/optstr +++ b/tests/ts/libmount/optstr @@ -141,4 +141,20 @@ ts_init_subtest "deduplicate-empty" ts_run $TESTPROG --dedup bbb,ccc,AAA,xxx,AAA=a,AAA=bbb,ddd,AAA=,fff=eee AAA &> $TS_OUTPUT ts_finalize_subtest +ts_init_subtest "match" +ts_run $TESTPROG --match "xxx,yyy,zzz" "nozzz" &>> $TS_OUTPUT +ts_run $TESTPROG --match "xxx,yyy,zzz" "xxx,noeee" &>> $TS_OUTPUT +ts_run $TESTPROG --match "bar,zzz" "nofoo" &>> $TS_OUTPUT +ts_run $TESTPROG --match "nofoo,bar" "nofoo" &>> $TS_OUTPUT +ts_run $TESTPROG --match "nofoo,bar" "+nofoo" &>> $TS_OUTPUT +ts_run $TESTPROG --match "bar,zzz" "+nofoo" &>> $TS_OUTPUT +ts_run $TESTPROG --match "bar,zzz" "" &>> $TS_OUTPUT +ts_run $TESTPROG --match "bar,zzz" "+" &>> $TS_OUTPUT +ts_run $TESTPROG --match "" "" &>> $TS_OUTPUT +ts_run $TESTPROG --match "" "foo" &>> $TS_OUTPUT +ts_run $TESTPROG --match "" "nofoo" &>> $TS_OUTPUT +ts_run $TESTPROG --match "" "no,foo" &>> $TS_OUTPUT +ts_run $TESTPROG --match "no" "+no" &>> $TS_OUTPUT +ts_finalize_subtest + ts_finalize