2 This file is part of systemd.
4 Copyright 2010 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/mount.h>
29 #include "alloc-util.h"
30 #include "dev-setup.h"
33 #include "loopback-setup.h"
36 #include "mount-util.h"
37 #include "namespace.h"
38 #include "path-util.h"
39 #include "selinux-util.h"
40 #include "socket-util.h"
41 #include "string-table.h"
42 #include "string-util.h"
44 #include "umask-util.h"
45 #include "user-util.h"
48 #define DEV_MOUNT_OPTIONS (MS_NOSUID|MS_STRICTATIME|MS_NOEXEC)
50 typedef enum MountMode
{
51 /* This is ordered by priority! */
60 typedef struct BindMount
{
61 const char *path
; /* stack memory, doesn't need to be freed explicitly */
62 char *chased
; /* malloc()ed memory, needs to be freed */
64 bool ignore
; /* Ignore if path does not exist */
67 typedef struct TargetMount
{
70 bool ignore
; /* Ignore if path does not exist */
74 * The following Protect tables are to protect paths and mark some of them
75 * READONLY, in case a path is covered by an option from another table, then
76 * it is marked READWRITE in the current one, and the more restrictive mode is
77 * applied from that other table. This way all options can be combined in a
78 * safe and comprehensible way for users.
81 /* ProtectKernelTunables= option and the related filesystem APIs */
82 static const TargetMount protect_kernel_tunables_table
[] = {
83 { "/proc/sys", READONLY
, false },
84 { "/proc/sysrq-trigger", READONLY
, true },
85 { "/proc/latency_stats", READONLY
, true },
86 { "/proc/mtrr", READONLY
, true },
87 { "/proc/apm", READONLY
, true },
88 { "/proc/acpi", READONLY
, true },
89 { "/proc/timer_stats", READONLY
, true },
90 { "/proc/asound", READONLY
, true },
91 { "/proc/bus", READONLY
, true },
92 { "/proc/fs", READONLY
, true },
93 { "/proc/irq", READONLY
, true },
94 { "/sys", READONLY
, false },
95 { "/sys/kernel/debug", READONLY
, true },
96 { "/sys/kernel/tracing", READONLY
, true },
97 { "/sys/fs/cgroup", READWRITE
, false }, /* READONLY is set by ProtectControlGroups= option */
100 /* ProtectKernelModules= option */
101 static const TargetMount protect_kernel_modules_table
[] = {
102 #ifdef HAVE_SPLIT_USR
103 { "/lib/modules", INACCESSIBLE
, true },
105 { "/usr/lib/modules", INACCESSIBLE
, true },
109 * ProtectHome=read-only table, protect $HOME and $XDG_RUNTIME_DIR and rest of
110 * system should be protected by ProtectSystem=
112 static const TargetMount protect_home_read_only_table
[] = {
113 { "/home", READONLY
, true },
114 { "/run/user", READONLY
, true },
115 { "/root", READONLY
, true },
118 /* ProtectHome=yes table */
119 static const TargetMount protect_home_yes_table
[] = {
120 { "/home", INACCESSIBLE
, true },
121 { "/run/user", INACCESSIBLE
, true },
122 { "/root", INACCESSIBLE
, true },
125 /* ProtectSystem=yes table */
126 static const TargetMount protect_system_yes_table
[] = {
127 { "/usr", READONLY
, false },
128 { "/boot", READONLY
, true },
129 { "/efi", READONLY
, true },
132 /* ProtectSystem=full includes ProtectSystem=yes */
133 static const TargetMount protect_system_full_table
[] = {
134 { "/usr", READONLY
, false },
135 { "/boot", READONLY
, true },
136 { "/efi", READONLY
, true },
137 { "/etc", READONLY
, false },
141 * ProtectSystem=strict table. In this strict mode, we mount everything
142 * read-only, except for /proc, /dev, /sys which are the kernel API VFS,
143 * which are left writable, but PrivateDevices= + ProtectKernelTunables=
144 * protect those, and these options should be fully orthogonal.
145 * (And of course /home and friends are also left writable, as ProtectHome=
146 * shall manage those, orthogonally).
148 static const TargetMount protect_system_strict_table
[] = {
149 { "/", READONLY
, false },
150 { "/proc", READWRITE
, false }, /* ProtectKernelTunables= */
151 { "/sys", READWRITE
, false }, /* ProtectKernelTunables= */
152 { "/dev", READWRITE
, false }, /* PrivateDevices= */
153 { "/home", READWRITE
, true }, /* ProtectHome= */
154 { "/run/user", READWRITE
, true }, /* ProtectHome= */
155 { "/root", READWRITE
, true }, /* ProtectHome= */
158 static void set_bind_mount(BindMount
**p
, const char *path
, MountMode mode
, bool ignore
) {
161 (*p
)->ignore
= ignore
;
164 static int append_mounts(BindMount
**p
, char **strv
, MountMode mode
) {
169 STRV_FOREACH(i
, strv
) {
172 if (IN_SET(mode
, INACCESSIBLE
, READONLY
, READWRITE
) && startswith(*i
, "-")) {
177 if (!path_is_absolute(*i
))
180 set_bind_mount(p
, *i
, mode
, ignore
);
187 static int append_target_mounts(BindMount
**p
, const char *root_directory
, const TargetMount
*mounts
, const size_t size
) {
193 for (i
= 0; i
< size
; i
++) {
195 * Here we assume that the ignore field is set during
196 * declaration we do not support "-" at the beginning.
198 const TargetMount
*m
= &mounts
[i
];
199 const char *path
= prefix_roota(root_directory
, m
->path
);
201 if (!path_is_absolute(path
))
204 set_bind_mount(p
, path
, m
->mode
, m
->ignore
);
211 static int append_protect_kernel_tunables(BindMount
**p
, const char *root_directory
) {
214 return append_target_mounts(p
, root_directory
, protect_kernel_tunables_table
,
215 ELEMENTSOF(protect_kernel_tunables_table
));
218 static int append_protect_kernel_modules(BindMount
**p
, const char *root_directory
) {
221 return append_target_mounts(p
, root_directory
, protect_kernel_modules_table
,
222 ELEMENTSOF(protect_kernel_modules_table
));
225 static int append_protect_home(BindMount
**p
, const char *root_directory
, ProtectHome protect_home
) {
230 if (protect_home
== PROTECT_HOME_NO
)
233 switch (protect_home
) {
234 case PROTECT_HOME_READ_ONLY
:
235 r
= append_target_mounts(p
, root_directory
, protect_home_read_only_table
,
236 ELEMENTSOF(protect_home_read_only_table
));
238 case PROTECT_HOME_YES
:
239 r
= append_target_mounts(p
, root_directory
, protect_home_yes_table
,
240 ELEMENTSOF(protect_home_yes_table
));
250 static int append_protect_system(BindMount
**p
, const char *root_directory
, ProtectSystem protect_system
) {
255 if (protect_system
== PROTECT_SYSTEM_NO
)
258 switch (protect_system
) {
259 case PROTECT_SYSTEM_STRICT
:
260 r
= append_target_mounts(p
, root_directory
, protect_system_strict_table
,
261 ELEMENTSOF(protect_system_strict_table
));
263 case PROTECT_SYSTEM_YES
:
264 r
= append_target_mounts(p
, root_directory
, protect_system_yes_table
,
265 ELEMENTSOF(protect_system_yes_table
));
267 case PROTECT_SYSTEM_FULL
:
268 r
= append_target_mounts(p
, root_directory
, protect_system_full_table
,
269 ELEMENTSOF(protect_system_full_table
));
279 static int mount_path_compare(const void *a
, const void *b
) {
280 const BindMount
*p
= a
, *q
= b
;
283 /* If the paths are not equal, then order prefixes first */
284 d
= path_compare(p
->path
, q
->path
);
288 /* If the paths are equal, check the mode */
289 if (p
->mode
< q
->mode
)
292 if (p
->mode
> q
->mode
)
298 static void drop_duplicates(BindMount
*m
, unsigned *n
) {
299 BindMount
*f
, *t
, *previous
;
304 /* Drops duplicate entries. Expects that the array is properly ordered already. */
306 for (f
= m
, t
= m
, previous
= NULL
; f
< m
+*n
; f
++) {
308 /* The first one wins (which is the one with the more restrictive mode), see mount_path_compare()
310 if (previous
&& path_equal(f
->path
, previous
->path
)) {
311 log_debug("%s is duplicate.", f
->path
);
323 static void drop_inaccessible(BindMount
*m
, unsigned *n
) {
325 const char *clear
= NULL
;
330 /* Drops all entries obstructed by another entry further up the tree. Expects that the array is properly
331 * ordered already. */
333 for (f
= m
, t
= m
; f
< m
+*n
; f
++) {
335 /* If we found a path set for INACCESSIBLE earlier, and this entry has it as prefix we should drop
336 * it, as inaccessible paths really should drop the entire subtree. */
337 if (clear
&& path_startswith(f
->path
, clear
)) {
338 log_debug("%s is masked by %s.", f
->path
, clear
);
342 clear
= f
->mode
== INACCESSIBLE
? f
->path
: NULL
;
351 static void drop_nop(BindMount
*m
, unsigned *n
) {
357 /* Drops all entries which have an immediate parent that has the same type, as they are redundant. Assumes the
358 * list is ordered by prefixes. */
360 for (f
= m
, t
= m
; f
< m
+*n
; f
++) {
362 /* Only suppress such subtrees for READONLY and READWRITE entries */
363 if (IN_SET(f
->mode
, READONLY
, READWRITE
)) {
367 /* Now let's find the first parent of the entry we are looking at. */
368 for (p
= t
-1; p
>= m
; p
--) {
369 if (path_startswith(f
->path
, p
->path
)) {
375 /* We found it, let's see if it's the same mode, if so, we can drop this entry */
376 if (found
&& p
->mode
== f
->mode
) {
377 log_debug("%s is redundant by %s", f
->path
, p
->path
);
389 static void drop_outside_root(const char *root_directory
, BindMount
*m
, unsigned *n
) {
398 /* Drops all mounts that are outside of the root directory. */
400 for (f
= m
, t
= m
; f
< m
+*n
; f
++) {
402 if (!path_startswith(f
->path
, root_directory
)) {
403 log_debug("%s is outside of root directory.", f
->path
);
414 static int mount_dev(BindMount
*m
) {
415 static const char devnodes
[] =
423 char temporary_mount
[] = "/tmp/namespace-dev-XXXXXX";
424 const char *d
, *dev
= NULL
, *devpts
= NULL
, *devshm
= NULL
, *devhugepages
= NULL
, *devmqueue
= NULL
, *devlog
= NULL
, *devptmx
= NULL
;
425 _cleanup_umask_ mode_t u
;
432 if (!mkdtemp(temporary_mount
))
435 dev
= strjoina(temporary_mount
, "/dev");
436 (void) mkdir(dev
, 0755);
437 if (mount("tmpfs", dev
, "tmpfs", DEV_MOUNT_OPTIONS
, "mode=755") < 0) {
442 devpts
= strjoina(temporary_mount
, "/dev/pts");
443 (void) mkdir(devpts
, 0755);
444 if (mount("/dev/pts", devpts
, NULL
, MS_BIND
, NULL
) < 0) {
449 devptmx
= strjoina(temporary_mount
, "/dev/ptmx");
450 if (symlink("pts/ptmx", devptmx
) < 0) {
455 devshm
= strjoina(temporary_mount
, "/dev/shm");
456 (void) mkdir(devshm
, 01777);
457 r
= mount("/dev/shm", devshm
, NULL
, MS_BIND
, NULL
);
463 devmqueue
= strjoina(temporary_mount
, "/dev/mqueue");
464 (void) mkdir(devmqueue
, 0755);
465 (void) mount("/dev/mqueue", devmqueue
, NULL
, MS_BIND
, NULL
);
467 devhugepages
= strjoina(temporary_mount
, "/dev/hugepages");
468 (void) mkdir(devhugepages
, 0755);
469 (void) mount("/dev/hugepages", devhugepages
, NULL
, MS_BIND
, NULL
);
471 devlog
= strjoina(temporary_mount
, "/dev/log");
472 (void) symlink("/run/systemd/journal/dev-log", devlog
);
474 NULSTR_FOREACH(d
, devnodes
) {
475 _cleanup_free_
char *dn
= NULL
;
488 if (!S_ISBLK(st
.st_mode
) &&
489 !S_ISCHR(st
.st_mode
)) {
497 dn
= strappend(temporary_mount
, d
);
503 mac_selinux_create_file_prepare(d
, st
.st_mode
);
504 r
= mknod(dn
, st
.st_mode
, st
.st_rdev
);
505 mac_selinux_create_file_clear();
513 dev_setup(temporary_mount
, UID_INVALID
, GID_INVALID
);
515 /* Create the /dev directory if missing. It is more likely to be
516 * missing when the service is started with RootDirectory. This is
517 * consistent with mount units creating the mount points when missing.
519 (void) mkdir_p_label(m
->path
, 0755);
521 /* Unmount everything in old /dev */
522 umount_recursive(m
->path
, 0);
523 if (mount(dev
, m
->path
, NULL
, MS_MOVE
, NULL
) < 0) {
529 rmdir(temporary_mount
);
541 umount(devhugepages
);
548 rmdir(temporary_mount
);
553 static int apply_mount(
556 const char *var_tmp_dir
) {
563 log_debug("Applying namespace mount on %s", m
->path
);
570 /* First, get rid of everything that is below if there
571 * is anything... Then, overmount it with an
572 * inaccessible path. */
573 (void) umount_recursive(m
->path
, 0);
575 if (lstat(m
->path
, &target
) < 0)
576 return log_debug_errno(errno
, "Failed to lstat() %s to determine what to mount over it: %m", m
->path
);
578 what
= mode_to_inaccessible_node(target
.st_mode
);
580 log_debug("File type not supported for inaccessible mounts. Note that symlinks are not allowed");
589 r
= path_is_mount_point(m
->path
, 0);
591 return log_debug_errno(r
, "Failed to determine whether %s is already a mount point: %m", m
->path
);
592 if (r
> 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY bit for the mount point if needed. */
595 /* This isn't a mount point yet, let's make it one. */
603 case PRIVATE_VAR_TMP
:
611 assert_not_reached("Unknown mode");
616 if (mount(what
, m
->path
, NULL
, MS_BIND
|MS_REC
, NULL
) < 0)
617 return log_debug_errno(errno
, "Failed to mount %s to %s: %m", what
, m
->path
);
619 log_debug("Successfully mounted %s to %s", what
, m
->path
);
623 static int make_read_only(BindMount
*m
, char **blacklist
) {
628 if (IN_SET(m
->mode
, INACCESSIBLE
, READONLY
))
629 r
= bind_remount_recursive(m
->path
, true, blacklist
);
630 else if (m
->mode
== PRIVATE_DEV
) { /* Can be readonly but the submounts can't*/
631 if (mount(NULL
, m
->path
, NULL
, MS_REMOUNT
|DEV_MOUNT_OPTIONS
|MS_RDONLY
, NULL
) < 0)
636 /* Not that we only turn on the MS_RDONLY flag here, we never turn it off. Something that was marked read-only
637 * already stays this way. This improves compatibility with container managers, where we won't attempt to undo
638 * read-only mounts already applied. */
643 static int chase_all_symlinks(const char *root_directory
, BindMount
*m
, unsigned *n
) {
650 /* Since mount() will always follow symlinks and we need to take the different root directory into account we
651 * chase the symlinks on our own first. This call wil do so for all entries and remove all entries where we
652 * can't resolve the path, and which have been marked for such removal. */
654 for (f
= m
, t
= m
; f
< m
+*n
; f
++) {
656 r
= chase_symlinks(f
->path
, root_directory
, &f
->chased
);
657 if (r
== -ENOENT
&& f
->ignore
) /* Doesn't exist? Then remove it! */
660 return log_debug_errno(r
, "Failed to chase symlinks for %s: %m", f
->path
);
662 if (path_equal(f
->path
, f
->chased
))
663 f
->chased
= mfree(f
->chased
);
665 log_debug("Chased %s → %s", f
->path
, f
->chased
);
677 static unsigned namespace_calculate_mounts(
678 const NameSpaceInfo
*ns_info
,
679 char** read_write_paths
,
680 char** read_only_paths
,
681 char** inaccessible_paths
,
683 const char* var_tmp_dir
,
684 ProtectHome protect_home
,
685 ProtectSystem protect_system
) {
687 unsigned protect_home_cnt
;
688 unsigned protect_system_cnt
=
689 (protect_system
== PROTECT_SYSTEM_STRICT
?
690 ELEMENTSOF(protect_system_strict_table
) :
691 ((protect_system
== PROTECT_SYSTEM_FULL
) ?
692 ELEMENTSOF(protect_system_full_table
) :
693 ((protect_system
== PROTECT_SYSTEM_YES
) ?
694 ELEMENTSOF(protect_system_yes_table
) : 0)));
697 (protect_home
== PROTECT_HOME_YES
?
698 ELEMENTSOF(protect_home_yes_table
) :
699 ((protect_home
== PROTECT_HOME_READ_ONLY
) ?
700 ELEMENTSOF(protect_home_read_only_table
) : 0));
702 return !!tmp_dir
+ !!var_tmp_dir
+
703 strv_length(read_write_paths
) +
704 strv_length(read_only_paths
) +
705 strv_length(inaccessible_paths
) +
706 ns_info
->private_dev
+
707 (ns_info
->protect_kernel_tunables
? ELEMENTSOF(protect_kernel_tunables_table
) : 0) +
708 (ns_info
->protect_control_groups
? 1 : 0) +
709 (ns_info
->protect_kernel_modules
? ELEMENTSOF(protect_kernel_modules_table
) : 0) +
710 protect_home_cnt
+ protect_system_cnt
;
714 const char* root_directory
,
715 const NameSpaceInfo
*ns_info
,
716 char** read_write_paths
,
717 char** read_only_paths
,
718 char** inaccessible_paths
,
720 const char* var_tmp_dir
,
721 ProtectHome protect_home
,
722 ProtectSystem protect_system
,
723 unsigned long mount_flags
) {
725 BindMount
*m
, *mounts
= NULL
;
726 bool make_slave
= false;
730 if (mount_flags
== 0)
731 mount_flags
= MS_SHARED
;
733 n
= namespace_calculate_mounts(ns_info
,
737 tmp_dir
, var_tmp_dir
,
738 protect_home
, protect_system
);
740 /* Set mount slave mode */
741 if (root_directory
|| n
> 0)
745 m
= mounts
= (BindMount
*) alloca0(n
* sizeof(BindMount
));
746 r
= append_mounts(&m
, read_write_paths
, READWRITE
);
750 r
= append_mounts(&m
, read_only_paths
, READONLY
);
754 r
= append_mounts(&m
, inaccessible_paths
, INACCESSIBLE
);
759 m
->path
= prefix_roota(root_directory
, "/tmp");
760 m
->mode
= PRIVATE_TMP
;
765 m
->path
= prefix_roota(root_directory
, "/var/tmp");
766 m
->mode
= PRIVATE_VAR_TMP
;
770 if (ns_info
->private_dev
) {
771 m
->path
= prefix_roota(root_directory
, "/dev");
772 m
->mode
= PRIVATE_DEV
;
776 if (ns_info
->protect_kernel_tunables
) {
777 r
= append_protect_kernel_tunables(&m
, root_directory
);
782 if (ns_info
->protect_kernel_modules
) {
783 r
= append_protect_kernel_modules(&m
, root_directory
);
788 if (ns_info
->protect_control_groups
) {
789 m
->path
= prefix_roota(root_directory
, "/sys/fs/cgroup");
794 r
= append_protect_home(&m
, root_directory
, protect_home
);
798 r
= append_protect_system(&m
, root_directory
, protect_system
);
802 assert(mounts
+ n
== m
);
804 /* Resolve symlinks manually first, as mount() will always follow them relative to the host's
805 * root. Moreover we want to suppress duplicates based on the resolved paths. This of course is a bit
807 r
= chase_all_symlinks(root_directory
, mounts
, &n
);
811 qsort(mounts
, n
, sizeof(BindMount
), mount_path_compare
);
813 drop_duplicates(mounts
, &n
);
814 drop_outside_root(root_directory
, mounts
, &n
);
815 drop_inaccessible(mounts
, &n
);
816 drop_nop(mounts
, &n
);
819 if (unshare(CLONE_NEWNS
) < 0) {
825 /* Remount / as SLAVE so that nothing now mounted in the namespace
826 shows up in the parent */
827 if (mount(NULL
, "/", NULL
, MS_SLAVE
|MS_REC
, NULL
) < 0) {
833 if (root_directory
) {
834 /* Turn directory into bind mount, if it isn't one yet */
835 r
= path_is_mount_point(root_directory
, AT_SYMLINK_FOLLOW
);
839 if (mount(root_directory
, root_directory
, NULL
, MS_BIND
|MS_REC
, NULL
) < 0) {
850 /* First round, add in all special mounts we need */
851 for (m
= mounts
; m
< mounts
+ n
; ++m
) {
852 r
= apply_mount(m
, tmp_dir
, var_tmp_dir
);
857 /* Create a blacklist we can pass to bind_mount_recursive() */
858 blacklist
= newa(char*, n
+1);
859 for (j
= 0; j
< n
; j
++)
860 blacklist
[j
] = (char*) mounts
[j
].path
;
863 /* Second round, flip the ro bits if necessary. */
864 for (m
= mounts
; m
< mounts
+ n
; ++m
) {
865 r
= make_read_only(m
, blacklist
);
871 if (root_directory
) {
872 /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */
873 r
= mount_move_root(root_directory
);
878 /* Remount / as the desired mode. Not that this will not
879 * reestablish propagation from our side to the host, since
880 * what's disconnected is disconnected. */
881 if (mount(NULL
, "/", NULL
, mount_flags
| MS_REC
, NULL
) < 0) {
889 for (m
= mounts
; m
< mounts
+ n
; m
++)
895 static int setup_one_tmp_dir(const char *id
, const char *prefix
, char **path
) {
896 _cleanup_free_
char *x
= NULL
;
897 char bid
[SD_ID128_STRING_MAX
];
905 /* We include the boot id in the directory so that after a
906 * reboot we can easily identify obsolete directories. */
908 r
= sd_id128_get_boot(&boot_id
);
912 x
= strjoin(prefix
, "/systemd-private-", sd_id128_to_string(boot_id
, bid
), "-", id
, "-XXXXXX");
920 RUN_WITH_UMASK(0000) {
923 y
= strjoina(x
, "/tmp");
925 if (mkdir(y
, 0777 | S_ISVTX
) < 0)
935 int setup_tmp_dirs(const char *id
, char **tmp_dir
, char **var_tmp_dir
) {
943 r
= setup_one_tmp_dir(id
, "/tmp", &a
);
947 r
= setup_one_tmp_dir(id
, "/var/tmp", &b
);
951 t
= strjoina(a
, "/tmp");
965 int setup_netns(int netns_storage_socket
[2]) {
966 _cleanup_close_
int netns
= -1;
969 assert(netns_storage_socket
);
970 assert(netns_storage_socket
[0] >= 0);
971 assert(netns_storage_socket
[1] >= 0);
973 /* We use the passed socketpair as a storage buffer for our
974 * namespace reference fd. Whatever process runs this first
975 * shall create a new namespace, all others should just join
976 * it. To serialize that we use a file lock on the socket
979 * It's a bit crazy, but hey, works great! */
981 if (lockf(netns_storage_socket
[0], F_LOCK
, 0) < 0)
984 netns
= receive_one_fd(netns_storage_socket
[0], MSG_DONTWAIT
);
985 if (netns
== -EAGAIN
) {
986 /* Nothing stored yet, so let's create a new namespace */
988 if (unshare(CLONE_NEWNET
) < 0) {
995 netns
= open("/proc/self/ns/net", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
1003 } else if (netns
< 0) {
1008 /* Yay, found something, so let's join the namespace */
1009 if (setns(netns
, CLONE_NEWNET
) < 0) {
1017 q
= send_one_fd(netns_storage_socket
[1], netns
, MSG_DONTWAIT
);
1024 (void) lockf(netns_storage_socket
[0], F_ULOCK
, 0);
1028 static const char *const protect_home_table
[_PROTECT_HOME_MAX
] = {
1029 [PROTECT_HOME_NO
] = "no",
1030 [PROTECT_HOME_YES
] = "yes",
1031 [PROTECT_HOME_READ_ONLY
] = "read-only",
1034 DEFINE_STRING_TABLE_LOOKUP(protect_home
, ProtectHome
);
1036 static const char *const protect_system_table
[_PROTECT_SYSTEM_MAX
] = {
1037 [PROTECT_SYSTEM_NO
] = "no",
1038 [PROTECT_SYSTEM_YES
] = "yes",
1039 [PROTECT_SYSTEM_FULL
] = "full",
1040 [PROTECT_SYSTEM_STRICT
] = "strict",
1043 DEFINE_STRING_TABLE_LOOKUP(protect_system
, ProtectSystem
);