Merge pull request #18955 from keszybz/fstab-escaping

Fix handling of escaped characters in fstab
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2021-03-11 14:45:11 +01:00
committed by GitHub
12 changed files with 272 additions and 192 deletions
+79 -88
View File
@@ -79,34 +79,95 @@ int fstab_is_mount_point(const char *mount) {
return false;
}
int fstab_filter_options(const char *opts, const char *names,
const char **ret_namefound, char **ret_value, char **ret_filtered) {
int fstab_filter_options(
const char *opts,
const char *names,
const char **ret_namefound,
char **ret_value,
char ***ret_values,
char **ret_filtered) {
const char *name, *namefound = NULL, *x;
_cleanup_strv_free_ char **stor = NULL;
_cleanup_free_ char *v = NULL, **strv = NULL;
_cleanup_strv_free_ char **stor = NULL, **values = NULL;
_cleanup_free_ char *value = NULL, **filtered = NULL;
int r;
assert(names && *names);
assert(!(ret_value && ret_values));
if (!opts)
goto answer;
/* If !ret_value and !ret_filtered, this function is not allowed to fail. */
/* Finds any options matching 'names', and returns:
* - the last matching option name in ret_namefound,
* - the last matching value in ret_value,
* - any matching values in ret_values,
* - the rest of the option string in ret_filtered.
*
* If !ret_value and !ret_values and !ret_filtered, this function is not allowed to fail.
*
* Returns negative on error, true if any matching options were found, false otherwise. */
if (!ret_filtered) {
if (ret_filtered || ret_value || ret_values) {
/* 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(&stor, opts, ",", EXTRACT_UNESCAPE_SEPARATORS | EXTRACT_UNESCAPE_RELAX);
if (r < 0)
return r;
filtered = memdup(stor, sizeof(char*) * (strv_length(stor) + 1));
if (!filtered)
return -ENOMEM;
char **t = filtered;
for (char **s = t; *s; s++) {
NULSTR_FOREACH(name, names) {
x = startswith(*s, name);
if (!x)
continue;
/* Match name, but when ret_values, only when followed by assignment. */
if (*x == '=' || (!ret_values && *x == '\0'))
goto found;
}
*t = *s;
t++;
continue;
found:
/* Keep the last occurrence found */
namefound = name;
if (ret_value || ret_values) {
assert(IN_SET(*x, '=', '\0'));
if (ret_value) {
r = free_and_strdup(&value, *x == '=' ? x + 1 : NULL);
if (r < 0)
return r;
} else if (*x) {
r = strv_extend(&values, x + 1);
if (r < 0)
return r;
}
}
}
*t = NULL;
} else
for (const char *word = opts;;) {
const char *end = word;
/* Look for an *non-escaped* comma separator. Only commas can be escaped, so "\," is
* the only valid escape sequence, so we can do a very simple test here. */
/* Look for a *non-escaped* comma separator. Only commas and backslashes can be
* escaped, so "\," and "\\" are the only valid escape sequences, and we can do a
* very simple test here. */
for (;;) {
size_t n = strcspn(end, ",");
end += strcspn(end, ",\\");
end += n;
if (n > 0 && end[-1] == '\\')
end++;
else
if (IN_SET(*end, ',', '\0'))
break;
assert(*end == '\\');
end ++; /* Skip the backslash */
if (*end != '\0')
end ++; /* Skip the escaped char, but watch out for a trailing commma */
}
NULSTR_FOREACH(name, names) {
@@ -119,17 +180,6 @@ int fstab_filter_options(const char *opts, const char *names,
x = word + strlen(name);
if (IN_SET(*x, '\0', '=', ',')) {
namefound = name;
if (ret_value) {
bool eq = *x == '=';
assert(eq || IN_SET(*x, ',', '\0'));
r = free_and_strndup(&v,
eq ? x + 1 : NULL,
eq ? end - x - 1 : 0);
if (r < 0)
return r;
}
break;
}
}
@@ -139,38 +189,6 @@ int fstab_filter_options(const char *opts, const char *names,
else
break;
}
} else {
r = strv_split_full(&stor, opts, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
if (r < 0)
return r;
strv = memdup(stor, sizeof(char*) * (strv_length(stor) + 1));
if (!strv)
return -ENOMEM;
char **t = strv;
for (char **s = strv; *s; s++) {
NULSTR_FOREACH(name, names) {
x = startswith(*s, name);
if (x && IN_SET(*x, '\0', '='))
goto found;
}
*t = *s;
t++;
continue;
found:
/* Keep the last occurrence found */
namefound = name;
if (ret_value) {
assert(IN_SET(*x, '=', '\0'));
r = free_and_strdup(&v, *x == '=' ? x + 1 : NULL);
if (r < 0)
return r;
}
}
*t = NULL;
}
answer:
if (ret_namefound)
@@ -178,54 +196,27 @@ answer:
if (ret_filtered) {
char *f;
f = strv_join_full(strv, ",", NULL, true);
f = strv_join_full(filtered, ",", NULL, true);
if (!f)
return -ENOMEM;
*ret_filtered = f;
}
if (ret_value)
*ret_value = TAKE_PTR(v);
*ret_value = TAKE_PTR(value);
if (ret_values)
*ret_values = TAKE_PTR(values);
return !!namefound;
}
int fstab_extract_values(const char *opts, const char *name, char ***values) {
_cleanup_strv_free_ char **optsv = NULL, **res = NULL;
char **s;
assert(opts);
assert(name);
assert(values);
optsv = strv_split(opts, ",");
if (!optsv)
return -ENOMEM;
STRV_FOREACH(s, optsv) {
char *arg;
int r;
arg = startswith(*s, name);
if (!arg || *arg != '=')
continue;
r = strv_extend(&res, arg + 1);
if (r < 0)
return r;
}
*values = TAKE_PTR(res);
return !!*values;
}
int fstab_find_pri(const char *options, int *ret) {
_cleanup_free_ char *opt = NULL;
int r, pri;
assert(ret);
r = fstab_filter_options(options, "pri\0", NULL, &opt, NULL);
r = fstab_filter_options(options, "pri\0", NULL, &opt, NULL, NULL);
if (r < 0)
return r;
if (r == 0 || !opt)
+9 -5
View File
@@ -10,12 +10,16 @@ bool fstab_is_extrinsic(const char *mount, const char *opts);
int fstab_is_mount_point(const char *mount);
int fstab_has_fstype(const char *fstype);
int fstab_filter_options(const char *opts, const char *names, const char **namefound, char **value, char **filtered);
int fstab_extract_values(const char *opts, const char *name, char ***values);
int fstab_filter_options(
const char *opts,
const char *names,
const char **ret_namefound,
char **ret_value,
char ***ret_values,
char **ret_filtered);
static inline bool fstab_test_option(const char *opts, const char *names) {
return !!fstab_filter_options(opts, names, NULL, NULL, NULL);
return !!fstab_filter_options(opts, names, NULL, NULL, NULL, NULL);
}
int fstab_find_pri(const char *options, int *ret);
@@ -26,7 +30,7 @@ static inline bool fstab_test_yes_no_option(const char *opts, const char *yes_no
/* If first name given is last, return 1.
* If second name given is last or neither is found, return 0. */
assert_se(fstab_filter_options(opts, yes_no, &opt, NULL, NULL) >= 0);
assert_se(fstab_filter_options(opts, yes_no, &opt, NULL, NULL, NULL) >= 0);
return opt == yes_no;
}
+7 -3
View File
@@ -215,9 +215,13 @@ int generator_write_timeouts(
r = fstab_filter_options(opts, "comment=systemd.device-timeout\0"
"x-systemd.device-timeout\0",
NULL, &timeout, filtered);
if (r <= 0)
return r;
NULL, &timeout, NULL, filtered);
if (r < 0) {
log_warning_errno(r, "Failed to parse fstab options, ignoring: %m");
return 0;
}
if (r == 0)
return 0;
r = parse_sec_fix_0(timeout, &u);
if (r < 0) {