1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
From d7942fe5fc197d1eb77986b5c73b5c36d82e141e Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Fri, 5 Jan 2024 20:39:40 +0100
Subject: [PATCH] core: escape spaces in paths during serialization
Otherwise we split them incorrectly when deserializing them.
Resolves: #30747
Conflict:NA
Reference:https://github.com/systemd/systemd/commit/d7942fe5fc197d1eb77986b5c73b5c36d82e141e
---
src/core/execute-serialize.c | 17 ++++++------
test/units/testsuite-07.exec-context.sh | 36 ++++++++++++++++---------
2 files changed, 32 insertions(+), 21 deletions(-)
diff --git a/src/core/execute-serialize.c b/src/core/execute-serialize.c
index 55d24094f7..dd48ad3f65 100644
--- a/src/core/execute-serialize.c
+++ b/src/core/execute-serialize.c
@@ -1930,7 +1930,7 @@ 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, ":");
+ path_escaped = shell_escape(i->path, ":" WHITESPACE);
if (!path_escaped)
return log_oom_debug();
@@ -1943,7 +1943,7 @@ 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, ":");
+ link_escaped = shell_escape(*d, ":" WHITESPACE);
if (!link_escaped)
return log_oom_debug();
@@ -2264,11 +2264,11 @@ 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, ":");
+ src_escaped = shell_escape(mount->source, ":" WHITESPACE);
if (!src_escaped)
return log_oom_debug();
- dst_escaped = shell_escape(mount->destination, ":");
+ dst_escaped = shell_escape(mount->destination, ":" WHITESPACE);
if (!dst_escaped)
return log_oom_debug();
@@ -2455,11 +2455,11 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
FOREACH_ARRAY(mount, c->mount_images, c->n_mount_images) {
_cleanup_free_ char *s = NULL, *source_escaped = NULL, *dest_escaped = NULL;
- source_escaped = shell_escape(mount->source, " ");
+ source_escaped = shell_escape(mount->source, WHITESPACE);
if (!source_escaped)
return log_oom_debug();
- dest_escaped = shell_escape(mount->destination, " ");
+ dest_escaped = shell_escape(mount->destination, WHITESPACE);
if (!dest_escaped)
return log_oom_debug();
@@ -2496,7 +2496,7 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) {
FOREACH_ARRAY(mount, c->extension_images, c->n_extension_images) {
_cleanup_free_ char *s = NULL, *source_escaped = NULL;
- source_escaped = shell_escape(mount->source, ":");
+ source_escaped = shell_escape(mount->source, ":" WHITESPACE);
if (!source_escaped)
return log_oom_debug();
@@ -2847,7 +2847,8 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) {
_cleanup_free_ char *tuple = NULL, *path = NULL, *only_create = NULL;
const char *p;
- r = extract_first_word(&val, &tuple, WHITESPACE, EXTRACT_RETAIN_ESCAPE);
+ /* 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);
if (r < 0)
return r;
if (r == 0)
diff --git a/test/units/testsuite-07.exec-context.sh b/test/units/testsuite-07.exec-context.sh
index c84974f1de..dd63163008 100755
--- a/test/units/testsuite-07.exec-context.sh
+++ b/test/units/testsuite-07.exec-context.sh
@@ -93,6 +93,13 @@ systemd-run --wait --pipe -p BindPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/u
bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; ! mountpoint /usr"
systemd-run --wait --pipe -p BindReadOnlyPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \
bash -xec "test ! -w /etc; test ! -w /mnt; ! mountpoint /usr"
+# Make sure we properly serialize/deserialize paths with spaces
+# See: https://github.com/systemd/systemd/issues/30747
+touch "/tmp/test file with spaces"
+systemd-run --wait --pipe -p TemporaryFileSystem="/tmp" -p BindPaths="/etc /home:/mnt:norbind /tmp/test\ file\ with\ spaces" \
+ bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; stat '/tmp/test file with spaces'"
+systemd-run --wait --pipe -p TemporaryFileSystem="/tmp" -p BindPaths="/etc /home:/mnt:norbind /tmp/test\ file\ with\ spaces:/tmp/destination\ wi\:th\ spaces" \
+ bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; stat '/tmp/destination wi:th spaces'"
# Check if we correctly serialize, deserialize, and set directives that
# have more complex internal handling
@@ -206,18 +213,20 @@ fi
# {Cache,Configuration,Logs,Runtime,State}Directory=
ARGUMENTS=(
- -p CacheDirectory="foo/bar/baz"
+ -p CacheDirectory="foo/bar/baz also\ with\ spaces"
-p CacheDirectory="foo"
-p CacheDirectory="context"
-p CacheDirectoryMode="0123"
-p CacheDirectoryMode="0666"
- -p ConfigurationDirectory="context/foo also_context/bar context/nested/baz"
+ -p ConfigurationDirectory="context/foo also_context/bar context/nested/baz context/semi\:colon"
-p ConfigurationDirectoryMode="0400"
-p LogsDirectory="context/foo"
-p LogsDirectory=""
-p LogsDirectory="context/a/very/nested/logs/dir"
- -p RuntimeDirectory="context"
- -p RuntimeDirectory="also_context"
+ -p RuntimeDirectory="context/with\ spaces"
+ # Note: {Runtime,State,Cache,Logs}Directory= directives support the directory:symlink syntax, which
+ # requires an additional level of escaping for the colon character
+ -p RuntimeDirectory="also_context:a\ symlink\ with\ \\\:\ col\\\:ons\ and\ \ spaces"
-p RuntimeDirectoryPreserve=yes
-p StateDirectory="context"
-p StateDirectory="./././././././context context context"
@@ -226,21 +235,22 @@ ARGUMENTS=(
rm -rf /run/context
systemd-run --wait --pipe "${ARGUMENTS[@]}" \
- bash -xec '[[ $CACHE_DIRECTORY == /var/cache/context:/var/cache/foo:/var/cache/foo/bar/baz ]];
- [[ $(stat -c "%a" ${CACHE_DIRECTORY##*:}) == 666 ]]'
+ bash -xec '[[ $CACHE_DIRECTORY == "/var/cache/also with spaces:/var/cache/context:/var/cache/foo:/var/cache/foo/bar/baz" ]];
+ [[ $(stat -c "%a" "${CACHE_DIRECTORY##*:}") == 666 ]]'
systemd-run --wait --pipe "${ARGUMENTS[@]}" \
- bash -xec '[[ $CONFIGURATION_DIRECTORY == /etc/also_context/bar:/etc/context/foo:/etc/context/nested/baz ]];
- [[ $(stat -c "%a" ${CONFIGURATION_DIRECTORY##*:}) == 400 ]]'
+ bash -xec '[[ $CONFIGURATION_DIRECTORY == /etc/also_context/bar:/etc/context/foo:/etc/context/nested/baz:/etc/context/semi:colon ]];
+ [[ $(stat -c "%a" "${CONFIGURATION_DIRECTORY%%:*}") == 400 ]]'
systemd-run --wait --pipe "${ARGUMENTS[@]}" \
bash -xec '[[ $LOGS_DIRECTORY == /var/log/context/a/very/nested/logs/dir:/var/log/context/foo ]];
- [[ $(stat -c "%a" ${LOGS_DIRECTORY##*:}) == 755 ]]'
+ [[ $(stat -c "%a" "${LOGS_DIRECTORY##*:}") == 755 ]]'
systemd-run --wait --pipe "${ARGUMENTS[@]}" \
- bash -xec '[[ $RUNTIME_DIRECTORY == /run/also_context:/run/context ]];
- [[ $(stat -c "%a" ${RUNTIME_DIRECTORY##*:}) == 755 ]];
- [[ $(stat -c "%a" ${RUNTIME_DIRECTORY%%:*}) == 755 ]]'
+ bash -xec '[[ $RUNTIME_DIRECTORY == "/run/also_context:/run/context/with spaces" ]];
+ [[ $(stat -c "%a" "${RUNTIME_DIRECTORY##*:}") == 755 ]];
+ [[ $(stat -c "%a" "${RUNTIME_DIRECTORY%%:*}") == 755 ]]'
systemd-run --wait --pipe "${ARGUMENTS[@]}" \
bash -xec '[[ $STATE_DIRECTORY == /var/lib/context ]]; [[ $(stat -c "%a" $STATE_DIRECTORY) == 0 ]]'
-test -d /run/context
+test -d "/run/context/with spaces"
+test -s "/run/a symlink with : col:ons and spaces"
rm -rf /var/{cache,lib,log}/context /etc/{also_,}context
# Limit*=
--
2.43.0
|