]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/namespace.c
units: don't enforce a holdoff time for journald, logind, udevd
[thirdparty/systemd.git] / src / core / namespace.c
CommitLineData
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"
39
40typedef enum PathMode {
41 /* This is ordered by priority! */
42 INACCESSIBLE,
43 READONLY,
44 PRIVATE,
45 READWRITE
46} PathMode;
47
48typedef struct Path {
49 const char *path;
50 PathMode mode;
51} Path;
52
53static int append_paths(Path **p, char **strv, PathMode mode) {
54 char **i;
55
56 STRV_FOREACH(i, strv) {
57
58 if (!path_is_absolute(*i))
59 return -EINVAL;
60
61 (*p)->path = *i;
62 (*p)->mode = mode;
63 (*p)++;
64 }
65
66 return 0;
67}
68
69static int path_compare(const void *a, const void *b) {
70 const Path *p = a, *q = b;
71
72 if (path_equal(p->path, q->path)) {
73
74 /* If the paths are equal, check the mode */
75 if (p->mode < q->mode)
76 return -1;
77
78 if (p->mode > q->mode)
79 return 1;
80
81 return 0;
82 }
83
84 /* If the paths are not equal, then order prefixes first */
85 if (path_startswith(p->path, q->path))
86 return 1;
87
88 if (path_startswith(q->path, p->path))
89 return -1;
90
91 return 0;
92}
93
94static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible, bool *need_private) {
95 Path *f, *t, *previous;
96
97 assert(p);
98 assert(n);
99 assert(need_inaccessible);
100 assert(need_private);
101
102 for (f = p, t = p, previous = NULL; f < p+*n; f++) {
103
104 if (previous && path_equal(f->path, previous->path))
105 continue;
106
107 t->path = f->path;
108 t->mode = f->mode;
109
110 if (t->mode == PRIVATE)
111 *need_private = true;
112
113 if (t->mode == INACCESSIBLE)
114 *need_inaccessible = true;
115
116 previous = t;
117
118 t++;
119 }
120
121 *n = t - p;
122}
123
124static int apply_mount(Path *p, const char *root_dir, const char *inaccessible_dir, const char *private_dir, unsigned long flags) {
125 const char *what;
126 char *where;
127 int r;
15ae422b
LP
128
129 assert(p);
130 assert(root_dir);
131 assert(inaccessible_dir);
132 assert(private_dir);
133
c1d70f7c
LP
134 where = strappend(root_dir, p->path);
135 if (!where)
15ae422b
LP
136 return -ENOMEM;
137
138 switch (p->mode) {
139
140 case INACCESSIBLE:
141 what = inaccessible_dir;
5dcfe57b 142 flags |= MS_RDONLY;
15ae422b
LP
143 break;
144
145 case READONLY:
5dcfe57b 146 flags |= MS_RDONLY;
15ae422b
LP
147 /* Fall through */
148
149 case READWRITE:
150 what = p->path;
151 break;
152
153 case PRIVATE:
154 what = private_dir;
155 break;
e364ad06
LP
156
157 default:
158 assert_not_reached("Unknown mode");
15ae422b
LP
159 }
160
c1d70f7c
LP
161 r = mount(what, where, NULL, MS_BIND|MS_REC, NULL);
162 if (r >= 0) {
15ae422b
LP
163 log_debug("Successfully mounted %s to %s", what, where);
164
165 /* The bind mount will always inherit the original
166 * flags. If we want to set any flag we need
35b8ca3a 167 * to do so in a second independent step. */
15ae422b 168 if (flags)
5dcfe57b 169 r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|MS_REC|flags, NULL);
15ae422b 170
35b8ca3a 171 /* Avoid exponential growth of trees */
15ae422b 172 if (r >= 0 && path_equal(p->path, "/"))
4d46fec5 173 r = mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|flags, NULL);
15ae422b
LP
174
175 if (r < 0) {
176 r = -errno;
177 umount2(where, MNT_DETACH);
178 }
179 }
180
181 free(where);
182 return r;
183}
184
185int setup_namespace(
186 char **writable,
187 char **readable,
188 char **inaccessible,
189 bool private_tmp,
190 unsigned long flags) {
191
192 char
193 tmp_dir[] = "/tmp/systemd-namespace-XXXXXX",
194 root_dir[] = "/tmp/systemd-namespace-XXXXXX/root",
195 old_root_dir[] = "/tmp/systemd-namespace-XXXXXX/root/tmp/old-root-XXXXXX",
196 inaccessible_dir[] = "/tmp/systemd-namespace-XXXXXX/inaccessible",
197 private_dir[] = "/tmp/systemd-namespace-XXXXXX/private";
198
199 Path *paths, *p;
200 unsigned n;
201 bool need_private = false, need_inaccessible = false;
202 bool remove_tmp = false, remove_root = false, remove_old_root = false, remove_inaccessible = false, remove_private = false;
203 int r;
204 const char *t;
205
206 n =
207 strv_length(writable) +
208 strv_length(readable) +
209 strv_length(inaccessible) +
c1d70f7c 210 (private_tmp ? 3 : 1);
15ae422b 211
c1d70f7c
LP
212 paths = new(Path, n);
213 if (!paths)
15ae422b
LP
214 return -ENOMEM;
215
216 p = paths;
217 if ((r = append_paths(&p, writable, READWRITE)) < 0 ||
218 (r = append_paths(&p, readable, READONLY)) < 0 ||
219 (r = append_paths(&p, inaccessible, INACCESSIBLE)) < 0)
220 goto fail;
221
222 if (private_tmp) {
223 p->path = "/tmp";
224 p->mode = PRIVATE;
225 p++;
c1d70f7c
LP
226
227 p->path = "/var/tmp";
228 p->mode = PRIVATE;
229 p++;
15ae422b
LP
230 }
231
232 p->path = "/";
233 p->mode = READWRITE;
234 p++;
235
236 assert(paths + n == p);
237
238 qsort(paths, n, sizeof(Path), path_compare);
239 drop_duplicates(paths, &n, &need_inaccessible, &need_private);
240
241 if (!mkdtemp(tmp_dir)) {
242 r = -errno;
243 goto fail;
244 }
245 remove_tmp = true;
246
247 memcpy(root_dir, tmp_dir, sizeof(tmp_dir)-1);
248 if (mkdir(root_dir, 0777) < 0) {
249 r = -errno;
250 goto fail;
251 }
252 remove_root = true;
253
254 if (need_inaccessible) {
255 memcpy(inaccessible_dir, tmp_dir, sizeof(tmp_dir)-1);
256 if (mkdir(inaccessible_dir, 0) < 0) {
257 r = -errno;
258 goto fail;
259 }
260 remove_inaccessible = true;
261 }
262
263 if (need_private) {
21d279cf
LP
264 mode_t u;
265
15ae422b 266 memcpy(private_dir, tmp_dir, sizeof(tmp_dir)-1);
21d279cf
LP
267
268 u = umask(0000);
15ae422b 269 if (mkdir(private_dir, 0777 + S_ISVTX) < 0) {
21d279cf
LP
270 umask(u);
271
15ae422b
LP
272 r = -errno;
273 goto fail;
274 }
21d279cf
LP
275
276 umask(u);
15ae422b
LP
277 remove_private = true;
278 }
279
280 if (unshare(CLONE_NEWNS) < 0) {
281 r = -errno;
282 goto fail;
283 }
284
dc4b0200
DW
285 /* Remount / as SLAVE so that nothing mounted in the namespace
286 shows up in the parent */
287 if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) {
288 r = -errno;
289 goto fail;
290 }
15ae422b 291
c1d70f7c
LP
292 for (p = paths; p < paths + n; p++) {
293 r = apply_mount(p, root_dir, inaccessible_dir, private_dir, flags);
294 if (r < 0)
15ae422b 295 goto undo_mounts;
c1d70f7c 296 }
15ae422b
LP
297
298 memcpy(old_root_dir, tmp_dir, sizeof(tmp_dir)-1);
299 if (!mkdtemp(old_root_dir)) {
300 r = -errno;
301 goto undo_mounts;
302 }
303 remove_old_root = true;
304
305 if (chdir(root_dir) < 0) {
306 r = -errno;
307 goto undo_mounts;
308 }
309
310 if (pivot_root(root_dir, old_root_dir) < 0) {
311 r = -errno;
312 goto undo_mounts;
313 }
314
315 t = old_root_dir + sizeof(root_dir) - 1;
316 if (umount2(t, MNT_DETACH) < 0)
317 /* At this point it's too late to turn anything back,
318 * since we are already in the new root. */
319 return -errno;
320
321 if (rmdir(t) < 0)
322 return -errno;
323
324 return 0;
325
326undo_mounts:
327
328 for (p--; p >= paths; p--) {
329 char full_path[PATH_MAX];
330
331 snprintf(full_path, sizeof(full_path), "%s%s", root_dir, p->path);
332 char_array_0(full_path);
333
334 umount2(full_path, MNT_DETACH);
335 }
336
337fail:
338 if (remove_old_root)
339 rmdir(old_root_dir);
340
341 if (remove_inaccessible)
342 rmdir(inaccessible_dir);
343
344 if (remove_private)
345 rmdir(private_dir);
346
347 if (remove_root)
348 rmdir(root_dir);
349
350 if (remove_tmp)
351 rmdir(tmp_dir);
352
c1d70f7c 353 free(paths);
15ae422b
LP
354
355 return r;
356}