core: fix bind paths whitespace parsing

Stop warning on extra spaces in BindPaths= and BindReadOnlyPaths=.
Keep bind path parsing and serialization consistent.

Fixes: #43214
This commit is contained in:
dongshengyuan
2026-08-15 00:26:40 +09:00
committed by Yu Watanabe
parent 77b9a626ce
commit 507ba1e51c
5 changed files with 190 additions and 153 deletions
+71 -115
View File
@@ -2253,17 +2253,17 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
FOREACH_ARRAY(mount, c->bind_mounts, c->n_bind_mounts) {
_cleanup_free_ char *src_escaped = NULL, *dst_escaped = NULL;
src_escaped = shell_escape(mount->source, ":" WHITESPACE);
src_escaped = shell_escape(mount->source, ":\"");
if (!src_escaped)
return log_oom_debug();
dst_escaped = shell_escape(mount->destination, ":" WHITESPACE);
dst_escaped = shell_escape(mount->destination, ":\"");
if (!dst_escaped)
return log_oom_debug();
r = serialize_item_format(f,
mount->read_only ? "exec-context-bind-read-only-path" : "exec-context-bind-path",
"%s%s:%s:%s",
"\"%s%s:%s:%s\"",
mount->ignore_enoent ? "-" : "",
src_escaped,
dst_escaped,
@@ -2537,6 +2537,72 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
return 0;
}
static int exec_context_deserialize_bind_mount(ExecContext *c, const char *value, bool read_only) {
bool have_entry = false;
int r;
assert(c);
assert(value);
for (const char *p = value;;) {
_cleanup_free_ char *tuple = NULL, *source = NULL, *destination = NULL, *options = NULL,
*extra = NULL;
bool rbind = true, ignore_enoent = false;
char *s, *d;
int n_fields;
r = extract_first_word(&p, &tuple, /* separators= */ NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return r;
if (r == 0)
return have_entry ? 0 : -EINVAL;
have_entry = true;
const char *q = tuple;
r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS|EXTRACT_DONT_COALESCE_SEPARATORS,
&source, &destination, &options, &extra);
if (r < 0)
return r;
if (r == 0 || extra)
continue;
n_fields = r;
s = source;
if (s[0] == '-') {
ignore_enoent = true;
s++;
}
if (n_fields >= 2) {
if (isempty(destination))
continue;
d = destination;
} else
d = s;
if (n_fields >= 3) {
if (isempty(options) || streq(options, "rbind"))
rbind = true;
else if (streq(options, "norbind"))
rbind = false;
else
continue;
}
r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
&(BindMount) {
.source = s,
.destination = d,
.read_only = read_only,
.recursive = rbind,
.ignore_enoent = ignore_enoent,
});
if (r < 0)
return log_oom_debug();
}
}
static int exec_context_deserialize(ExecContext *c, FILE *f) {
int r;
@@ -3244,123 +3310,13 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
if (r < 0)
return r;
} else if ((val = startswith(l, "exec-context-bind-read-only-path="))) {
_cleanup_free_ char *source = NULL, *destination = NULL;
bool rbind = true, ignore_enoent = false;
char *s = NULL, *d = NULL;
r = extract_first_word(&val,
&source,
":" WHITESPACE,
EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNESCAPE_SEPARATORS);
r = exec_context_deserialize_bind_mount(c, val, /* read_only= */ true);
if (r < 0)
return r;
if (r == 0)
return -EINVAL;
s = source;
if (s[0] == '-') {
ignore_enoent = true;
s++;
}
if (val && val[-1] == ':') {
r = extract_first_word(&val,
&destination,
":" WHITESPACE,
EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNESCAPE_SEPARATORS);
if (r < 0)
return r;
if (r == 0)
continue;
d = destination;
if (val && val[-1] == ':') {
_cleanup_free_ char *options = NULL;
r = extract_first_word(&val, &options, NULL, EXTRACT_UNQUOTE);
if (r < 0)
return r;
if (isempty(options) || streq(options, "rbind"))
rbind = true;
else if (streq(options, "norbind"))
rbind = false;
else
continue;
}
} else
d = s;
r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
&(BindMount) {
.source = s,
.destination = d,
.read_only = true,
.recursive = rbind,
.ignore_enoent = ignore_enoent,
});
if (r < 0)
return log_oom_debug();
} else if ((val = startswith(l, "exec-context-bind-path="))) {
_cleanup_free_ char *source = NULL, *destination = NULL;
bool rbind = true, ignore_enoent = false;
char *s = NULL, *d = NULL;
r = extract_first_word(&val,
&source,
":" WHITESPACE,
EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNESCAPE_SEPARATORS);
r = exec_context_deserialize_bind_mount(c, val, /* read_only= */ false);
if (r < 0)
return r;
if (r == 0)
return -EINVAL;
s = source;
if (s[0] == '-') {
ignore_enoent = true;
s++;
}
if (val && val[-1] == ':') {
r = extract_first_word(&val,
&destination,
":" WHITESPACE,
EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_UNESCAPE_SEPARATORS);
if (r < 0)
return r;
if (r == 0)
continue;
d = destination;
if (val && val[-1] == ':') {
_cleanup_free_ char *options = NULL;
r = extract_first_word(&val, &options, NULL, EXTRACT_UNQUOTE);
if (r < 0)
return r;
if (isempty(options) || streq(options, "rbind"))
rbind = true;
else if (streq(options, "norbind"))
rbind = false;
else
continue;
}
} else
d = s;
r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
&(BindMount) {
.source = s,
.destination = d,
.read_only = false,
.recursive = rbind,
.ignore_enoent = ignore_enoent,
});
if (r < 0)
return log_oom_debug();
} else if ((val = startswith(l, "exec-context-temporary-filesystems="))) {
_cleanup_free_ char *path = NULL, *options = NULL;
+31 -38
View File
@@ -5208,12 +5208,12 @@ int config_parse_bind_paths(
}
for (const char *p = rvalue;;) {
_cleanup_free_ char *source = NULL, *destination = NULL;
_cleanup_free_ char *sresolved = NULL, *dresolved = NULL;
_cleanup_free_ char *tuple = NULL, *source = NULL, *destination = NULL, *options = NULL,
*extra = NULL, *sresolved = NULL, *dresolved = NULL;
char *s = NULL, *d = NULL;
bool rbind = true, ignore_enoent = false;
r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
r = extract_first_word(&p, &tuple, /* separators= */ NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
@@ -5223,6 +5223,21 @@ int config_parse_bind_paths(
if (r == 0)
break;
const char *q = tuple;
r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS|EXTRACT_DONT_COALESCE_SEPARATORS,
&source, &destination, &options, &extra);
if (r == -ENOMEM)
return log_oom();
if (r <= 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring: %s", lvalue, tuple);
continue;
}
if (extra) {
log_syntax(unit, LOG_WARNING, filename, line, 0, "Too many parameters in %s=, ignoring: %s", lvalue, tuple);
continue;
}
int n_fields = r;
r = unit_path_printf(u, source, &sresolved);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
@@ -5241,19 +5256,7 @@ int config_parse_bind_paths(
continue;
/* Optionally, the destination is specified. */
if (p && p[-1] == ':') {
r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s, ignoring: %s", lvalue, rvalue);
return 0;
}
if (r == 0) {
log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing argument after ':', ignoring: %s", s);
continue;
}
if (n_fields >= 2) {
r = unit_path_printf(u, destination, &dresolved);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
@@ -5266,31 +5269,21 @@ int config_parse_bind_paths(
continue;
d = dresolved;
/* Optionally, there's also a short option string specified */
if (p && p[-1] == ':') {
_cleanup_free_ char *options = NULL;
r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
return 0;
}
if (isempty(options) || streq(options, "rbind"))
rbind = true;
else if (streq(options, "norbind"))
rbind = false;
else {
log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid option string, ignoring setting: %s", options);
continue;
}
}
} else
d = s;
/* Optionally, there's also a short option string specified */
if (n_fields >= 3) {
if (isempty(options) || streq(options, "rbind"))
rbind = true;
else if (streq(options, "norbind"))
rbind = false;
else {
log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid option string, ignoring setting: %s", options);
continue;
}
}
r = bind_mount_add(&c->bind_mounts, &c->n_bind_mounts,
&(BindMount) {
.source = s,