fstab-filter: preserve fstab option escaping in the filtered output
fstab_filter_options() used strv_split_full(..., EXTRACT_UNESCAPE_SEPARATORS |
EXTRACT_UNESCAPE_RELAX), which unescapes "\," and "\\" while splitting, and then
strv_join_full(escape_separator=true) to re-escape the kept options. The round-trip
was lossy for invalid escapes kept verbatim by RELAX (e.g. an option value "\xff"
became "\\xff"), so the generated Options= line diverged from the input. A trailing
backslash was also collapsed: "val1\\" in the fstab became "val1\" in the split
word, which let the backslash escape the option separator in Options= (issue #42787).
Rework fstab_filter_options() to split with EXTRACT_UNESCAPE_RELAX only, so the
escape sequences of kept options are preserved verbatim and the filtered output is
assembled with the plain strv_join(); the matching option values are unescaped
manually so callers keep getting the unescaped form they had before.
Fixes a bug introduced by d4d9f034b1.
Fixes #42787.
This commit is contained in:
+43
-5
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user