core: quote each exec directory entry when serializing

Quote each serialized exec directory entry, and use extract flags
compatible with config_parse_exec_directories() when deserializing.
This allows paths containing spaces and escaped characters to round-trip
correctly.

Fixes #41853.
Replaces #42686.
This commit is contained in:
Yu Watanabe
2026-07-31 10:46:50 +09:00
parent 6d7a2ec6ba
commit 71a2af43f9
3 changed files with 31 additions and 7 deletions
+9 -7
View File
@@ -1867,11 +1867,11 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
FOREACH_ARRAY(i, c->directories[dt].items, c->directories[dt].n_items) {
_cleanup_free_ char *path_escaped = NULL;
path_escaped = shell_escape(i->path, ":" WHITESPACE);
path_escaped = shell_escape(i->path, ":\"");
if (!path_escaped)
return log_oom_debug();
if (!strextend(&value, " ", path_escaped))
if (!strextend(&value, " \"", path_escaped))
return log_oom_debug();
if (!strextend(&value, ":", yes_no(FLAGS_SET(i->flags, EXEC_DIRECTORY_ONLY_CREATE))))
@@ -1883,13 +1883,16 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
STRV_FOREACH(d, i->symlinks) {
_cleanup_free_ char *link_escaped = NULL;
link_escaped = shell_escape(*d, ":" WHITESPACE);
link_escaped = shell_escape(*d, ":\"");
if (!link_escaped)
return log_oom_debug();
if (!strextend(&value, ":", link_escaped))
return log_oom_debug();
}
if (!strextend(&value, "\""))
return log_oom_debug();
}
r = serialize_item(f, key, value);
@@ -2830,15 +2833,14 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
ExecDirectoryFlags exec_directory_flags = 0;
const char *p;
/* Use EXTRACT_UNESCAPE_RELAX here, as we unescape the colons in subsequent calls */
r = extract_first_word(&val, &tuple, WHITESPACE, EXTRACT_UNESCAPE_SEPARATORS|EXTRACT_UNESCAPE_RELAX);
r = extract_first_word(&val, &tuple, WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return r;
if (r == 0)
break;
p = tuple;
r = extract_many_words(&p, ":", EXTRACT_UNESCAPE_SEPARATORS, &path, &only_create, &read_only);
r = extract_many_words(&p, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &path, &only_create, &read_only);
if (r < 0)
return r;
if (r < 2)
@@ -2866,7 +2868,7 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
for (;;) {
_cleanup_free_ char *link = NULL;
r = extract_first_word(&p, &link, ":", EXTRACT_UNESCAPE_SEPARATORS);
r = extract_first_word(&p, &link, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS);
if (r < 0)
return r;
if (r == 0)
+4
View File
@@ -1140,6 +1140,10 @@ static void test_exec_runtimedirectory(Manager *m) {
test(m, "exec-runtimedirectory.service", 0, CLD_EXITED);
(void) rm_rf("/run/test-exec_runtimedirectory2", REMOVE_ROOT|REMOVE_PHYSICAL);
(void) rm_rf("/run/test-exec_runtimedirectory-escape", REMOVE_ROOT|REMOVE_PHYSICAL);
test(m, "exec-runtimedirectory@foo\\x2dv1.service", 0, CLD_EXITED);
(void) rm_rf("/run/test-exec_runtimedirectory-escape", REMOVE_ROOT|REMOVE_PHYSICAL);
test(m, "exec-runtimedirectory-mode.service", 0, CLD_EXITED);
test(m, "exec-runtimedirectory-owner.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
@@ -0,0 +1,18 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
[Unit]
Description=Test for RuntimeDirectory (escape)
[Service]
ExecStart=bash -x -c 'test -d "%t/test-exec_runtimedirectory-escape/backsl\ash"'
ExecStart=bash -x -c 'test -d "%t/test-exec_runtimedirectory-escape/foo bar:baz\\quux"'
ExecStart=bash -x -c 'test -d "%t/test-exec_runtimedirectory-escape/%I"'
ExecStart=bash -x -c 'test -d "%t/test-exec_runtimedirectory-escape/%i"'
ExecStart=bash -x -c 'test -d "%t/test-exec_runtimedirectory-escape/hoge ho:ge"'
ExecStart=bash -x -c 'test -L "%t/test-exec_runtimedirectory-escape/foo:bar"'
ExecStart=bash -x -c 'test "$(readlink %t/test-exec_runtimedirectory-escape/foo:bar)" = "hoge ho:ge"'
Type=oneshot
RuntimeDirectory=test-exec_runtimedirectory-escape/backsl\ash
RuntimeDirectory="test-exec_runtimedirectory-escape/foo bar\:baz\\quux"
RuntimeDirectory=test-exec_runtimedirectory-escape/%I
RuntimeDirectory=test-exec_runtimedirectory-escape/%i
RuntimeDirectory="test-exec_runtimedirectory-escape/hoge ho\:ge:test-exec_runtimedirectory-escape/foo\:bar"