diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c index 5ebf67437c..c21ac1fde5 100644 --- a/src/basic/extract-word.c +++ b/src/basic/extract-word.c @@ -78,7 +78,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra return -EINVAL; } - if (flags & (EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS)) { + if (flags & (EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS|EXTRACT_UNESCAPE_RELAX)) { bool eight_bit = false; char32_t u; diff --git a/src/basic/strv.c b/src/basic/strv.c index a05da77e50..443ae9028d 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -529,7 +529,7 @@ int strv_split_colon_pairs(char ***t, const char *s) { return (int) n; } -char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separator) { +char* strv_join_full(char * const *l, const char *separator, const char *prefix) { char *r, *e; size_t n, k, m; @@ -539,17 +539,12 @@ char* strv_join_full(char * const *l, const char *separator, const char *prefix, k = strlen(separator); m = strlen_ptr(prefix); - if (escape_separator) /* If the separator was multi-char, we wouldn't know how to escape it. */ - assert(k == 1); - n = 0; STRV_FOREACH(s, l) { if (s != l) n += k; - bool needs_escaping = escape_separator && strchr(*s, *separator); - - n += m + strlen(*s) * (1 + needs_escaping); + n += m + strlen(*s); } r = new(char, n+1); @@ -564,16 +559,7 @@ char* strv_join_full(char * const *l, const char *separator, const char *prefix, if (prefix) e = stpcpy(e, prefix); - bool needs_escaping = escape_separator && strchr(*s, *separator); - - if (needs_escaping) - for (size_t i = 0; (*s)[i]; i++) { - if ((*s)[i] == *separator) - *(e++) = '\\'; - *(e++) = (*s)[i]; - } - else - e = stpcpy(e, *s); + e = stpcpy(e, *s); } *e = 0; diff --git a/src/basic/strv.h b/src/basic/strv.h index 2a0c2730d1..5afd345358 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -120,9 +120,9 @@ char** strv_split_newlines(const char *s); * string in the vector is an empty string. */ int strv_split_colon_pairs(char ***t, const char *s); -char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separator); +char* strv_join_full(char * const *l, const char *separator, const char *prefix); static inline char *strv_join(char * const *l, const char *separator) { - return strv_join_full(l, separator, NULL, false); + return strv_join_full(l, separator, NULL); } bool strv_overlap(char * const *a, char * const *b) _pure_; diff --git a/src/kernel-install/kernel-install.c b/src/kernel-install/kernel-install.c index be097cd5cd..f9cc40fd7d 100644 --- a/src/kernel-install/kernel-install.c +++ b/src/kernel-install/kernel-install.c @@ -1172,10 +1172,10 @@ static int context_execute(Context *c) { return r; if (DEBUG_LOGGING) { - _cleanup_free_ char *x = strv_join_full(c->plugins, "", "\n ", /* escape_separator= */ false); + _cleanup_free_ char *x = strv_join_full(c->plugins, "", "\n "); log_debug("Using plugins: %s", strna(x)); - _cleanup_free_ char *y = strv_join_full(c->envp, "", "\n ", /* escape_separator= */ false); + _cleanup_free_ char *y = strv_join_full(c->envp, "", "\n "); log_debug("Plugin environment: %s", strna(y)); _cleanup_free_ char *z = strv_join(strv_skip(c->argv, 1), " "); diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c index 25e229bf3e..a4f5e348ab 100644 --- a/src/shared/fstab-util.c +++ b/src/shared/fstab-util.c @@ -200,6 +200,34 @@ int fstab_is_mount_point_full(const char *where, const char *path) { #endif } +static char* fstab_unescape_option(const char *s) { + /* Undo the fstab option escaping: "\," -> ",", "\\" -> "\", and keep any + * other "\x" verbatim (matches EXTRACT_UNESCAPE_SEPARATORS|EXTRACT_UNESCAPE_RELAX). */ + char *ret, *e; + + assert(s); + + ret = new(char, strlen(s) + 1); + if (!ret) + return NULL; + + e = ret; + for (const char *p = s; *p; p++) { + if (*p == '\\' && p[1] != '\0') { + if (IN_SET(p[1], ',', '\\')) { + *e++ = p[1]; + p++; + continue; + } + /* Keep the backslash and the following character verbatim. */ + *e++ = *p++; + } + *e++ = *p; + } + *e = '\0'; + return ret; +} + int fstab_filter_options( const char *opts, const char *names, @@ -234,8 +262,10 @@ int fstab_filter_options( _cleanup_free_ char **filtered_strv = NULL; /* strings are owned by 'opts_split' */ /* For backwards compatibility, we need to pass-through escape characters. - * The only ones we "consume" are the ones used as "\," or "\\". */ - r = strv_split_full(&opts_split, opts, ",", EXTRACT_UNESCAPE_SEPARATORS|EXTRACT_UNESCAPE_RELAX); + * The only ones we "consume" are the ones used as "\," or "\\". The option + * values are not unescaped by strv_split_full() here; matching option values are + * unescaped manually below so that callers keep getting the unescaped form. */ + r = strv_split_full(&opts_split, opts, ",", EXTRACT_UNESCAPE_RELAX); if (r < 0) return r; @@ -257,10 +287,18 @@ int fstab_filter_options( } if (found) { + _cleanup_free_ char *unescaped = NULL; + + if ((ret_value && *x == '=') || ret_values) { + unescaped = fstab_unescape_option(x + 1); + if (!unescaped) + return -ENOMEM; + } + if (ret_value) - r = free_and_strdup(&value, *x == '=' ? x + 1 : NULL); + r = free_and_replace(value, unescaped); else if (ret_values) - r = strv_extend(&values, x + 1); + r = strv_extend(&values, unescaped); else r = 0; } else @@ -270,7 +308,7 @@ int fstab_filter_options( } if (ret_filtered) { - filtered = strv_join_full(filtered_strv, ",", NULL, /* escape_separator= */ true); + filtered = strv_join(filtered_strv, ","); if (!filtered) return -ENOMEM; } diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c index aad9bbc47d..3e11f5922b 100644 --- a/src/test/test-extract-word.c +++ b/src/test/test-extract-word.c @@ -247,13 +247,13 @@ TEST(extract_first_word) { p = original = "fooo\\ bar quux"; assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX) > 0); - ASSERT_STREQ(t, "fooo bar"); + ASSERT_STREQ(t, "fooo\\ bar"); free(t); assert_se(p == original + 10); p = original = "fooo\\ bar quux"; assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0); - ASSERT_STREQ(t, "fooo bar"); + ASSERT_STREQ(t, "fooo\\ bar"); free(t); assert_se(p == original + 10); diff --git a/src/test/test-fstab-util.c b/src/test/test-fstab-util.c index 773b1f9799..63b70863e6 100644 --- a/src/test/test-fstab-util.c +++ b/src/test/test-fstab-util.c @@ -125,7 +125,7 @@ TEST(fstab_filter_options) { /* escaped characters */ do_fstab_filter_options("opt1=\\\\,opt2=\\xff", "opt1\0", 1, 1, "opt1", "\\", "\\", "opt2=\\xff"); - do_fstab_filter_options("opt1=\\\\,opt2=\\xff", "opt2\0", 1, 1, "opt2", "\\xff", "\\xff", "opt1=\\"); + do_fstab_filter_options("opt1=\\\\,opt2=\\xff", "opt2\0", 1, 1, "opt2", "\\xff", "\\xff", "opt1=\\\\"); } TEST(fstab_find_pri) { diff --git a/src/test/test-strv.c b/src/test/test-strv.c index f94f1e328f..33480a8a25 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -159,44 +159,31 @@ TEST(strv_join) { } TEST(strv_join_full) { - _cleanup_free_ char *p = strv_join_full((char **)input_table_multiple, ", ", "foo", false); + _cleanup_free_ char *p = strv_join_full((char **)input_table_multiple, ", ", "foo"); assert_se(p); ASSERT_STREQ(p, "fooone, footwo, foothree"); - _cleanup_free_ char *q = strv_join_full((char **)input_table_multiple, ";", "foo", false); + _cleanup_free_ char *q = strv_join_full((char **)input_table_multiple, ";", "foo"); assert_se(q); ASSERT_STREQ(q, "fooone;footwo;foothree"); - _cleanup_free_ char *r = strv_join_full(STRV_MAKE("a", "a;b", "a:c"), ";", NULL, true); - assert_se(r); - ASSERT_STREQ(r, "a;a\\;b;a:c"); - - _cleanup_free_ char *s = strv_join_full(STRV_MAKE("a", "a;b", "a;;c", ";", ";x"), ";", NULL, true); - assert_se(s); - ASSERT_STREQ(s, "a;a\\;b;a\\;\\;c;\\;;\\;x"); - - _cleanup_free_ char *t = strv_join_full(STRV_MAKE("a", "a;b", "a:c", ";"), ";", "=", true); - assert_se(t); - ASSERT_STREQ(t, "=a;=a\\;b;=a:c;=\\;"); - t = mfree(t); - - _cleanup_free_ char *u = strv_join_full((char **)input_table_multiple, NULL, "foo", false); + _cleanup_free_ char *u = strv_join_full((char **)input_table_multiple, NULL, "foo"); assert_se(u); ASSERT_STREQ(u, "fooone footwo foothree"); - _cleanup_free_ char *v = strv_join_full((char **)input_table_one, ", ", "foo", false); + _cleanup_free_ char *v = strv_join_full((char **)input_table_one, ", ", "foo"); assert_se(v); ASSERT_STREQ(v, "fooone"); - _cleanup_free_ char *w = strv_join_full((char **)input_table_none, ", ", "foo", false); + _cleanup_free_ char *w = strv_join_full((char **)input_table_none, ", ", "foo"); assert_se(w); ASSERT_STREQ(w, ""); - _cleanup_free_ char *x = strv_join_full((char **)input_table_two_empties, ", ", "foo", false); + _cleanup_free_ char *x = strv_join_full((char **)input_table_two_empties, ", ", "foo"); assert_se(x); ASSERT_STREQ(x, "foo, foo"); - _cleanup_free_ char *y = strv_join_full((char **)input_table_one_empty, ", ", "foo", false); + _cleanup_free_ char *y = strv_join_full((char **)input_table_one_empty, ", ", "foo"); assert_se(y); ASSERT_STREQ(y, "foo"); }