]>
Commit | Line | Data |
---|---|---|
d6c9574f | 1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
15ae422b LP |
2 | |
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2010 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
5430f7f2 LP |
9 | under the terms of the GNU Lesser General Public License as published by |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
15ae422b LP |
11 | (at your option) any later version. |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
5430f7f2 | 16 | Lesser General Public License for more details. |
15ae422b | 17 | |
5430f7f2 | 18 | You should have received a copy of the GNU Lesser General Public License |
15ae422b LP |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. |
20 | ***/ | |
21 | ||
22 | #include <errno.h> | |
23 | #include <sys/mount.h> | |
24 | #include <string.h> | |
25 | #include <stdio.h> | |
26 | #include <unistd.h> | |
27 | #include <sys/stat.h> | |
28 | #include <sys/types.h> | |
29 | #include <sched.h> | |
30 | #include <sys/syscall.h> | |
31 | #include <limits.h> | |
25e870b5 | 32 | #include <linux/fs.h> |
15ae422b LP |
33 | |
34 | #include "strv.h" | |
35 | #include "util.h" | |
9eb977db | 36 | #include "path-util.h" |
15ae422b LP |
37 | #include "namespace.h" |
38 | #include "missing.h" | |
c17ec25e | 39 | #include "execute.h" |
15ae422b | 40 | |
c17ec25e | 41 | typedef enum MountMode { |
15ae422b LP |
42 | /* This is ordered by priority! */ |
43 | INACCESSIBLE, | |
44 | READONLY, | |
ac0930c8 LP |
45 | PRIVATE_TMP, |
46 | PRIVATE_VAR_TMP, | |
15ae422b | 47 | READWRITE |
c17ec25e | 48 | } MountMode; |
15ae422b | 49 | |
c17ec25e | 50 | typedef struct BindMount { |
15ae422b | 51 | const char *path; |
c17ec25e | 52 | MountMode mode; |
ac0930c8 | 53 | bool done; |
ea92ae33 | 54 | bool ignore; |
c17ec25e | 55 | } BindMount; |
15ae422b | 56 | |
c17ec25e | 57 | static int append_mounts(BindMount **p, char **strv, MountMode mode) { |
15ae422b LP |
58 | char **i; |
59 | ||
60 | STRV_FOREACH(i, strv) { | |
61 | ||
ea92ae33 MW |
62 | (*p)->ignore = false; |
63 | ||
64 | if ((mode == INACCESSIBLE || mode == READONLY) && (*i)[0] == '-') { | |
65 | (*p)->ignore = true; | |
66 | (*i)++; | |
67 | } | |
68 | ||
15ae422b LP |
69 | if (!path_is_absolute(*i)) |
70 | return -EINVAL; | |
71 | ||
72 | (*p)->path = *i; | |
73 | (*p)->mode = mode; | |
74 | (*p)++; | |
75 | } | |
76 | ||
77 | return 0; | |
78 | } | |
79 | ||
c17ec25e MS |
80 | static int mount_path_compare(const void *a, const void *b) { |
81 | const BindMount *p = a, *q = b; | |
15ae422b LP |
82 | |
83 | if (path_equal(p->path, q->path)) { | |
84 | ||
85 | /* If the paths are equal, check the mode */ | |
86 | if (p->mode < q->mode) | |
87 | return -1; | |
88 | ||
89 | if (p->mode > q->mode) | |
90 | return 1; | |
91 | ||
92 | return 0; | |
93 | } | |
94 | ||
95 | /* If the paths are not equal, then order prefixes first */ | |
96 | if (path_startswith(p->path, q->path)) | |
97 | return 1; | |
98 | ||
99 | if (path_startswith(q->path, p->path)) | |
100 | return -1; | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
c17ec25e MS |
105 | static void drop_duplicates(BindMount *m, unsigned *n) { |
106 | BindMount *f, *t, *previous; | |
15ae422b | 107 | |
c17ec25e | 108 | assert(m); |
15ae422b | 109 | assert(n); |
15ae422b | 110 | |
c17ec25e | 111 | for (f = m, t = m, previous = NULL; f < m+*n; f++) { |
15ae422b | 112 | |
ac0930c8 | 113 | /* The first one wins */ |
15ae422b LP |
114 | if (previous && path_equal(f->path, previous->path)) |
115 | continue; | |
116 | ||
117 | t->path = f->path; | |
118 | t->mode = f->mode; | |
119 | ||
15ae422b LP |
120 | previous = t; |
121 | ||
122 | t++; | |
123 | } | |
124 | ||
c17ec25e | 125 | *n = t - m; |
15ae422b LP |
126 | } |
127 | ||
ac0930c8 | 128 | static int apply_mount( |
c17ec25e | 129 | BindMount *m, |
ac0930c8 | 130 | const char *tmp_dir, |
c17ec25e | 131 | const char *var_tmp_dir) { |
ac0930c8 | 132 | |
15ae422b | 133 | const char *what; |
15ae422b | 134 | int r; |
15ae422b | 135 | |
c17ec25e | 136 | assert(m); |
15ae422b | 137 | |
c17ec25e | 138 | switch (m->mode) { |
15ae422b LP |
139 | |
140 | case INACCESSIBLE: | |
c17ec25e | 141 | what = "/run/systemd/inaccessible"; |
15ae422b LP |
142 | break; |
143 | ||
144 | case READONLY: | |
15ae422b | 145 | case READWRITE: |
c17ec25e | 146 | what = m->path; |
15ae422b LP |
147 | break; |
148 | ||
ac0930c8 LP |
149 | case PRIVATE_TMP: |
150 | what = tmp_dir; | |
151 | break; | |
152 | ||
153 | case PRIVATE_VAR_TMP: | |
154 | what = var_tmp_dir; | |
15ae422b | 155 | break; |
e364ad06 LP |
156 | |
157 | default: | |
158 | assert_not_reached("Unknown mode"); | |
15ae422b LP |
159 | } |
160 | ||
ac0930c8 | 161 | assert(what); |
15ae422b | 162 | |
c17ec25e | 163 | r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL); |
ac0930c8 | 164 | if (r >= 0) |
c17ec25e | 165 | log_debug("Successfully mounted %s to %s", what, m->path); |
ea92ae33 MW |
166 | else if (m->ignore && errno == ENOENT) |
167 | r = 0; | |
15ae422b | 168 | |
ac0930c8 LP |
169 | return r; |
170 | } | |
15ae422b | 171 | |
c17ec25e | 172 | static int make_read_only(BindMount *m) { |
ac0930c8 | 173 | int r; |
15ae422b | 174 | |
c17ec25e | 175 | assert(m); |
ac0930c8 | 176 | |
c17ec25e | 177 | if (m->mode != INACCESSIBLE && m->mode != READONLY) |
ac0930c8 LP |
178 | return 0; |
179 | ||
c17ec25e | 180 | r = mount(NULL, m->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL); |
ea92ae33 | 181 | if (r < 0 && !(m->ignore && errno == ENOENT)) |
ac0930c8 LP |
182 | return -errno; |
183 | ||
184 | return 0; | |
15ae422b LP |
185 | } |
186 | ||
c17ec25e MS |
187 | int setup_tmpdirs(char **tmp_dir, |
188 | char **var_tmp_dir) { | |
189 | int r = 0; | |
190 | char tmp_dir_template[] = "/tmp/systemd-private-XXXXXX", | |
191 | var_tmp_dir_template[] = "/var/tmp/systemd-private-XXXXXX"; | |
15ae422b | 192 | |
c17ec25e MS |
193 | assert(tmp_dir); |
194 | assert(var_tmp_dir); | |
15ae422b | 195 | |
d34cd374 | 196 | r = create_tmp_dir(tmp_dir_template, tmp_dir); |
c17ec25e | 197 | if (r < 0) |
d34cd374 | 198 | return r; |
15ae422b | 199 | |
d34cd374 ZJS |
200 | r = create_tmp_dir(var_tmp_dir_template, var_tmp_dir); |
201 | if (r == 0) | |
202 | return 0; | |
15ae422b | 203 | |
d34cd374 | 204 | /* failure */ |
c17ec25e | 205 | rmdir(*tmp_dir); |
d34cd374 | 206 | rmdir(tmp_dir_template); |
c17ec25e MS |
207 | free(*tmp_dir); |
208 | *tmp_dir = NULL; | |
15ae422b | 209 | |
c17ec25e MS |
210 | return r; |
211 | } | |
15ae422b | 212 | |
c17ec25e MS |
213 | int setup_namespace(char** read_write_dirs, |
214 | char** read_only_dirs, | |
215 | char** inaccessible_dirs, | |
216 | char* tmp_dir, | |
217 | char* var_tmp_dir, | |
218 | bool private_tmp, | |
219 | unsigned mount_flags) { | |
15ae422b | 220 | |
c17ec25e MS |
221 | unsigned n = strv_length(read_write_dirs) + |
222 | strv_length(read_only_dirs) + | |
223 | strv_length(inaccessible_dirs) + | |
224 | (private_tmp ? 2 : 0); | |
225 | BindMount *m, *mounts; | |
226 | int r = 0; | |
15ae422b | 227 | |
c17ec25e MS |
228 | if (!mount_flags) |
229 | mount_flags = MS_SHARED; | |
ac0930c8 | 230 | |
d5a3f0ea ZJS |
231 | if (unshare(CLONE_NEWNS) < 0) |
232 | return -errno; | |
15ae422b | 233 | |
c17ec25e MS |
234 | m = mounts = (BindMount *) alloca(n * sizeof(BindMount)); |
235 | if ((r = append_mounts(&m, read_write_dirs, READWRITE)) < 0 || | |
236 | (r = append_mounts(&m, read_only_dirs, READONLY)) < 0 || | |
237 | (r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE)) < 0) | |
d5a3f0ea | 238 | return r; |
ac0930c8 | 239 | |
c17ec25e MS |
240 | if (private_tmp) { |
241 | m->path = "/tmp"; | |
242 | m->mode = PRIVATE_TMP; | |
243 | m++; | |
ac0930c8 | 244 | |
c17ec25e MS |
245 | m->path = "/var/tmp"; |
246 | m->mode = PRIVATE_VAR_TMP; | |
247 | m++; | |
15ae422b LP |
248 | } |
249 | ||
c17ec25e MS |
250 | assert(mounts + n == m); |
251 | ||
252 | qsort(mounts, n, sizeof(BindMount), mount_path_compare); | |
253 | drop_duplicates(mounts, &n); | |
15ae422b | 254 | |
ac0930c8 | 255 | /* Remount / as SLAVE so that nothing now mounted in the namespace |
dc4b0200 | 256 | shows up in the parent */ |
d5a3f0ea ZJS |
257 | if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) |
258 | return -errno; | |
15ae422b | 259 | |
c17ec25e MS |
260 | for (m = mounts; m < mounts + n; ++m) { |
261 | r = apply_mount(m, tmp_dir, var_tmp_dir); | |
c1d70f7c | 262 | if (r < 0) |
15ae422b | 263 | goto undo_mounts; |
c1d70f7c | 264 | } |
15ae422b | 265 | |
c17ec25e MS |
266 | for (m = mounts; m < mounts + n; ++m) { |
267 | r = make_read_only(m); | |
ac0930c8 LP |
268 | if (r < 0) |
269 | goto undo_mounts; | |
15ae422b LP |
270 | } |
271 | ||
ac0930c8 | 272 | /* Remount / as the desired mode */ |
c17ec25e | 273 | if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) { |
15ae422b LP |
274 | r = -errno; |
275 | goto undo_mounts; | |
276 | } | |
277 | ||
15ae422b LP |
278 | return 0; |
279 | ||
280 | undo_mounts: | |
c17ec25e MS |
281 | for (m = mounts; m < mounts + n; ++m) { |
282 | if (m->done) | |
283 | umount2(m->path, MNT_DETACH); | |
284 | } | |
15ae422b | 285 | |
15ae422b LP |
286 | return r; |
287 | } |