]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/namespace.c
util-lib: move more locale-related calls to locale-util.[ch]
[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>
07630cea 23#include <sched.h>
15ae422b 24#include <stdio.h>
07630cea
LP
25#include <string.h>
26#include <sys/mount.h>
15ae422b 27#include <sys/stat.h>
07630cea 28#include <unistd.h>
25e870b5 29#include <linux/fs.h>
15ae422b 30
7f112f50 31#include "dev-setup.h"
3ffd4af2 32#include "fd-util.h"
07630cea
LP
33#include "loopback-setup.h"
34#include "missing.h"
35#include "mkdir.h"
4349cd7c 36#include "mount-util.h"
3ffd4af2 37#include "namespace.h"
07630cea 38#include "path-util.h"
d7b8eec7 39#include "selinux-util.h"
2583fbea 40#include "socket-util.h"
8b43440b 41#include "string-table.h"
07630cea
LP
42#include "string-util.h"
43#include "strv.h"
44#include "util.h"
15ae422b 45
c17ec25e 46typedef enum MountMode {
15ae422b
LP
47 /* This is ordered by priority! */
48 INACCESSIBLE,
49 READONLY,
ac0930c8
LP
50 PRIVATE_TMP,
51 PRIVATE_VAR_TMP,
7f112f50 52 PRIVATE_DEV,
a610cc4f 53 PRIVATE_BUS_ENDPOINT,
15ae422b 54 READWRITE
c17ec25e 55} MountMode;
15ae422b 56
c17ec25e 57typedef struct BindMount {
15ae422b 58 const char *path;
c17ec25e 59 MountMode mode;
ac0930c8 60 bool done;
ea92ae33 61 bool ignore;
c17ec25e 62} BindMount;
15ae422b 63
c17ec25e 64static int append_mounts(BindMount **p, char **strv, MountMode mode) {
15ae422b
LP
65 char **i;
66
613b411c
LP
67 assert(p);
68
15ae422b
LP
69 STRV_FOREACH(i, strv) {
70
ea92ae33 71 (*p)->ignore = false;
002b2268 72 (*p)->done = false;
ea92ae33 73
94828d2d 74 if ((mode == INACCESSIBLE || mode == READONLY || mode == READWRITE) && (*i)[0] == '-') {
ea92ae33
MW
75 (*p)->ignore = true;
76 (*i)++;
77 }
78
15ae422b
LP
79 if (!path_is_absolute(*i))
80 return -EINVAL;
81
82 (*p)->path = *i;
83 (*p)->mode = mode;
84 (*p)++;
85 }
86
87 return 0;
88}
89
c17ec25e
MS
90static int mount_path_compare(const void *a, const void *b) {
91 const BindMount *p = a, *q = b;
a0827e2b 92 int d;
15ae422b 93
a0827e2b 94 d = path_compare(p->path, q->path);
15ae422b 95
5a8af538 96 if (d == 0) {
15ae422b
LP
97 /* If the paths are equal, check the mode */
98 if (p->mode < q->mode)
99 return -1;
100
101 if (p->mode > q->mode)
102 return 1;
103
104 return 0;
105 }
106
107 /* If the paths are not equal, then order prefixes first */
a0827e2b 108 return d;
15ae422b
LP
109}
110
c17ec25e
MS
111static void drop_duplicates(BindMount *m, unsigned *n) {
112 BindMount *f, *t, *previous;
15ae422b 113
c17ec25e 114 assert(m);
15ae422b 115 assert(n);
15ae422b 116
c17ec25e 117 for (f = m, t = m, previous = NULL; f < m+*n; f++) {
15ae422b 118
ac0930c8 119 /* The first one wins */
15ae422b
LP
120 if (previous && path_equal(f->path, previous->path))
121 continue;
122
e2d7c1a0 123 *t = *f;
15ae422b 124
15ae422b
LP
125 previous = t;
126
127 t++;
128 }
129
c17ec25e 130 *n = t - m;
15ae422b
LP
131}
132
7f112f50
LP
133static int mount_dev(BindMount *m) {
134 static const char devnodes[] =
135 "/dev/null\0"
136 "/dev/zero\0"
137 "/dev/full\0"
138 "/dev/random\0"
139 "/dev/urandom\0"
140 "/dev/tty\0";
141
2b85f4e1 142 char temporary_mount[] = "/tmp/namespace-dev-XXXXXX";
63cc4c31 143 const char *d, *dev = NULL, *devpts = NULL, *devshm = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL;
7f112f50
LP
144 _cleanup_umask_ mode_t u;
145 int r;
146
147 assert(m);
148
149 u = umask(0000);
150
2b85f4e1
LP
151 if (!mkdtemp(temporary_mount))
152 return -errno;
153
63c372cb 154 dev = strjoina(temporary_mount, "/dev");
dc751688 155 (void) mkdir(dev, 0755);
2b85f4e1
LP
156 if (mount("tmpfs", dev, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=755") < 0) {
157 r = -errno;
158 goto fail;
159 }
160
63c372cb 161 devpts = strjoina(temporary_mount, "/dev/pts");
dc751688 162 (void) mkdir(devpts, 0755);
2b85f4e1
LP
163 if (mount("/dev/pts", devpts, NULL, MS_BIND, NULL) < 0) {
164 r = -errno;
165 goto fail;
166 }
167
63c372cb 168 devptmx = strjoina(temporary_mount, "/dev/ptmx");
3164e3cb
ZJS
169 if (symlink("pts/ptmx", devptmx) < 0) {
170 r = -errno;
171 goto fail;
172 }
e06b6479 173
63c372cb 174 devshm = strjoina(temporary_mount, "/dev/shm");
dc751688 175 (void) mkdir(devshm, 01777);
2b85f4e1
LP
176 r = mount("/dev/shm", devshm, NULL, MS_BIND, NULL);
177 if (r < 0) {
178 r = -errno;
179 goto fail;
180 }
181
63c372cb 182 devmqueue = strjoina(temporary_mount, "/dev/mqueue");
dc751688 183 (void) mkdir(devmqueue, 0755);
3164e3cb 184 (void) mount("/dev/mqueue", devmqueue, NULL, MS_BIND, NULL);
2b85f4e1 185
63c372cb 186 devhugepages = strjoina(temporary_mount, "/dev/hugepages");
dc751688 187 (void) mkdir(devhugepages, 0755);
3164e3cb 188 (void) mount("/dev/hugepages", devhugepages, NULL, MS_BIND, NULL);
2b85f4e1 189
63c372cb 190 devlog = strjoina(temporary_mount, "/dev/log");
3164e3cb 191 (void) symlink("/run/systemd/journal/dev-log", devlog);
82d25240 192
7f112f50 193 NULSTR_FOREACH(d, devnodes) {
2b85f4e1
LP
194 _cleanup_free_ char *dn = NULL;
195 struct stat st;
196
197 r = stat(d, &st);
7f112f50 198 if (r < 0) {
2b85f4e1
LP
199
200 if (errno == ENOENT)
201 continue;
202
203 r = -errno;
204 goto fail;
7f112f50
LP
205 }
206
2b85f4e1
LP
207 if (!S_ISBLK(st.st_mode) &&
208 !S_ISCHR(st.st_mode)) {
209 r = -EINVAL;
210 goto fail;
211 }
212
213 if (st.st_rdev == 0)
214 continue;
215
216 dn = strappend(temporary_mount, d);
217 if (!dn) {
218 r = -ENOMEM;
219 goto fail;
220 }
221
ecabcf8b 222 mac_selinux_create_file_prepare(d, st.st_mode);
2b85f4e1 223 r = mknod(dn, st.st_mode, st.st_rdev);
ecabcf8b 224 mac_selinux_create_file_clear();
dd078a1e 225
2b85f4e1
LP
226 if (r < 0) {
227 r = -errno;
228 goto fail;
229 }
7f112f50
LP
230 }
231
03cfe0d5 232 dev_setup(temporary_mount, UID_INVALID, GID_INVALID);
7f112f50 233
ee818b89
AC
234 /* Create the /dev directory if missing. It is more likely to be
235 * missing when the service is started with RootDirectory. This is
236 * consistent with mount units creating the mount points when missing.
237 */
238 (void) mkdir_p_label(m->path, 0755);
239
240 if (mount(dev, m->path, NULL, MS_MOVE, NULL) < 0) {
2b85f4e1
LP
241 r = -errno;
242 goto fail;
243 }
7f112f50 244
2b85f4e1
LP
245 rmdir(dev);
246 rmdir(temporary_mount);
7f112f50 247
2b85f4e1 248 return 0;
7f112f50 249
2b85f4e1
LP
250fail:
251 if (devpts)
252 umount(devpts);
7f112f50 253
2b85f4e1
LP
254 if (devshm)
255 umount(devshm);
7f112f50 256
2b85f4e1
LP
257 if (devhugepages)
258 umount(devhugepages);
7f112f50 259
2b85f4e1
LP
260 if (devmqueue)
261 umount(devmqueue);
7f112f50 262
d267c5aa
ZJS
263 umount(dev);
264 rmdir(dev);
2b85f4e1 265 rmdir(temporary_mount);
7f112f50 266
2b85f4e1 267 return r;
7f112f50
LP
268}
269
a610cc4f
DM
270static int mount_kdbus(BindMount *m) {
271
272 char temporary_mount[] = "/tmp/kdbus-dev-XXXXXX";
273 _cleanup_free_ char *basepath = NULL;
274 _cleanup_umask_ mode_t u;
120d578e 275 char *busnode = NULL, *root;
a610cc4f
DM
276 struct stat st;
277 int r;
278
279 assert(m);
280
281 u = umask(0000);
282
4a62c710
MS
283 if (!mkdtemp(temporary_mount))
284 return log_error_errno(errno, "Failed create temp dir: %m");
a610cc4f 285
63c372cb 286 root = strjoina(temporary_mount, "/kdbus");
dc751688 287 (void) mkdir(root, 0755);
a610cc4f
DM
288 if (mount("tmpfs", root, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=777") < 0) {
289 r = -errno;
290 goto fail;
291 }
292
293 /* create a new /dev/null dev node copy so we have some fodder to
294 * bind-mount the custom endpoint over. */
295 if (stat("/dev/null", &st) < 0) {
76ef789d 296 r = log_error_errno(errno, "Failed to stat /dev/null: %m");
a610cc4f
DM
297 goto fail;
298 }
299
63c372cb 300 busnode = strjoina(root, "/bus");
a610cc4f 301 if (mknod(busnode, (st.st_mode & ~07777) | 0600, st.st_rdev) < 0) {
94c156cd
LP
302 r = log_error_errno(errno, "mknod() for %s failed: %m",
303 busnode);
a610cc4f
DM
304 goto fail;
305 }
306
4543768d 307 r = mount(m->path, busnode, NULL, MS_BIND, NULL);
a610cc4f 308 if (r < 0) {
94c156cd
LP
309 r = log_error_errno(errno, "bind mount of %s failed: %m",
310 m->path);
a610cc4f
DM
311 goto fail;
312 }
313
314 basepath = dirname_malloc(m->path);
315 if (!basepath) {
316 r = -ENOMEM;
317 goto fail;
318 }
319
320 if (mount(root, basepath, NULL, MS_MOVE, NULL) < 0) {
94c156cd
LP
321 r = log_error_errno(errno, "bind mount of %s failed: %m",
322 basepath);
a610cc4f
DM
323 goto fail;
324 }
325
326 rmdir(temporary_mount);
327 return 0;
328
329fail:
330 if (busnode) {
331 umount(busnode);
332 unlink(busnode);
333 }
334
1775f1eb
ZJS
335 umount(root);
336 rmdir(root);
a610cc4f
DM
337 rmdir(temporary_mount);
338
339 return r;
340}
341
ac0930c8 342static int apply_mount(
c17ec25e 343 BindMount *m,
ac0930c8 344 const char *tmp_dir,
c17ec25e 345 const char *var_tmp_dir) {
ac0930c8 346
15ae422b 347 const char *what;
15ae422b 348 int r;
15ae422b 349
c17ec25e 350 assert(m);
15ae422b 351
c17ec25e 352 switch (m->mode) {
15ae422b
LP
353
354 case INACCESSIBLE:
6d313367
LP
355
356 /* First, get rid of everything that is below if there
357 * is anything... Then, overmount it with an
358 * inaccessible directory. */
359 umount_recursive(m->path, 0);
360
c17ec25e 361 what = "/run/systemd/inaccessible";
15ae422b
LP
362 break;
363
364 case READONLY:
15ae422b 365 case READWRITE:
d6797c92
LP
366 /* Nothing to mount here, we just later toggle the
367 * MS_RDONLY bit for the mount point */
368 return 0;
15ae422b 369
ac0930c8
LP
370 case PRIVATE_TMP:
371 what = tmp_dir;
372 break;
373
374 case PRIVATE_VAR_TMP:
375 what = var_tmp_dir;
15ae422b 376 break;
e364ad06 377
d6797c92
LP
378 case PRIVATE_DEV:
379 return mount_dev(m);
380
a610cc4f
DM
381 case PRIVATE_BUS_ENDPOINT:
382 return mount_kdbus(m);
383
e364ad06
LP
384 default:
385 assert_not_reached("Unknown mode");
15ae422b
LP
386 }
387
ac0930c8 388 assert(what);
15ae422b 389
c17ec25e 390 r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL);
ac0930c8 391 if (r >= 0)
c17ec25e 392 log_debug("Successfully mounted %s to %s", what, m->path);
ea92ae33 393 else if (m->ignore && errno == ENOENT)
d6797c92 394 return 0;
15ae422b 395
ac0930c8
LP
396 return r;
397}
15ae422b 398
c17ec25e 399static int make_read_only(BindMount *m) {
ac0930c8 400 int r;
15ae422b 401
c17ec25e 402 assert(m);
ac0930c8 403
d6797c92
LP
404 if (IN_SET(m->mode, INACCESSIBLE, READONLY))
405 r = bind_remount_recursive(m->path, true);
664064d6 406 else if (IN_SET(m->mode, READWRITE, PRIVATE_TMP, PRIVATE_VAR_TMP, PRIVATE_DEV))
d6797c92
LP
407 r = bind_remount_recursive(m->path, false);
408 else
409 r = 0;
ac0930c8 410
d6797c92
LP
411 if (m->ignore && r == -ENOENT)
412 return 0;
ac0930c8 413
d6797c92 414 return r;
15ae422b
LP
415}
416
613b411c 417int setup_namespace(
ee818b89 418 const char* root_directory,
613b411c
LP
419 char** read_write_dirs,
420 char** read_only_dirs,
421 char** inaccessible_dirs,
a004cb4c
LP
422 const char* tmp_dir,
423 const char* var_tmp_dir,
424 const char* bus_endpoint_path,
7f112f50 425 bool private_dev,
1b8689f9
LP
426 ProtectHome protect_home,
427 ProtectSystem protect_system,
e6547662 428 unsigned long mount_flags) {
15ae422b 429
7ff7394d 430 BindMount *m, *mounts = NULL;
613b411c 431 unsigned n;
c17ec25e 432 int r = 0;
15ae422b 433
613b411c 434 if (mount_flags == 0)
c17ec25e 435 mount_flags = MS_SHARED;
ac0930c8 436
d5a3f0ea
ZJS
437 if (unshare(CLONE_NEWNS) < 0)
438 return -errno;
15ae422b 439
a610cc4f 440 n = !!tmp_dir + !!var_tmp_dir + !!bus_endpoint_path +
613b411c
LP
441 strv_length(read_write_dirs) +
442 strv_length(read_only_dirs) +
7f112f50 443 strv_length(inaccessible_dirs) +
417116f2 444 private_dev +
c8835999 445 (protect_home != PROTECT_HOME_NO ? 3 : 0) +
051be1f7 446 (protect_system != PROTECT_SYSTEM_NO ? 2 : 0) +
1b8689f9 447 (protect_system == PROTECT_SYSTEM_FULL ? 1 : 0);
613b411c
LP
448
449 if (n > 0) {
002b2268 450 m = mounts = (BindMount *) alloca0(n * sizeof(BindMount));
613b411c
LP
451 r = append_mounts(&m, read_write_dirs, READWRITE);
452 if (r < 0)
453 return r;
454
455 r = append_mounts(&m, read_only_dirs, READONLY);
456 if (r < 0)
457 return r;
458
459 r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE);
460 if (r < 0)
7ff7394d
ZJS
461 return r;
462
613b411c 463 if (tmp_dir) {
ee818b89 464 m->path = prefix_roota(root_directory, "/tmp");
7ff7394d
ZJS
465 m->mode = PRIVATE_TMP;
466 m++;
613b411c 467 }
7ff7394d 468
613b411c 469 if (var_tmp_dir) {
ee818b89 470 m->path = prefix_roota(root_directory, "/var/tmp");
7ff7394d
ZJS
471 m->mode = PRIVATE_VAR_TMP;
472 m++;
473 }
ac0930c8 474
7f112f50 475 if (private_dev) {
ee818b89 476 m->path = prefix_roota(root_directory, "/dev");
7f112f50
LP
477 m->mode = PRIVATE_DEV;
478 m++;
479 }
480
a610cc4f 481 if (bus_endpoint_path) {
ee818b89 482 m->path = prefix_roota(root_directory, bus_endpoint_path);
a610cc4f
DM
483 m->mode = PRIVATE_BUS_ENDPOINT;
484 m++;
485 }
486
1b8689f9 487 if (protect_home != PROTECT_HOME_NO) {
ee818b89
AC
488 const char *home_dir, *run_user_dir, *root_dir;
489
490 home_dir = prefix_roota(root_directory, "/home");
491 home_dir = strjoina("-", home_dir);
492 run_user_dir = prefix_roota(root_directory, "/run/user");
493 run_user_dir = strjoina("-", run_user_dir);
494 root_dir = prefix_roota(root_directory, "/root");
495 root_dir = strjoina("-", root_dir);
496
497 r = append_mounts(&m, STRV_MAKE(home_dir, run_user_dir, root_dir),
498 protect_home == PROTECT_HOME_READ_ONLY ? READONLY : INACCESSIBLE);
417116f2
LP
499 if (r < 0)
500 return r;
501 }
502
1b8689f9 503 if (protect_system != PROTECT_SYSTEM_NO) {
ee818b89
AC
504 const char *usr_dir, *boot_dir, *etc_dir;
505
d38e01dc 506 usr_dir = prefix_roota(root_directory, "/usr");
ee818b89
AC
507 boot_dir = prefix_roota(root_directory, "/boot");
508 boot_dir = strjoina("-", boot_dir);
509 etc_dir = prefix_roota(root_directory, "/etc");
510
511 r = append_mounts(&m, protect_system == PROTECT_SYSTEM_FULL
512 ? STRV_MAKE(usr_dir, boot_dir, etc_dir)
513 : STRV_MAKE(usr_dir, boot_dir), READONLY);
417116f2
LP
514 if (r < 0)
515 return r;
516 }
517
7ff7394d 518 assert(mounts + n == m);
ac0930c8 519
7ff7394d
ZJS
520 qsort(mounts, n, sizeof(BindMount), mount_path_compare);
521 drop_duplicates(mounts, &n);
15ae422b
LP
522 }
523
ee818b89 524 if (n > 0 || root_directory) {
c2c13f2d
LP
525 /* Remount / as SLAVE so that nothing now mounted in the namespace
526 shows up in the parent */
527 if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
528 return -errno;
ee818b89
AC
529 }
530
531 if (root_directory) {
532 /* Turn directory into bind mount */
533 if (mount(root_directory, root_directory, NULL, MS_BIND|MS_REC, NULL) < 0)
534 return -errno;
535 }
c2c13f2d 536
ee818b89 537 if (n > 0) {
c2c13f2d
LP
538 for (m = mounts; m < mounts + n; ++m) {
539 r = apply_mount(m, tmp_dir, var_tmp_dir);
540 if (r < 0)
541 goto fail;
542 }
15ae422b 543
c2c13f2d
LP
544 for (m = mounts; m < mounts + n; ++m) {
545 r = make_read_only(m);
546 if (r < 0)
547 goto fail;
548 }
15ae422b
LP
549 }
550
ee818b89
AC
551 if (root_directory) {
552 /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
553 r = mount_move_root(root_directory);
554
555 /* at this point, we cannot rollback */
556 if (r < 0)
557 return r;
558 }
559
c2c13f2d
LP
560 /* Remount / as the desired mode. Not that this will not
561 * reestablish propagation from our side to the host, since
562 * what's disconnected is disconnected. */
1f6b4113 563 if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0)
ee818b89
AC
564 /* at this point, we cannot rollback */
565 return -errno;
15ae422b 566
15ae422b
LP
567 return 0;
568
613b411c 569fail:
c2c13f2d
LP
570 if (n > 0) {
571 for (m = mounts; m < mounts + n; ++m)
572 if (m->done)
42b1b990 573 (void) umount2(m->path, MNT_DETACH);
c2c13f2d 574 }
613b411c
LP
575
576 return r;
577}
578
579static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) {
580 _cleanup_free_ char *x = NULL;
6b46ea73
LP
581 char bid[SD_ID128_STRING_MAX];
582 sd_id128_t boot_id;
583 int r;
613b411c
LP
584
585 assert(id);
586 assert(prefix);
587 assert(path);
588
6b46ea73
LP
589 /* We include the boot id in the directory so that after a
590 * reboot we can easily identify obsolete directories. */
591
592 r = sd_id128_get_boot(&boot_id);
593 if (r < 0)
594 return r;
595
596 x = strjoin(prefix, "/systemd-private-", sd_id128_to_string(boot_id, bid), "-", id, "-XXXXXX", NULL);
613b411c
LP
597 if (!x)
598 return -ENOMEM;
599
600 RUN_WITH_UMASK(0077)
601 if (!mkdtemp(x))
602 return -errno;
603
604 RUN_WITH_UMASK(0000) {
605 char *y;
606
63c372cb 607 y = strjoina(x, "/tmp");
613b411c
LP
608
609 if (mkdir(y, 0777 | S_ISVTX) < 0)
610 return -errno;
c17ec25e 611 }
15ae422b 612
613b411c
LP
613 *path = x;
614 x = NULL;
615
616 return 0;
617}
618
619int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) {
620 char *a, *b;
621 int r;
622
623 assert(id);
624 assert(tmp_dir);
625 assert(var_tmp_dir);
626
627 r = setup_one_tmp_dir(id, "/tmp", &a);
628 if (r < 0)
629 return r;
630
631 r = setup_one_tmp_dir(id, "/var/tmp", &b);
632 if (r < 0) {
633 char *t;
634
63c372cb 635 t = strjoina(a, "/tmp");
613b411c
LP
636 rmdir(t);
637 rmdir(a);
638
639 free(a);
640 return r;
641 }
642
643 *tmp_dir = a;
644 *var_tmp_dir = b;
645
646 return 0;
647}
648
649int setup_netns(int netns_storage_socket[2]) {
650 _cleanup_close_ int netns = -1;
3ee897d6 651 int r, q;
613b411c
LP
652
653 assert(netns_storage_socket);
654 assert(netns_storage_socket[0] >= 0);
655 assert(netns_storage_socket[1] >= 0);
656
657 /* We use the passed socketpair as a storage buffer for our
76cd584b
LP
658 * namespace reference fd. Whatever process runs this first
659 * shall create a new namespace, all others should just join
660 * it. To serialize that we use a file lock on the socket
661 * pair.
613b411c
LP
662 *
663 * It's a bit crazy, but hey, works great! */
664
665 if (lockf(netns_storage_socket[0], F_LOCK, 0) < 0)
666 return -errno;
667
3ee897d6
LP
668 netns = receive_one_fd(netns_storage_socket[0], MSG_DONTWAIT);
669 if (netns == -EAGAIN) {
613b411c
LP
670 /* Nothing stored yet, so let's create a new namespace */
671
672 if (unshare(CLONE_NEWNET) < 0) {
673 r = -errno;
674 goto fail;
675 }
676
677 loopback_setup();
678
679 netns = open("/proc/self/ns/net", O_RDONLY|O_CLOEXEC|O_NOCTTY);
680 if (netns < 0) {
681 r = -errno;
682 goto fail;
683 }
684
685 r = 1;
613b411c 686
3ee897d6
LP
687 } else if (netns < 0) {
688 r = netns;
689 goto fail;
613b411c 690
3ee897d6
LP
691 } else {
692 /* Yay, found something, so let's join the namespace */
613b411c
LP
693 if (setns(netns, CLONE_NEWNET) < 0) {
694 r = -errno;
695 goto fail;
696 }
697
698 r = 0;
699 }
700
3ee897d6
LP
701 q = send_one_fd(netns_storage_socket[1], netns, MSG_DONTWAIT);
702 if (q < 0) {
703 r = q;
613b411c
LP
704 goto fail;
705 }
706
707fail:
708 lockf(netns_storage_socket[0], F_ULOCK, 0);
15ae422b
LP
709 return r;
710}
417116f2 711
1b8689f9
LP
712static const char *const protect_home_table[_PROTECT_HOME_MAX] = {
713 [PROTECT_HOME_NO] = "no",
714 [PROTECT_HOME_YES] = "yes",
715 [PROTECT_HOME_READ_ONLY] = "read-only",
417116f2
LP
716};
717
1b8689f9
LP
718DEFINE_STRING_TABLE_LOOKUP(protect_home, ProtectHome);
719
720static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = {
721 [PROTECT_SYSTEM_NO] = "no",
722 [PROTECT_SYSTEM_YES] = "yes",
723 [PROTECT_SYSTEM_FULL] = "full",
724};
725
726DEFINE_STRING_TABLE_LOOKUP(protect_system, ProtectSystem);