| File: | build-scan/../src/core/namespace.c |
| Warning: | line 639, column 9 Value stored to 'u' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
| 2 | |
| 3 | #include <errno(*__errno_location ()).h> |
| 4 | #include <sched.h> |
| 5 | #include <stdio.h> |
| 6 | #include <string.h> |
| 7 | #include <sys/mount.h> |
| 8 | #include <sys/stat.h> |
| 9 | #include <unistd.h> |
| 10 | #include <linux1/fs.h> |
| 11 | |
| 12 | #include "alloc-util.h" |
| 13 | #include "base-filesystem.h" |
| 14 | #include "dev-setup.h" |
| 15 | #include "fd-util.h" |
| 16 | #include "fs-util.h" |
| 17 | #include "label.h" |
| 18 | #include "loop-util.h" |
| 19 | #include "loopback-setup.h" |
| 20 | #include "missing.h" |
| 21 | #include "mkdir.h" |
| 22 | #include "mount-util.h" |
| 23 | #include "namespace.h" |
| 24 | #include "path-util.h" |
| 25 | #include "selinux-util.h" |
| 26 | #include "socket-util.h" |
| 27 | #include "stat-util.h" |
| 28 | #include "string-table.h" |
| 29 | #include "string-util.h" |
| 30 | #include "strv.h" |
| 31 | #include "umask-util.h" |
| 32 | #include "user-util.h" |
| 33 | #include "util.h" |
| 34 | |
| 35 | #define DEV_MOUNT_OPTIONS(2|(1<<24)|8) (MS_NOSUID2|MS_STRICTATIME(1<<24)|MS_NOEXEC8) |
| 36 | |
| 37 | typedef enum MountMode { |
| 38 | /* This is ordered by priority! */ |
| 39 | INACCESSIBLE, |
| 40 | BIND_MOUNT, |
| 41 | BIND_MOUNT_RECURSIVE, |
| 42 | PRIVATE_TMP, |
| 43 | PRIVATE_DEV, |
| 44 | BIND_DEV, |
| 45 | EMPTY_DIR, |
| 46 | SYSFS, |
| 47 | PROCFS, |
| 48 | READONLY, |
| 49 | READWRITE, |
| 50 | TMPFS, |
| 51 | } MountMode; |
| 52 | |
| 53 | typedef struct MountEntry { |
| 54 | const char *path_const; /* Memory allocated on stack or static */ |
| 55 | MountMode mode:5; |
| 56 | bool_Bool ignore:1; /* Ignore if path does not exist? */ |
| 57 | bool_Bool has_prefix:1; /* Already is prefixed by the root dir? */ |
| 58 | bool_Bool read_only:1; /* Shall this mount point be read-only? */ |
| 59 | bool_Bool applied:1; /* Already applied */ |
| 60 | char *path_malloc; /* Use this instead of 'path_const' if we had to allocate memory */ |
| 61 | const char *source_const; /* The source path, for bind mounts */ |
| 62 | char *source_malloc; |
| 63 | const char *options_const;/* Mount options for tmpfs */ |
| 64 | char *options_malloc; |
| 65 | unsigned long flags; /* Mount flags used by EMPTY_DIR and TMPFS. Do not include MS_RDONLY here, but please use read_only. */ |
| 66 | unsigned n_followed; |
| 67 | } MountEntry; |
| 68 | |
| 69 | /* If MountAPIVFS= is used, let's mount /sys and /proc into the it, but only as a fallback if the user hasn't mounted |
| 70 | * something there already. These mounts are hence overridden by any other explicitly configured mounts. */ |
| 71 | static const MountEntry apivfs_table[] = { |
| 72 | { "/proc", PROCFS, false0 }, |
| 73 | { "/dev", BIND_DEV, false0 }, |
| 74 | { "/sys", SYSFS, false0 }, |
| 75 | }; |
| 76 | |
| 77 | /* ProtectKernelTunables= option and the related filesystem APIs */ |
| 78 | static const MountEntry protect_kernel_tunables_table[] = { |
| 79 | { "/proc/acpi", READONLY, true1 }, |
| 80 | { "/proc/apm", READONLY, true1 }, /* Obsolete API, there's no point in permitting access to this, ever */ |
| 81 | { "/proc/asound", READONLY, true1 }, |
| 82 | { "/proc/bus", READONLY, true1 }, |
| 83 | { "/proc/fs", READONLY, true1 }, |
| 84 | { "/proc/irq", READONLY, true1 }, |
| 85 | { "/proc/kallsyms", INACCESSIBLE, true1 }, |
| 86 | { "/proc/kcore", INACCESSIBLE, true1 }, |
| 87 | { "/proc/latency_stats", READONLY, true1 }, |
| 88 | { "/proc/mtrr", READONLY, true1 }, |
| 89 | { "/proc/scsi", READONLY, true1 }, |
| 90 | { "/proc/sys", READONLY, false0 }, |
| 91 | { "/proc/sysrq-trigger", READONLY, true1 }, |
| 92 | { "/proc/timer_stats", READONLY, true1 }, |
| 93 | { "/sys", READONLY, false0 }, |
| 94 | { "/sys/fs/bpf", READONLY, true1 }, |
| 95 | { "/sys/fs/cgroup", READWRITE, false0 }, /* READONLY is set by ProtectControlGroups= option */ |
| 96 | { "/sys/fs/selinux", READWRITE, true1 }, |
| 97 | { "/sys/kernel/debug", READONLY, true1 }, |
| 98 | { "/sys/kernel/tracing", READONLY, true1 }, |
| 99 | }; |
| 100 | |
| 101 | /* ProtectKernelModules= option */ |
| 102 | static const MountEntry protect_kernel_modules_table[] = { |
| 103 | #if HAVE_SPLIT_USR0 |
| 104 | { "/lib/modules", INACCESSIBLE, true1 }, |
| 105 | #endif |
| 106 | { "/usr/lib/modules", INACCESSIBLE, true1 }, |
| 107 | }; |
| 108 | |
| 109 | /* |
| 110 | * ProtectHome=read-only table, protect $HOME and $XDG_RUNTIME_DIR and rest of |
| 111 | * system should be protected by ProtectSystem= |
| 112 | */ |
| 113 | static const MountEntry protect_home_read_only_table[] = { |
| 114 | { "/home", READONLY, true1 }, |
| 115 | { "/run/user", READONLY, true1 }, |
| 116 | { "/root", READONLY, true1 }, |
| 117 | }; |
| 118 | |
| 119 | /* ProtectHome=tmpfs table */ |
| 120 | static const MountEntry protect_home_tmpfs_table[] = { |
| 121 | { "/home", TMPFS, true1, .read_only = true1, .options_const = "mode=0755", .flags = MS_NODEV4|MS_STRICTATIME(1<<24) }, |
| 122 | { "/run/user", TMPFS, true1, .read_only = true1, .options_const = "mode=0755", .flags = MS_NODEV4|MS_STRICTATIME(1<<24) }, |
| 123 | { "/root", TMPFS, true1, .read_only = true1, .options_const = "mode=0700", .flags = MS_NODEV4|MS_STRICTATIME(1<<24) }, |
| 124 | }; |
| 125 | |
| 126 | /* ProtectHome=yes table */ |
| 127 | static const MountEntry protect_home_yes_table[] = { |
| 128 | { "/home", INACCESSIBLE, true1 }, |
| 129 | { "/run/user", INACCESSIBLE, true1 }, |
| 130 | { "/root", INACCESSIBLE, true1 }, |
| 131 | }; |
| 132 | |
| 133 | /* ProtectSystem=yes table */ |
| 134 | static const MountEntry protect_system_yes_table[] = { |
| 135 | { "/usr", READONLY, false0 }, |
| 136 | { "/boot", READONLY, true1 }, |
| 137 | { "/efi", READONLY, true1 }, |
| 138 | #if HAVE_SPLIT_USR0 |
| 139 | { "/lib", READONLY, true1 }, |
| 140 | { "/lib64", READONLY, true1 }, |
| 141 | { "/bin", READONLY, true1 }, |
| 142 | # if HAVE_SPLIT_BIN1 |
| 143 | { "/sbin", READONLY, true1 }, |
| 144 | # endif |
| 145 | #endif |
| 146 | }; |
| 147 | |
| 148 | /* ProtectSystem=full includes ProtectSystem=yes */ |
| 149 | static const MountEntry protect_system_full_table[] = { |
| 150 | { "/usr", READONLY, false0 }, |
| 151 | { "/boot", READONLY, true1 }, |
| 152 | { "/efi", READONLY, true1 }, |
| 153 | { "/etc", READONLY, false0 }, |
| 154 | #if HAVE_SPLIT_USR0 |
| 155 | { "/lib", READONLY, true1 }, |
| 156 | { "/lib64", READONLY, true1 }, |
| 157 | { "/bin", READONLY, true1 }, |
| 158 | # if HAVE_SPLIT_BIN1 |
| 159 | { "/sbin", READONLY, true1 }, |
| 160 | # endif |
| 161 | #endif |
| 162 | }; |
| 163 | |
| 164 | /* |
| 165 | * ProtectSystem=strict table. In this strict mode, we mount everything |
| 166 | * read-only, except for /proc, /dev, /sys which are the kernel API VFS, |
| 167 | * which are left writable, but PrivateDevices= + ProtectKernelTunables= |
| 168 | * protect those, and these options should be fully orthogonal. |
| 169 | * (And of course /home and friends are also left writable, as ProtectHome= |
| 170 | * shall manage those, orthogonally). |
| 171 | */ |
| 172 | static const MountEntry protect_system_strict_table[] = { |
| 173 | { "/", READONLY, false0 }, |
| 174 | { "/proc", READWRITE, false0 }, /* ProtectKernelTunables= */ |
| 175 | { "/sys", READWRITE, false0 }, /* ProtectKernelTunables= */ |
| 176 | { "/dev", READWRITE, false0 }, /* PrivateDevices= */ |
| 177 | { "/home", READWRITE, true1 }, /* ProtectHome= */ |
| 178 | { "/run/user", READWRITE, true1 }, /* ProtectHome= */ |
| 179 | { "/root", READWRITE, true1 }, /* ProtectHome= */ |
| 180 | }; |
| 181 | |
| 182 | static const char *mount_entry_path(const MountEntry *p) { |
| 183 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/core/namespace.c", 183, __PRETTY_FUNCTION__ ); } while (0); |
| 184 | |
| 185 | /* Returns the path of this bind mount. If the malloc()-allocated ->path_buffer field is set we return that, |
| 186 | * otherwise the stack/static ->path field is returned. */ |
| 187 | |
| 188 | return p->path_malloc ?: p->path_const; |
| 189 | } |
| 190 | |
| 191 | static bool_Bool mount_entry_read_only(const MountEntry *p) { |
| 192 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/core/namespace.c", 192, __PRETTY_FUNCTION__ ); } while (0); |
| 193 | |
| 194 | return p->read_only || IN_SET(p->mode, READONLY, INACCESSIBLE)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){READONLY, INACCESSIBLE})/sizeof(int)]; switch (p->mode) { case READONLY: case INACCESSIBLE: _found = 1; break ; default: break; } _found; }); |
| 195 | } |
| 196 | |
| 197 | static const char *mount_entry_source(const MountEntry *p) { |
| 198 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/core/namespace.c", 198, __PRETTY_FUNCTION__ ); } while (0); |
| 199 | |
| 200 | return p->source_malloc ?: p->source_const; |
| 201 | } |
| 202 | |
| 203 | static const char *mount_entry_options(const MountEntry *p) { |
| 204 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/core/namespace.c", 204, __PRETTY_FUNCTION__ ); } while (0); |
| 205 | |
| 206 | return p->options_malloc ?: p->options_const; |
| 207 | } |
| 208 | |
| 209 | static void mount_entry_done(MountEntry *p) { |
| 210 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/core/namespace.c", 210, __PRETTY_FUNCTION__ ); } while (0); |
| 211 | |
| 212 | p->path_malloc = mfree(p->path_malloc); |
| 213 | p->source_malloc = mfree(p->source_malloc); |
| 214 | p->options_malloc = mfree(p->options_malloc); |
| 215 | } |
| 216 | |
| 217 | static int append_access_mounts(MountEntry **p, char **strv, MountMode mode, bool_Bool forcibly_require_prefix) { |
| 218 | char **i; |
| 219 | |
| 220 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/core/namespace.c", 220, __PRETTY_FUNCTION__ ); } while (0); |
| 221 | |
| 222 | /* Adds a list of user-supplied READWRITE/READONLY/INACCESSIBLE entries */ |
| 223 | |
| 224 | STRV_FOREACH(i, strv)for ((i) = (strv); (i) && *(i); (i)++) { |
| 225 | bool_Bool ignore = false0, needs_prefix = false0; |
| 226 | const char *e = *i; |
| 227 | |
| 228 | /* Look for any prefixes */ |
| 229 | if (startswith(e, "-")) { |
| 230 | e++; |
| 231 | ignore = true1; |
| 232 | } |
| 233 | if (startswith(e, "+")) { |
| 234 | e++; |
| 235 | needs_prefix = true1; |
| 236 | } |
| 237 | |
| 238 | if (!path_is_absolute(e)) |
| 239 | return -EINVAL22; |
| 240 | |
| 241 | *((*p)++) = (MountEntry) { |
| 242 | .path_const = e, |
| 243 | .mode = mode, |
| 244 | .ignore = ignore, |
| 245 | .has_prefix = !needs_prefix && !forcibly_require_prefix, |
| 246 | }; |
| 247 | } |
| 248 | |
| 249 | return 0; |
| 250 | } |
| 251 | |
| 252 | static int append_empty_dir_mounts(MountEntry **p, char **strv) { |
| 253 | char **i; |
| 254 | |
| 255 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/core/namespace.c", 255, __PRETTY_FUNCTION__ ); } while (0); |
| 256 | |
| 257 | /* Adds tmpfs mounts to provide readable but empty directories. This is primarily used to implement the |
| 258 | * "/private/" boundary directories for DynamicUser=1. */ |
| 259 | |
| 260 | STRV_FOREACH(i, strv)for ((i) = (strv); (i) && *(i); (i)++) { |
| 261 | |
| 262 | *((*p)++) = (MountEntry) { |
| 263 | .path_const = *i, |
| 264 | .mode = EMPTY_DIR, |
| 265 | .ignore = false0, |
| 266 | .has_prefix = false0, |
| 267 | .read_only = true1, |
| 268 | .options_const = "mode=755", |
| 269 | .flags = MS_NOSUID2|MS_NOEXEC8|MS_NODEV4|MS_STRICTATIME(1<<24), |
| 270 | }; |
| 271 | } |
| 272 | |
| 273 | return 0; |
| 274 | } |
| 275 | |
| 276 | static int append_bind_mounts(MountEntry **p, const BindMount *binds, size_t n) { |
| 277 | size_t i; |
| 278 | |
| 279 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/core/namespace.c", 279, __PRETTY_FUNCTION__ ); } while (0); |
| 280 | |
| 281 | for (i = 0; i < n; i++) { |
| 282 | const BindMount *b = binds + i; |
| 283 | |
| 284 | *((*p)++) = (MountEntry) { |
| 285 | .path_const = b->destination, |
| 286 | .mode = b->recursive ? BIND_MOUNT_RECURSIVE : BIND_MOUNT, |
| 287 | .read_only = b->read_only, |
| 288 | .source_const = b->source, |
| 289 | .ignore = b->ignore_enoent, |
| 290 | }; |
| 291 | } |
| 292 | |
| 293 | return 0; |
| 294 | } |
| 295 | |
| 296 | static int append_tmpfs_mounts(MountEntry **p, const TemporaryFileSystem *tmpfs, size_t n) { |
| 297 | size_t i; |
| 298 | int r; |
| 299 | |
| 300 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/core/namespace.c", 300, __PRETTY_FUNCTION__ ); } while (0); |
| 301 | |
| 302 | for (i = 0; i < n; i++) { |
| 303 | const TemporaryFileSystem *t = tmpfs + i; |
| 304 | _cleanup_free___attribute__((cleanup(freep))) char *o = NULL((void*)0), *str = NULL((void*)0); |
| 305 | unsigned long flags = MS_NODEV4|MS_STRICTATIME(1<<24); |
| 306 | bool_Bool ro = false0; |
| 307 | |
| 308 | if (!path_is_absolute(t->path)) |
| 309 | return -EINVAL22; |
| 310 | |
| 311 | if (!isempty(t->options)) { |
| 312 | str = strjoin("mode=0755,", t->options)strjoin_real(("mode=0755,"), t->options, ((void*)0)); |
| 313 | if (!str) |
| 314 | return -ENOMEM12; |
| 315 | |
| 316 | r = mount_option_mangle(str, MS_NODEV4|MS_STRICTATIME(1<<24), &flags, &o); |
| 317 | if (r < 0) |
| 318 | return r; |
| 319 | |
| 320 | ro = flags & MS_RDONLY1; |
| 321 | if (ro) |
| 322 | flags ^= MS_RDONLY1; |
| 323 | } |
| 324 | |
| 325 | *((*p)++) = (MountEntry) { |
| 326 | .path_const = t->path, |
| 327 | .mode = TMPFS, |
| 328 | .read_only = ro, |
| 329 | .options_malloc = o, |
| 330 | .flags = flags, |
| 331 | }; |
| 332 | |
| 333 | o = NULL((void*)0); |
| 334 | } |
| 335 | |
| 336 | return 0; |
| 337 | } |
| 338 | |
| 339 | static int append_static_mounts(MountEntry **p, const MountEntry *mounts, size_t n, bool_Bool ignore_protect) { |
| 340 | size_t i; |
| 341 | |
| 342 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/core/namespace.c", 342, __PRETTY_FUNCTION__ ); } while (0); |
| 343 | assert(mounts)do { if ((__builtin_expect(!!(!(mounts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("mounts"), "../src/core/namespace.c", 343 , __PRETTY_FUNCTION__); } while (0); |
| 344 | |
| 345 | /* Adds a list of static pre-defined entries */ |
| 346 | |
| 347 | for (i = 0; i < n; i++) |
| 348 | *((*p)++) = (MountEntry) { |
| 349 | .path_const = mount_entry_path(mounts+i), |
| 350 | .mode = mounts[i].mode, |
| 351 | .ignore = mounts[i].ignore || ignore_protect, |
| 352 | }; |
| 353 | |
| 354 | return 0; |
| 355 | } |
| 356 | |
| 357 | static int append_protect_home(MountEntry **p, ProtectHome protect_home, bool_Bool ignore_protect) { |
| 358 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/core/namespace.c", 358, __PRETTY_FUNCTION__ ); } while (0); |
| 359 | |
| 360 | switch (protect_home) { |
| 361 | |
| 362 | case PROTECT_HOME_NO: |
| 363 | return 0; |
| 364 | |
| 365 | case PROTECT_HOME_READ_ONLY: |
| 366 | return append_static_mounts(p, protect_home_read_only_table, ELEMENTSOF(protect_home_read_only_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_home_read_only_table), typeof(&*(protect_home_read_only_table ))), sizeof(protect_home_read_only_table)/sizeof((protect_home_read_only_table )[0]), ((void)0))), ignore_protect); |
| 367 | |
| 368 | case PROTECT_HOME_TMPFS: |
| 369 | return append_static_mounts(p, protect_home_tmpfs_table, ELEMENTSOF(protect_home_tmpfs_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_home_tmpfs_table), typeof(&*(protect_home_tmpfs_table ))), sizeof(protect_home_tmpfs_table)/sizeof((protect_home_tmpfs_table )[0]), ((void)0))), ignore_protect); |
| 370 | |
| 371 | case PROTECT_HOME_YES: |
| 372 | return append_static_mounts(p, protect_home_yes_table, ELEMENTSOF(protect_home_yes_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_home_yes_table), typeof(&*(protect_home_yes_table ))), sizeof(protect_home_yes_table)/sizeof((protect_home_yes_table )[0]), ((void)0))), ignore_protect); |
| 373 | |
| 374 | default: |
| 375 | assert_not_reached("Unexpected ProtectHome= value")do { log_assert_failed_unreachable_realm(LOG_REALM_SYSTEMD, ( "Unexpected ProtectHome= value"), "../src/core/namespace.c", 375 , __PRETTY_FUNCTION__); } while (0); |
| 376 | } |
| 377 | } |
| 378 | |
| 379 | static int append_protect_system(MountEntry **p, ProtectSystem protect_system, bool_Bool ignore_protect) { |
| 380 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/core/namespace.c", 380, __PRETTY_FUNCTION__ ); } while (0); |
| 381 | |
| 382 | switch (protect_system) { |
| 383 | |
| 384 | case PROTECT_SYSTEM_NO: |
| 385 | return 0; |
| 386 | |
| 387 | case PROTECT_SYSTEM_STRICT: |
| 388 | return append_static_mounts(p, protect_system_strict_table, ELEMENTSOF(protect_system_strict_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_system_strict_table), typeof(&*(protect_system_strict_table ))), sizeof(protect_system_strict_table)/sizeof((protect_system_strict_table )[0]), ((void)0))), ignore_protect); |
| 389 | |
| 390 | case PROTECT_SYSTEM_YES: |
| 391 | return append_static_mounts(p, protect_system_yes_table, ELEMENTSOF(protect_system_yes_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_system_yes_table), typeof(&*(protect_system_yes_table ))), sizeof(protect_system_yes_table)/sizeof((protect_system_yes_table )[0]), ((void)0))), ignore_protect); |
| 392 | |
| 393 | case PROTECT_SYSTEM_FULL: |
| 394 | return append_static_mounts(p, protect_system_full_table, ELEMENTSOF(protect_system_full_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_system_full_table), typeof(&*(protect_system_full_table ))), sizeof(protect_system_full_table)/sizeof((protect_system_full_table )[0]), ((void)0))), ignore_protect); |
| 395 | |
| 396 | default: |
| 397 | assert_not_reached("Unexpected ProtectSystem= value")do { log_assert_failed_unreachable_realm(LOG_REALM_SYSTEMD, ( "Unexpected ProtectSystem= value"), "../src/core/namespace.c" , 397, __PRETTY_FUNCTION__); } while (0); |
| 398 | } |
| 399 | } |
| 400 | |
| 401 | static int mount_path_compare(const void *a, const void *b) { |
| 402 | const MountEntry *p = a, *q = b; |
| 403 | int d; |
| 404 | |
| 405 | /* If the paths are not equal, then order prefixes first */ |
| 406 | d = path_compare(mount_entry_path(p), mount_entry_path(q)); |
| 407 | if (d != 0) |
| 408 | return d; |
| 409 | |
| 410 | /* If the paths are equal, check the mode */ |
| 411 | if (p->mode < q->mode) |
| 412 | return -1; |
| 413 | if (p->mode > q->mode) |
| 414 | return 1; |
| 415 | |
| 416 | return 0; |
| 417 | } |
| 418 | |
| 419 | static int prefix_where_needed(MountEntry *m, size_t n, const char *root_directory) { |
| 420 | size_t i; |
| 421 | |
| 422 | /* Prefixes all paths in the bind mount table with the root directory if it is specified and the entry needs |
| 423 | * that. */ |
| 424 | |
| 425 | if (!root_directory) |
| 426 | return 0; |
| 427 | |
| 428 | for (i = 0; i < n; i++) { |
| 429 | char *s; |
| 430 | |
| 431 | if (m[i].has_prefix) |
| 432 | continue; |
| 433 | |
| 434 | s = prefix_root(root_directory, mount_entry_path(m+i)); |
| 435 | if (!s) |
| 436 | return -ENOMEM12; |
| 437 | |
| 438 | free_and_replace(m[i].path_malloc, s)({ free(m[i].path_malloc); (m[i].path_malloc) = (s); (s) = (( void*)0); 0; }); |
| 439 | m[i].has_prefix = true1; |
| 440 | } |
| 441 | |
| 442 | return 0; |
| 443 | } |
| 444 | |
| 445 | static void drop_duplicates(MountEntry *m, size_t *n) { |
| 446 | MountEntry *f, *t, *previous; |
| 447 | |
| 448 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/core/namespace.c", 448, __PRETTY_FUNCTION__ ); } while (0); |
| 449 | assert(n)do { if ((__builtin_expect(!!(!(n)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("n"), "../src/core/namespace.c", 449, __PRETTY_FUNCTION__ ); } while (0); |
| 450 | |
| 451 | /* Drops duplicate entries. Expects that the array is properly ordered already. */ |
| 452 | |
| 453 | for (f = m, t = m, previous = NULL((void*)0); f < m + *n; f++) { |
| 454 | |
| 455 | /* The first one wins (which is the one with the more restrictive mode), see mount_path_compare() |
| 456 | * above. Note that we only drop duplicates that haven't been mounted yet. */ |
| 457 | if (previous && |
| 458 | path_equal(mount_entry_path(f), mount_entry_path(previous)) && |
| 459 | !f->applied && !previous->applied) { |
| 460 | log_debug("%s is duplicate.", mount_entry_path(f))({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 460, __func__, "%s is duplicate." , mount_entry_path(f)) : -abs(_e); }); |
| 461 | previous->read_only = previous->read_only || mount_entry_read_only(f); /* Propagate the read-only flag to the remaining entry */ |
| 462 | mount_entry_done(f); |
| 463 | continue; |
| 464 | } |
| 465 | |
| 466 | *t = *f; |
| 467 | previous = t; |
| 468 | t++; |
| 469 | } |
| 470 | |
| 471 | *n = t - m; |
| 472 | } |
| 473 | |
| 474 | static void drop_inaccessible(MountEntry *m, size_t *n) { |
| 475 | MountEntry *f, *t; |
| 476 | const char *clear = NULL((void*)0); |
| 477 | |
| 478 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/core/namespace.c", 478, __PRETTY_FUNCTION__ ); } while (0); |
| 479 | assert(n)do { if ((__builtin_expect(!!(!(n)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("n"), "../src/core/namespace.c", 479, __PRETTY_FUNCTION__ ); } while (0); |
| 480 | |
| 481 | /* Drops all entries obstructed by another entry further up the tree. Expects that the array is properly |
| 482 | * ordered already. */ |
| 483 | |
| 484 | for (f = m, t = m; f < m + *n; f++) { |
| 485 | |
| 486 | /* If we found a path set for INACCESSIBLE earlier, and this entry has it as prefix we should drop |
| 487 | * it, as inaccessible paths really should drop the entire subtree. */ |
| 488 | if (clear && path_startswith(mount_entry_path(f), clear)) { |
| 489 | log_debug("%s is masked by %s.", mount_entry_path(f), clear)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 489, __func__, "%s is masked by %s." , mount_entry_path(f), clear) : -abs(_e); }); |
| 490 | mount_entry_done(f); |
| 491 | continue; |
| 492 | } |
| 493 | |
| 494 | clear = f->mode == INACCESSIBLE ? mount_entry_path(f) : NULL((void*)0); |
| 495 | |
| 496 | *t = *f; |
| 497 | t++; |
| 498 | } |
| 499 | |
| 500 | *n = t - m; |
| 501 | } |
| 502 | |
| 503 | static void drop_nop(MountEntry *m, size_t *n) { |
| 504 | MountEntry *f, *t; |
| 505 | |
| 506 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/core/namespace.c", 506, __PRETTY_FUNCTION__ ); } while (0); |
| 507 | assert(n)do { if ((__builtin_expect(!!(!(n)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("n"), "../src/core/namespace.c", 507, __PRETTY_FUNCTION__ ); } while (0); |
| 508 | |
| 509 | /* Drops all entries which have an immediate parent that has the same type, as they are redundant. Assumes the |
| 510 | * list is ordered by prefixes. */ |
| 511 | |
| 512 | for (f = m, t = m; f < m + *n; f++) { |
| 513 | |
| 514 | /* Only suppress such subtrees for READONLY and READWRITE entries */ |
| 515 | if (IN_SET(f->mode, READONLY, READWRITE)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){READONLY, READWRITE})/sizeof(int)]; switch (f->mode) { case READONLY: case READWRITE: _found = 1; break ; default: break; } _found; })) { |
| 516 | MountEntry *p; |
| 517 | bool_Bool found = false0; |
| 518 | |
| 519 | /* Now let's find the first parent of the entry we are looking at. */ |
| 520 | for (p = t-1; p >= m; p--) { |
| 521 | if (path_startswith(mount_entry_path(f), mount_entry_path(p))) { |
| 522 | found = true1; |
| 523 | break; |
| 524 | } |
| 525 | } |
| 526 | |
| 527 | /* We found it, let's see if it's the same mode, if so, we can drop this entry */ |
| 528 | if (found && p->mode == f->mode) { |
| 529 | log_debug("%s is redundant by %s", mount_entry_path(f), mount_entry_path(p))({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 529, __func__, "%s is redundant by %s" , mount_entry_path(f), mount_entry_path(p)) : -abs(_e); }); |
| 530 | mount_entry_done(f); |
| 531 | continue; |
| 532 | } |
| 533 | } |
| 534 | |
| 535 | *t = *f; |
| 536 | t++; |
| 537 | } |
| 538 | |
| 539 | *n = t - m; |
| 540 | } |
| 541 | |
| 542 | static void drop_outside_root(const char *root_directory, MountEntry *m, size_t *n) { |
| 543 | MountEntry *f, *t; |
| 544 | |
| 545 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/core/namespace.c", 545, __PRETTY_FUNCTION__ ); } while (0); |
| 546 | assert(n)do { if ((__builtin_expect(!!(!(n)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("n"), "../src/core/namespace.c", 546, __PRETTY_FUNCTION__ ); } while (0); |
| 547 | |
| 548 | /* Nothing to do */ |
| 549 | if (!root_directory) |
| 550 | return; |
| 551 | |
| 552 | /* Drops all mounts that are outside of the root directory. */ |
| 553 | |
| 554 | for (f = m, t = m; f < m + *n; f++) { |
| 555 | |
| 556 | if (!path_startswith(mount_entry_path(f), root_directory)) { |
| 557 | log_debug("%s is outside of root directory.", mount_entry_path(f))({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 557, __func__, "%s is outside of root directory." , mount_entry_path(f)) : -abs(_e); }); |
| 558 | mount_entry_done(f); |
| 559 | continue; |
| 560 | } |
| 561 | |
| 562 | *t = *f; |
| 563 | t++; |
| 564 | } |
| 565 | |
| 566 | *n = t - m; |
| 567 | } |
| 568 | |
| 569 | static int clone_device_node(const char *d, const char *temporary_mount, bool_Bool *make_devnode) { |
| 570 | const char *dn; |
| 571 | struct stat st; |
| 572 | int r; |
| 573 | |
| 574 | if (stat(d, &st) < 0) { |
| 575 | if (errno(*__errno_location ()) == ENOENT2) |
| 576 | return -ENXIO6; |
| 577 | return -errno(*__errno_location ()); |
| 578 | } |
| 579 | |
| 580 | if (!S_ISBLK(st.st_mode)((((st.st_mode)) & 0170000) == (0060000)) && |
| 581 | !S_ISCHR(st.st_mode)((((st.st_mode)) & 0170000) == (0020000))) |
| 582 | return -EINVAL22; |
| 583 | |
| 584 | if (st.st_rdev == 0) |
| 585 | return -ENXIO6; |
| 586 | |
| 587 | dn = strjoina(temporary_mount, d)({ const char *_appendees_[] = { temporary_mount, d }; char * _d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); |
| 588 | |
| 589 | if (*make_devnode) { |
| 590 | mac_selinux_create_file_prepare(d, st.st_mode); |
| 591 | r = mknod(dn, st.st_mode, st.st_rdev); |
| 592 | mac_selinux_create_file_clear(); |
| 593 | |
| 594 | if (r == 0) |
| 595 | return 0; |
| 596 | if (errno(*__errno_location ()) != EPERM1) |
| 597 | return log_debug_errno(errno, "mknod failed for %s: %m", d)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/core/namespace.c", 597, __func__ , "mknod failed for %s: %m", d) : -abs(_e); }); |
| 598 | |
| 599 | *make_devnode = false0; |
| 600 | } |
| 601 | |
| 602 | /* We're about to fallback to bind-mounting the device |
| 603 | * node. So create a dummy bind-mount target. */ |
| 604 | mac_selinux_create_file_prepare(d, 0); |
| 605 | r = mknod(dn, S_IFREG0100000, 0); |
| 606 | mac_selinux_create_file_clear(); |
| 607 | |
| 608 | if (r < 0 && errno(*__errno_location ()) != EEXIST17) |
| 609 | return log_debug_errno(errno, "mknod fallback failed for %s: %m", d)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/core/namespace.c", 609, __func__ , "mknod fallback failed for %s: %m", d) : -abs(_e); }); |
| 610 | |
| 611 | /* Fallback to bind-mounting: |
| 612 | * The assumption here is that all used device nodes carry standard |
| 613 | * properties. Specifically, the devices nodes we bind-mount should |
| 614 | * either be owned by root:root or root:tty (e.g. /dev/tty, /dev/ptmx) |
| 615 | * and should not carry ACLs. */ |
| 616 | if (mount(d, dn, NULL((void*)0), MS_BIND4096, NULL((void*)0)) < 0) |
| 617 | return log_debug_errno(errno, "mount failed for %s: %m", d)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/core/namespace.c", 617, __func__ , "mount failed for %s: %m", d) : -abs(_e); }); |
| 618 | |
| 619 | return 0; |
| 620 | } |
| 621 | |
| 622 | static int mount_private_dev(MountEntry *m) { |
| 623 | static const char devnodes[] = |
| 624 | "/dev/null\0" |
| 625 | "/dev/zero\0" |
| 626 | "/dev/full\0" |
| 627 | "/dev/random\0" |
| 628 | "/dev/urandom\0" |
| 629 | "/dev/tty\0"; |
| 630 | |
| 631 | char temporary_mount[] = "/tmp/namespace-dev-XXXXXX"; |
| 632 | const char *d, *dev = NULL((void*)0), *devpts = NULL((void*)0), *devshm = NULL((void*)0), *devhugepages = NULL((void*)0), *devmqueue = NULL((void*)0), *devlog = NULL((void*)0), *devptmx = NULL((void*)0); |
| 633 | bool_Bool can_mknod = true1; |
| 634 | _cleanup_umask___attribute__((cleanup(umaskp))) mode_t u; |
| 635 | int r; |
| 636 | |
| 637 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/core/namespace.c", 637, __PRETTY_FUNCTION__ ); } while (0); |
| 638 | |
| 639 | u = umask(0000); |
Value stored to 'u' is never read | |
| 640 | |
| 641 | if (!mkdtemp(temporary_mount)) |
| 642 | return -errno(*__errno_location ()); |
| 643 | |
| 644 | dev = strjoina(temporary_mount, "/dev")({ const char *_appendees_[] = { temporary_mount, "/dev" }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); |
| 645 | (void) mkdir(dev, 0755); |
| 646 | if (mount("tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS(2|(1<<24)|8), "mode=755") < 0) { |
| 647 | r = -errno(*__errno_location ()); |
| 648 | goto fail; |
| 649 | } |
| 650 | |
| 651 | devpts = strjoina(temporary_mount, "/dev/pts")({ const char *_appendees_[] = { temporary_mount, "/dev/pts" } ; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0 ; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); |
| 652 | (void) mkdir(devpts, 0755); |
| 653 | if (mount("/dev/pts", devpts, NULL((void*)0), MS_BIND4096, NULL((void*)0)) < 0) { |
| 654 | r = -errno(*__errno_location ()); |
| 655 | goto fail; |
| 656 | } |
| 657 | |
| 658 | /* /dev/ptmx can either be a device node or a symlink to /dev/pts/ptmx |
| 659 | * when /dev/ptmx a device node, /dev/pts/ptmx has 000 permissions making it inaccessible |
| 660 | * thus, in that case make a clone |
| 661 | * |
| 662 | * in nspawn and other containers it will be a symlink, in that case make it a symlink |
| 663 | */ |
| 664 | r = is_symlink("/dev/ptmx"); |
| 665 | if (r < 0) |
| 666 | goto fail; |
| 667 | if (r > 0) { |
| 668 | devptmx = strjoina(temporary_mount, "/dev/ptmx")({ const char *_appendees_[] = { temporary_mount, "/dev/ptmx" }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); |
| 669 | if (symlink("pts/ptmx", devptmx) < 0) { |
| 670 | r = -errno(*__errno_location ()); |
| 671 | goto fail; |
| 672 | } |
| 673 | } else { |
| 674 | r = clone_device_node("/dev/ptmx", temporary_mount, &can_mknod); |
| 675 | if (r < 0) |
| 676 | goto fail; |
| 677 | } |
| 678 | |
| 679 | devshm = strjoina(temporary_mount, "/dev/shm")({ const char *_appendees_[] = { temporary_mount, "/dev/shm" } ; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0 ; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); |
| 680 | (void) mkdir(devshm, 0755); |
| 681 | r = mount("/dev/shm", devshm, NULL((void*)0), MS_BIND4096, NULL((void*)0)); |
| 682 | if (r < 0) { |
| 683 | r = -errno(*__errno_location ()); |
| 684 | goto fail; |
| 685 | } |
| 686 | |
| 687 | devmqueue = strjoina(temporary_mount, "/dev/mqueue")({ const char *_appendees_[] = { temporary_mount, "/dev/mqueue" }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); |
| 688 | (void) mkdir(devmqueue, 0755); |
| 689 | (void) mount("/dev/mqueue", devmqueue, NULL((void*)0), MS_BIND4096, NULL((void*)0)); |
| 690 | |
| 691 | devhugepages = strjoina(temporary_mount, "/dev/hugepages")({ const char *_appendees_[] = { temporary_mount, "/dev/hugepages" }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); |
| 692 | (void) mkdir(devhugepages, 0755); |
| 693 | (void) mount("/dev/hugepages", devhugepages, NULL((void*)0), MS_BIND4096, NULL((void*)0)); |
| 694 | |
| 695 | devlog = strjoina(temporary_mount, "/dev/log")({ const char *_appendees_[] = { temporary_mount, "/dev/log" } ; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0 ; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); |
| 696 | (void) symlink("/run/systemd/journal/dev-log", devlog); |
| 697 | |
| 698 | NULSTR_FOREACH(d, devnodes)for ((d) = (devnodes); (d) && *(d); (d) = strchr((d), 0)+1) { |
| 699 | r = clone_device_node(d, temporary_mount, &can_mknod); |
| 700 | /* ENXIO means the the *source* is not a device file, skip creation in that case */ |
| 701 | if (r < 0 && r != -ENXIO6) |
| 702 | goto fail; |
| 703 | } |
| 704 | |
| 705 | dev_setup(temporary_mount, UID_INVALID((uid_t) -1), GID_INVALID((gid_t) -1)); |
| 706 | |
| 707 | /* Create the /dev directory if missing. It is more likely to be |
| 708 | * missing when the service is started with RootDirectory. This is |
| 709 | * consistent with mount units creating the mount points when missing. |
| 710 | */ |
| 711 | (void) mkdir_p_label(mount_entry_path(m), 0755); |
| 712 | |
| 713 | /* Unmount everything in old /dev */ |
| 714 | umount_recursive(mount_entry_path(m), 0); |
| 715 | if (mount(dev, mount_entry_path(m), NULL((void*)0), MS_MOVE8192, NULL((void*)0)) < 0) { |
| 716 | r = -errno(*__errno_location ()); |
| 717 | goto fail; |
| 718 | } |
| 719 | |
| 720 | rmdir(dev); |
| 721 | rmdir(temporary_mount); |
| 722 | |
| 723 | return 0; |
| 724 | |
| 725 | fail: |
| 726 | if (devpts) |
| 727 | umount(devpts); |
| 728 | |
| 729 | if (devshm) |
| 730 | umount(devshm); |
| 731 | |
| 732 | if (devhugepages) |
| 733 | umount(devhugepages); |
| 734 | |
| 735 | if (devmqueue) |
| 736 | umount(devmqueue); |
| 737 | |
| 738 | umount(dev); |
| 739 | rmdir(dev); |
| 740 | rmdir(temporary_mount); |
| 741 | |
| 742 | return r; |
| 743 | } |
| 744 | |
| 745 | static int mount_bind_dev(const MountEntry *m) { |
| 746 | int r; |
| 747 | |
| 748 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/core/namespace.c", 748, __PRETTY_FUNCTION__ ); } while (0); |
| 749 | |
| 750 | /* Implements the little brother of mount_private_dev(): simply bind mounts the host's /dev into the service's |
| 751 | * /dev. This is only used when RootDirectory= is set. */ |
| 752 | |
| 753 | (void) mkdir_p_label(mount_entry_path(m), 0755); |
| 754 | |
| 755 | r = path_is_mount_point(mount_entry_path(m), NULL((void*)0), 0); |
| 756 | if (r < 0) |
| 757 | return log_debug_errno(r, "Unable to determine whether /dev is already mounted: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 757, __func__, "Unable to determine whether /dev is already mounted: %m" ) : -abs(_e); }); |
| 758 | if (r > 0) /* make this a NOP if /dev is already a mount point */ |
| 759 | return 0; |
| 760 | |
| 761 | if (mount("/dev", mount_entry_path(m), NULL((void*)0), MS_BIND4096|MS_REC16384, NULL((void*)0)) < 0) |
| 762 | return log_debug_errno(errno, "Failed to bind mount %s: %m", mount_entry_path(m))({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/core/namespace.c", 762, __func__ , "Failed to bind mount %s: %m", mount_entry_path(m)) : -abs( _e); }); |
| 763 | |
| 764 | return 1; |
| 765 | } |
| 766 | |
| 767 | static int mount_sysfs(const MountEntry *m) { |
| 768 | int r; |
| 769 | |
| 770 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/core/namespace.c", 770, __PRETTY_FUNCTION__ ); } while (0); |
| 771 | |
| 772 | (void) mkdir_p_label(mount_entry_path(m), 0755); |
| 773 | |
| 774 | r = path_is_mount_point(mount_entry_path(m), NULL((void*)0), 0); |
| 775 | if (r < 0) |
| 776 | return log_debug_errno(r, "Unable to determine whether /sys is already mounted: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 776, __func__, "Unable to determine whether /sys is already mounted: %m" ) : -abs(_e); }); |
| 777 | if (r > 0) /* make this a NOP if /sys is already a mount point */ |
| 778 | return 0; |
| 779 | |
| 780 | /* Bind mount the host's version so that we get all child mounts of it, too. */ |
| 781 | if (mount("/sys", mount_entry_path(m), NULL((void*)0), MS_BIND4096|MS_REC16384, NULL((void*)0)) < 0) |
| 782 | return log_debug_errno(errno, "Failed to mount %s: %m", mount_entry_path(m))({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/core/namespace.c", 782, __func__ , "Failed to mount %s: %m", mount_entry_path(m)) : -abs(_e); } ); |
| 783 | |
| 784 | return 1; |
| 785 | } |
| 786 | |
| 787 | static int mount_procfs(const MountEntry *m) { |
| 788 | int r; |
| 789 | |
| 790 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/core/namespace.c", 790, __PRETTY_FUNCTION__ ); } while (0); |
| 791 | |
| 792 | (void) mkdir_p_label(mount_entry_path(m), 0755); |
| 793 | |
| 794 | r = path_is_mount_point(mount_entry_path(m), NULL((void*)0), 0); |
| 795 | if (r < 0) |
| 796 | return log_debug_errno(r, "Unable to determine whether /proc is already mounted: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 796, __func__, "Unable to determine whether /proc is already mounted: %m" ) : -abs(_e); }); |
| 797 | if (r > 0) /* make this a NOP if /proc is already a mount point */ |
| 798 | return 0; |
| 799 | |
| 800 | /* Mount a new instance, so that we get the one that matches our user namespace, if we are running in one */ |
| 801 | if (mount("proc", mount_entry_path(m), "proc", MS_NOSUID2|MS_NOEXEC8|MS_NODEV4, NULL((void*)0)) < 0) |
| 802 | return log_debug_errno(errno, "Failed to mount %s: %m", mount_entry_path(m))({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/core/namespace.c", 802, __func__ , "Failed to mount %s: %m", mount_entry_path(m)) : -abs(_e); } ); |
| 803 | |
| 804 | return 1; |
| 805 | } |
| 806 | |
| 807 | static int mount_tmpfs(const MountEntry *m) { |
| 808 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/core/namespace.c", 808, __PRETTY_FUNCTION__ ); } while (0); |
| 809 | |
| 810 | /* First, get rid of everything that is below if there is anything. Then, overmount with our new tmpfs */ |
| 811 | |
| 812 | (void) mkdir_p_label(mount_entry_path(m), 0755); |
| 813 | (void) umount_recursive(mount_entry_path(m), 0); |
| 814 | |
| 815 | if (mount("tmpfs", mount_entry_path(m), "tmpfs", m->flags, mount_entry_options(m)) < 0) |
| 816 | return log_debug_errno(errno, "Failed to mount %s: %m", mount_entry_path(m))({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/core/namespace.c", 816, __func__ , "Failed to mount %s: %m", mount_entry_path(m)) : -abs(_e); } ); |
| 817 | |
| 818 | return 1; |
| 819 | } |
| 820 | |
| 821 | static int follow_symlink( |
| 822 | const char *root_directory, |
| 823 | MountEntry *m) { |
| 824 | |
| 825 | _cleanup_free___attribute__((cleanup(freep))) char *target = NULL((void*)0); |
| 826 | int r; |
| 827 | |
| 828 | /* Let's chase symlinks, but only one step at a time. That's because depending where the symlink points we |
| 829 | * might need to change the order in which we mount stuff. Hence: let's normalize piecemeal, and do one step at |
| 830 | * a time by specifying CHASE_STEP. This function returns 0 if we resolved one step, and > 0 if we reached the |
| 831 | * end and already have a fully normalized name. */ |
| 832 | |
| 833 | r = chase_symlinks(mount_entry_path(m), root_directory, CHASE_STEP|CHASE_NONEXISTENT, &target); |
| 834 | if (r < 0) |
| 835 | return log_debug_errno(r, "Failed to chase symlinks '%s': %m", mount_entry_path(m))({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 835, __func__, "Failed to chase symlinks '%s': %m" , mount_entry_path(m)) : -abs(_e); }); |
| 836 | if (r > 0) /* Reached the end, nothing more to resolve */ |
| 837 | return 1; |
| 838 | |
| 839 | if (m->n_followed >= CHASE_SYMLINKS_MAX32) { /* put a boundary on things */ |
| 840 | log_debug("Symlink loop on '%s'.", mount_entry_path(m))({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 840, __func__, "Symlink loop on '%s'." , mount_entry_path(m)) : -abs(_e); }); |
| 841 | return -ELOOP40; |
| 842 | } |
| 843 | |
| 844 | log_debug("Followed mount entry path symlink %s → %s.", mount_entry_path(m), target)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 844, __func__, "Followed mount entry path symlink %s → %s." , mount_entry_path(m), target) : -abs(_e); }); |
| 845 | |
| 846 | free_and_replace(m->path_malloc, target)({ free(m->path_malloc); (m->path_malloc) = (target); ( target) = ((void*)0); 0; }); |
| 847 | m->has_prefix = true1; |
| 848 | |
| 849 | m->n_followed ++; |
| 850 | |
| 851 | return 0; |
| 852 | } |
| 853 | |
| 854 | static int apply_mount( |
| 855 | const char *root_directory, |
| 856 | MountEntry *m) { |
| 857 | |
| 858 | bool_Bool rbind = true1, make = false0; |
| 859 | const char *what; |
| 860 | int r; |
| 861 | |
| 862 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/core/namespace.c", 862, __PRETTY_FUNCTION__ ); } while (0); |
| 863 | |
| 864 | log_debug("Applying namespace mount on %s", mount_entry_path(m))({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 864, __func__, "Applying namespace mount on %s" , mount_entry_path(m)) : -abs(_e); }); |
| 865 | |
| 866 | switch (m->mode) { |
| 867 | |
| 868 | case INACCESSIBLE: { |
| 869 | struct stat target; |
| 870 | |
| 871 | /* First, get rid of everything that is below if there |
| 872 | * is anything... Then, overmount it with an |
| 873 | * inaccessible path. */ |
| 874 | (void) umount_recursive(mount_entry_path(m), 0); |
| 875 | |
| 876 | if (lstat(mount_entry_path(m), &target) < 0) { |
| 877 | if (errno(*__errno_location ()) == ENOENT2 && m->ignore) |
| 878 | return 0; |
| 879 | |
| 880 | return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m", mount_entry_path(m))({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/core/namespace.c", 880, __func__ , "Failed to lstat() %s to determine what to mount over it: %m" , mount_entry_path(m)) : -abs(_e); }); |
| 881 | } |
| 882 | |
| 883 | what = mode_to_inaccessible_node(target.st_mode); |
| 884 | if (!what) { |
| 885 | log_debug("File type not supported for inaccessible mounts. Note that symlinks are not allowed")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 885, __func__, "File type not supported for inaccessible mounts. Note that symlinks are not allowed" ) : -abs(_e); }); |
| 886 | return -ELOOP40; |
| 887 | } |
| 888 | break; |
| 889 | } |
| 890 | |
| 891 | case READONLY: |
| 892 | case READWRITE: |
| 893 | r = path_is_mount_point(mount_entry_path(m), root_directory, 0); |
| 894 | if (r == -ENOENT2 && m->ignore) |
| 895 | return 0; |
| 896 | if (r < 0) |
| 897 | return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", mount_entry_path(m))({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 897, __func__, "Failed to determine whether %s is already a mount point: %m" , mount_entry_path(m)) : -abs(_e); }); |
| 898 | 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. */ |
| 899 | return 0; |
| 900 | /* This isn't a mount point yet, let's make it one. */ |
| 901 | what = mount_entry_path(m); |
| 902 | break; |
| 903 | |
| 904 | case BIND_MOUNT: |
| 905 | rbind = false0; |
| 906 | |
| 907 | _fallthrough_; |
| 908 | case BIND_MOUNT_RECURSIVE: { |
| 909 | _cleanup_free___attribute__((cleanup(freep))) char *chased = NULL((void*)0); |
| 910 | |
| 911 | /* Since mount() will always follow symlinks we chase the symlinks on our own first. Note that bind |
| 912 | * mount source paths are always relative to the host root, hence we pass NULL as root directory to |
| 913 | * chase_symlinks() here. */ |
| 914 | |
| 915 | r = chase_symlinks(mount_entry_source(m), NULL((void*)0), CHASE_TRAIL_SLASH, &chased); |
| 916 | if (r == -ENOENT2 && m->ignore) { |
| 917 | log_debug_errno(r, "Path %s does not exist, ignoring.", mount_entry_source(m))({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 917, __func__, "Path %s does not exist, ignoring." , mount_entry_source(m)) : -abs(_e); }); |
| 918 | return 0; |
| 919 | } |
| 920 | if (r < 0) |
| 921 | return log_debug_errno(r, "Failed to follow symlinks on %s: %m", mount_entry_source(m))({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 921, __func__, "Failed to follow symlinks on %s: %m" , mount_entry_source(m)) : -abs(_e); }); |
| 922 | |
| 923 | log_debug("Followed source symlinks %s → %s.", mount_entry_source(m), chased)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 923, __func__, "Followed source symlinks %s → %s." , mount_entry_source(m), chased) : -abs(_e); }); |
| 924 | |
| 925 | free_and_replace(m->source_malloc, chased)({ free(m->source_malloc); (m->source_malloc) = (chased ); (chased) = ((void*)0); 0; }); |
| 926 | |
| 927 | what = mount_entry_source(m); |
| 928 | make = true1; |
| 929 | break; |
| 930 | } |
| 931 | |
| 932 | case EMPTY_DIR: |
| 933 | case TMPFS: |
| 934 | return mount_tmpfs(m); |
| 935 | |
| 936 | case PRIVATE_TMP: |
| 937 | what = mount_entry_source(m); |
| 938 | make = true1; |
| 939 | break; |
| 940 | |
| 941 | case PRIVATE_DEV: |
| 942 | return mount_private_dev(m); |
| 943 | |
| 944 | case BIND_DEV: |
| 945 | return mount_bind_dev(m); |
| 946 | |
| 947 | case SYSFS: |
| 948 | return mount_sysfs(m); |
| 949 | |
| 950 | case PROCFS: |
| 951 | return mount_procfs(m); |
| 952 | |
| 953 | default: |
| 954 | assert_not_reached("Unknown mode")do { log_assert_failed_unreachable_realm(LOG_REALM_SYSTEMD, ( "Unknown mode"), "../src/core/namespace.c", 954, __PRETTY_FUNCTION__ ); } while (0); |
| 955 | } |
| 956 | |
| 957 | assert(what)do { if ((__builtin_expect(!!(!(what)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("what"), "../src/core/namespace.c", 957, __PRETTY_FUNCTION__); } while (0); |
| 958 | |
| 959 | if (mount(what, mount_entry_path(m), NULL((void*)0), MS_BIND4096|(rbind ? MS_REC16384 : 0), NULL((void*)0)) < 0) { |
| 960 | bool_Bool try_again = false0; |
| 961 | r = -errno(*__errno_location ()); |
| 962 | |
| 963 | if (r == -ENOENT2 && make) { |
| 964 | struct stat st; |
| 965 | |
| 966 | /* Hmm, either the source or the destination are missing. Let's see if we can create the destination, then try again */ |
| 967 | |
| 968 | if (stat(what, &st) < 0) |
| 969 | log_debug_errno(errno, "Mount point source '%s' is not accessible: %m", what)({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/core/namespace.c", 969, __func__ , "Mount point source '%s' is not accessible: %m", what) : -abs (_e); }); |
| 970 | else { |
| 971 | int q; |
| 972 | |
| 973 | (void) mkdir_parents(mount_entry_path(m), 0755); |
| 974 | |
| 975 | if (S_ISDIR(st.st_mode)((((st.st_mode)) & 0170000) == (0040000))) |
| 976 | q = mkdir(mount_entry_path(m), 0755) < 0 ? -errno(*__errno_location ()) : 0; |
| 977 | else |
| 978 | q = touch(mount_entry_path(m)); |
| 979 | |
| 980 | if (q < 0) |
| 981 | log_debug_errno(q, "Failed to create destination mount point node '%s': %m", mount_entry_path(m))({ int _level = ((7)), _e = ((q)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 981, __func__, "Failed to create destination mount point node '%s': %m" , mount_entry_path(m)) : -abs(_e); }); |
| 982 | else |
| 983 | try_again = true1; |
| 984 | } |
| 985 | } |
| 986 | |
| 987 | if (try_again) { |
| 988 | if (mount(what, mount_entry_path(m), NULL((void*)0), MS_BIND4096|(rbind ? MS_REC16384 : 0), NULL((void*)0)) < 0) |
| 989 | r = -errno(*__errno_location ()); |
| 990 | else |
| 991 | r = 0; |
| 992 | } |
| 993 | |
| 994 | if (r < 0) |
| 995 | return log_debug_errno(r, "Failed to mount %s to %s: %m", what, mount_entry_path(m))({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 995, __func__, "Failed to mount %s to %s: %m" , what, mount_entry_path(m)) : -abs(_e); }); |
| 996 | } |
| 997 | |
| 998 | log_debug("Successfully mounted %s to %s", what, mount_entry_path(m))({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/core/namespace.c", 998, __func__, "Successfully mounted %s to %s" , what, mount_entry_path(m)) : -abs(_e); }); |
| 999 | return 0; |
| 1000 | } |
| 1001 | |
| 1002 | static int make_read_only(const MountEntry *m, char **blacklist, FILE *proc_self_mountinfo) { |
| 1003 | int r = 0; |
| 1004 | |
| 1005 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/core/namespace.c", 1005, __PRETTY_FUNCTION__ ); } while (0); |
| 1006 | assert(proc_self_mountinfo)do { if ((__builtin_expect(!!(!(proc_self_mountinfo)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("proc_self_mountinfo"), "../src/core/namespace.c" , 1006, __PRETTY_FUNCTION__); } while (0); |
| 1007 | |
| 1008 | if (mount_entry_read_only(m)) { |
| 1009 | if (IN_SET(m->mode, EMPTY_DIR, TMPFS)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){EMPTY_DIR, TMPFS})/sizeof(int)]; switch( m->mode) { case EMPTY_DIR: case TMPFS: _found = 1; break; default : break; } _found; })) { |
| 1010 | /* Make superblock readonly */ |
| 1011 | if (mount(NULL((void*)0), mount_entry_path(m), NULL((void*)0), MS_REMOUNT32 | MS_RDONLY1 | m->flags, mount_entry_options(m)) < 0) |
| 1012 | r = -errno(*__errno_location ()); |
| 1013 | } else |
| 1014 | r = bind_remount_recursive_with_mountinfo(mount_entry_path(m), true1, blacklist, proc_self_mountinfo); |
| 1015 | } else if (m->mode == PRIVATE_DEV) { |
| 1016 | /* Superblock can be readonly but the submounts can't */ |
| 1017 | if (mount(NULL((void*)0), mount_entry_path(m), NULL((void*)0), MS_REMOUNT32|DEV_MOUNT_OPTIONS(2|(1<<24)|8)|MS_RDONLY1, NULL((void*)0)) < 0) |
| 1018 | r = -errno(*__errno_location ()); |
| 1019 | } else |
| 1020 | return 0; |
| 1021 | |
| 1022 | /* Not that we only turn on the MS_RDONLY flag here, we never turn it off. Something that was marked read-only |
| 1023 | * already stays this way. This improves compatibility with container managers, where we won't attempt to undo |
| 1024 | * read-only mounts already applied. */ |
| 1025 | |
| 1026 | if (r == -ENOENT2 && m->ignore) |
| 1027 | r = 0; |
| 1028 | |
| 1029 | return r; |
| 1030 | } |
| 1031 | |
| 1032 | static bool_Bool namespace_info_mount_apivfs(const char *root_directory, const NamespaceInfo *ns_info) { |
| 1033 | assert(ns_info)do { if ((__builtin_expect(!!(!(ns_info)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ns_info"), "../src/core/namespace.c", 1033 , __PRETTY_FUNCTION__); } while (0); |
| 1034 | |
| 1035 | /* |
| 1036 | * ProtectControlGroups= and ProtectKernelTunables= imply MountAPIVFS=, |
| 1037 | * since to protect the API VFS mounts, they need to be around in the |
| 1038 | * first place... and RootDirectory= or RootImage= need to be set. |
| 1039 | */ |
| 1040 | |
| 1041 | /* root_directory should point to a mount point */ |
| 1042 | return root_directory && |
| 1043 | (ns_info->mount_apivfs || |
| 1044 | ns_info->protect_control_groups || |
| 1045 | ns_info->protect_kernel_tunables); |
| 1046 | } |
| 1047 | |
| 1048 | static size_t namespace_calculate_mounts( |
| 1049 | const char* root_directory, |
| 1050 | const NamespaceInfo *ns_info, |
| 1051 | char** read_write_paths, |
| 1052 | char** read_only_paths, |
| 1053 | char** inaccessible_paths, |
| 1054 | char** empty_directories, |
| 1055 | size_t n_bind_mounts, |
| 1056 | size_t n_temporary_filesystems, |
| 1057 | const char* tmp_dir, |
| 1058 | const char* var_tmp_dir, |
| 1059 | ProtectHome protect_home, |
| 1060 | ProtectSystem protect_system) { |
| 1061 | |
| 1062 | size_t protect_home_cnt; |
| 1063 | size_t protect_system_cnt = |
| 1064 | (protect_system == PROTECT_SYSTEM_STRICT ? |
| 1065 | ELEMENTSOF(protect_system_strict_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_system_strict_table), typeof(&*(protect_system_strict_table ))), sizeof(protect_system_strict_table)/sizeof((protect_system_strict_table )[0]), ((void)0))) : |
| 1066 | ((protect_system == PROTECT_SYSTEM_FULL) ? |
| 1067 | ELEMENTSOF(protect_system_full_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_system_full_table), typeof(&*(protect_system_full_table ))), sizeof(protect_system_full_table)/sizeof((protect_system_full_table )[0]), ((void)0))) : |
| 1068 | ((protect_system == PROTECT_SYSTEM_YES) ? |
| 1069 | ELEMENTSOF(protect_system_yes_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_system_yes_table), typeof(&*(protect_system_yes_table ))), sizeof(protect_system_yes_table)/sizeof((protect_system_yes_table )[0]), ((void)0))) : 0))); |
| 1070 | |
| 1071 | protect_home_cnt = |
| 1072 | (protect_home == PROTECT_HOME_YES ? |
| 1073 | ELEMENTSOF(protect_home_yes_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_home_yes_table), typeof(&*(protect_home_yes_table ))), sizeof(protect_home_yes_table)/sizeof((protect_home_yes_table )[0]), ((void)0))) : |
| 1074 | ((protect_home == PROTECT_HOME_READ_ONLY) ? |
| 1075 | ELEMENTSOF(protect_home_read_only_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_home_read_only_table), typeof(&*(protect_home_read_only_table ))), sizeof(protect_home_read_only_table)/sizeof((protect_home_read_only_table )[0]), ((void)0))) : |
| 1076 | ((protect_home == PROTECT_HOME_TMPFS) ? |
| 1077 | ELEMENTSOF(protect_home_tmpfs_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_home_tmpfs_table), typeof(&*(protect_home_tmpfs_table ))), sizeof(protect_home_tmpfs_table)/sizeof((protect_home_tmpfs_table )[0]), ((void)0))) : 0))); |
| 1078 | |
| 1079 | return !!tmp_dir + !!var_tmp_dir + |
| 1080 | strv_length(read_write_paths) + |
| 1081 | strv_length(read_only_paths) + |
| 1082 | strv_length(inaccessible_paths) + |
| 1083 | strv_length(empty_directories) + |
| 1084 | n_bind_mounts + |
| 1085 | n_temporary_filesystems + |
| 1086 | ns_info->private_dev + |
| 1087 | (ns_info->protect_kernel_tunables ? ELEMENTSOF(protect_kernel_tunables_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_kernel_tunables_table), typeof(&*(protect_kernel_tunables_table ))), sizeof(protect_kernel_tunables_table)/sizeof((protect_kernel_tunables_table )[0]), ((void)0))) : 0) + |
| 1088 | (ns_info->protect_control_groups ? 1 : 0) + |
| 1089 | (ns_info->protect_kernel_modules ? ELEMENTSOF(protect_kernel_modules_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_kernel_modules_table), typeof(&*(protect_kernel_modules_table ))), sizeof(protect_kernel_modules_table)/sizeof((protect_kernel_modules_table )[0]), ((void)0))) : 0) + |
| 1090 | protect_home_cnt + protect_system_cnt + |
| 1091 | (namespace_info_mount_apivfs(root_directory, ns_info) ? ELEMENTSOF(apivfs_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(apivfs_table), typeof(&*(apivfs_table))), sizeof( apivfs_table)/sizeof((apivfs_table)[0]), ((void)0))) : 0); |
| 1092 | } |
| 1093 | |
| 1094 | static void normalize_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) { |
| 1095 | assert(n_mounts)do { if ((__builtin_expect(!!(!(n_mounts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("n_mounts"), "../src/core/namespace.c", 1095 , __PRETTY_FUNCTION__); } while (0); |
| 1096 | assert(mounts || *n_mounts == 0)do { if ((__builtin_expect(!!(!(mounts || *n_mounts == 0)),0) )) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("mounts || *n_mounts == 0" ), "../src/core/namespace.c", 1096, __PRETTY_FUNCTION__); } while (0); |
| 1097 | |
| 1098 | qsort_safe(mounts, *n_mounts, sizeof(MountEntry), mount_path_compare); |
| 1099 | |
| 1100 | drop_duplicates(mounts, n_mounts); |
| 1101 | drop_outside_root(root_directory, mounts, n_mounts); |
| 1102 | drop_inaccessible(mounts, n_mounts); |
| 1103 | drop_nop(mounts, n_mounts); |
| 1104 | } |
| 1105 | |
| 1106 | int setup_namespace( |
| 1107 | const char* root_directory, |
| 1108 | const char* root_image, |
| 1109 | const NamespaceInfo *ns_info, |
| 1110 | char** read_write_paths, |
| 1111 | char** read_only_paths, |
| 1112 | char** inaccessible_paths, |
| 1113 | char** empty_directories, |
| 1114 | const BindMount *bind_mounts, |
| 1115 | size_t n_bind_mounts, |
| 1116 | const TemporaryFileSystem *temporary_filesystems, |
| 1117 | size_t n_temporary_filesystems, |
| 1118 | const char* tmp_dir, |
| 1119 | const char* var_tmp_dir, |
| 1120 | ProtectHome protect_home, |
| 1121 | ProtectSystem protect_system, |
| 1122 | unsigned long mount_flags, |
| 1123 | DissectImageFlags dissect_image_flags) { |
| 1124 | |
| 1125 | _cleanup_(loop_device_unrefp)__attribute__((cleanup(loop_device_unrefp))) LoopDevice *loop_device = NULL((void*)0); |
| 1126 | _cleanup_(decrypted_image_unrefp)__attribute__((cleanup(decrypted_image_unrefp))) DecryptedImage *decrypted_image = NULL((void*)0); |
| 1127 | _cleanup_(dissected_image_unrefp)__attribute__((cleanup(dissected_image_unrefp))) DissectedImage *dissected_image = NULL((void*)0); |
| 1128 | _cleanup_free___attribute__((cleanup(freep))) void *root_hash = NULL((void*)0); |
| 1129 | MountEntry *m, *mounts = NULL((void*)0); |
| 1130 | size_t root_hash_size = 0; |
| 1131 | const char *root; |
| 1132 | size_t n_mounts; |
| 1133 | bool_Bool make_slave; |
| 1134 | bool_Bool require_prefix = false0; |
| 1135 | int r = 0; |
| 1136 | |
| 1137 | assert(ns_info)do { if ((__builtin_expect(!!(!(ns_info)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ns_info"), "../src/core/namespace.c", 1137 , __PRETTY_FUNCTION__); } while (0); |
| 1138 | |
| 1139 | if (mount_flags == 0) |
| 1140 | mount_flags = MS_SHARED(1<<20); |
| 1141 | |
| 1142 | if (root_image) { |
| 1143 | dissect_image_flags |= DISSECT_IMAGE_REQUIRE_ROOT; |
| 1144 | |
| 1145 | if (protect_system == PROTECT_SYSTEM_STRICT && |
| 1146 | protect_home != PROTECT_HOME_NO && |
| 1147 | strv_isempty(read_write_paths)) |
| 1148 | dissect_image_flags |= DISSECT_IMAGE_READ_ONLY; |
| 1149 | |
| 1150 | r = loop_device_make_by_path(root_image, |
| 1151 | dissect_image_flags & DISSECT_IMAGE_READ_ONLY ? O_RDONLY00 : O_RDWR02, |
| 1152 | &loop_device); |
| 1153 | if (r < 0) |
| 1154 | return r; |
| 1155 | |
| 1156 | r = root_hash_load(root_image, &root_hash, &root_hash_size); |
| 1157 | if (r < 0) |
| 1158 | return r; |
| 1159 | |
| 1160 | r = dissect_image(loop_device->fd, root_hash, root_hash_size, dissect_image_flags, &dissected_image); |
| 1161 | if (r < 0) |
| 1162 | return r; |
| 1163 | |
| 1164 | r = dissected_image_decrypt(dissected_image, NULL((void*)0), root_hash, root_hash_size, dissect_image_flags, &decrypted_image); |
| 1165 | if (r < 0) |
| 1166 | return r; |
| 1167 | } |
| 1168 | |
| 1169 | if (root_directory) |
| 1170 | root = root_directory; |
| 1171 | else { |
| 1172 | /* Always create the mount namespace in a temporary directory, instead of operating |
| 1173 | * directly in the root. The temporary directory prevents any mounts from being |
| 1174 | * potentially obscured my other mounts we already applied. |
| 1175 | * We use the same mount point for all images, which is safe, since they all live |
| 1176 | * in their own namespaces after all, and hence won't see each other. */ |
| 1177 | |
| 1178 | root = "/run/systemd/unit-root"; |
| 1179 | (void) mkdir_label(root, 0700); |
| 1180 | require_prefix = true1; |
| 1181 | } |
| 1182 | |
| 1183 | n_mounts = namespace_calculate_mounts( |
| 1184 | root, |
| 1185 | ns_info, |
| 1186 | read_write_paths, |
| 1187 | read_only_paths, |
| 1188 | inaccessible_paths, |
| 1189 | empty_directories, |
| 1190 | n_bind_mounts, |
| 1191 | n_temporary_filesystems, |
| 1192 | tmp_dir, var_tmp_dir, |
| 1193 | protect_home, protect_system); |
| 1194 | |
| 1195 | /* Set mount slave mode */ |
| 1196 | make_slave = root || n_mounts > 0 || ns_info->private_mounts; |
| 1197 | |
| 1198 | if (n_mounts > 0) { |
| 1199 | m = mounts = (MountEntry *) alloca0(n_mounts * sizeof(MountEntry))({ char *_new_; size_t _len_ = n_mounts * sizeof(MountEntry); _new_ = __builtin_alloca (_len_); (void *) memset(_new_, 0, _len_ ); }); |
| 1200 | r = append_access_mounts(&m, read_write_paths, READWRITE, require_prefix); |
| 1201 | if (r < 0) |
| 1202 | goto finish; |
| 1203 | |
| 1204 | r = append_access_mounts(&m, read_only_paths, READONLY, require_prefix); |
| 1205 | if (r < 0) |
| 1206 | goto finish; |
| 1207 | |
| 1208 | r = append_access_mounts(&m, inaccessible_paths, INACCESSIBLE, require_prefix); |
| 1209 | if (r < 0) |
| 1210 | goto finish; |
| 1211 | |
| 1212 | r = append_empty_dir_mounts(&m, empty_directories); |
| 1213 | if (r < 0) |
| 1214 | goto finish; |
| 1215 | |
| 1216 | r = append_bind_mounts(&m, bind_mounts, n_bind_mounts); |
| 1217 | if (r < 0) |
| 1218 | goto finish; |
| 1219 | |
| 1220 | r = append_tmpfs_mounts(&m, temporary_filesystems, n_temporary_filesystems); |
| 1221 | if (r < 0) |
| 1222 | goto finish; |
| 1223 | |
| 1224 | if (tmp_dir) { |
| 1225 | *(m++) = (MountEntry) { |
| 1226 | .path_const = "/tmp", |
| 1227 | .mode = PRIVATE_TMP, |
| 1228 | .source_const = tmp_dir, |
| 1229 | }; |
| 1230 | } |
| 1231 | |
| 1232 | if (var_tmp_dir) { |
| 1233 | *(m++) = (MountEntry) { |
| 1234 | .path_const = "/var/tmp", |
| 1235 | .mode = PRIVATE_TMP, |
| 1236 | .source_const = var_tmp_dir, |
| 1237 | }; |
| 1238 | } |
| 1239 | |
| 1240 | if (ns_info->private_dev) { |
| 1241 | *(m++) = (MountEntry) { |
| 1242 | .path_const = "/dev", |
| 1243 | .mode = PRIVATE_DEV, |
| 1244 | }; |
| 1245 | } |
| 1246 | |
| 1247 | if (ns_info->protect_kernel_tunables) { |
| 1248 | r = append_static_mounts(&m, protect_kernel_tunables_table, ELEMENTSOF(protect_kernel_tunables_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_kernel_tunables_table), typeof(&*(protect_kernel_tunables_table ))), sizeof(protect_kernel_tunables_table)/sizeof((protect_kernel_tunables_table )[0]), ((void)0))), ns_info->ignore_protect_paths); |
| 1249 | if (r < 0) |
| 1250 | goto finish; |
| 1251 | } |
| 1252 | |
| 1253 | if (ns_info->protect_kernel_modules) { |
| 1254 | r = append_static_mounts(&m, protect_kernel_modules_table, ELEMENTSOF(protect_kernel_modules_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_kernel_modules_table), typeof(&*(protect_kernel_modules_table ))), sizeof(protect_kernel_modules_table)/sizeof((protect_kernel_modules_table )[0]), ((void)0))), ns_info->ignore_protect_paths); |
| 1255 | if (r < 0) |
| 1256 | goto finish; |
| 1257 | } |
| 1258 | |
| 1259 | if (ns_info->protect_control_groups) { |
| 1260 | *(m++) = (MountEntry) { |
| 1261 | .path_const = "/sys/fs/cgroup", |
| 1262 | .mode = READONLY, |
| 1263 | }; |
| 1264 | } |
| 1265 | |
| 1266 | r = append_protect_home(&m, protect_home, ns_info->ignore_protect_paths); |
| 1267 | if (r < 0) |
| 1268 | goto finish; |
| 1269 | |
| 1270 | r = append_protect_system(&m, protect_system, false0); |
| 1271 | if (r < 0) |
| 1272 | goto finish; |
| 1273 | |
| 1274 | if (namespace_info_mount_apivfs(root, ns_info)) { |
| 1275 | r = append_static_mounts(&m, apivfs_table, ELEMENTSOF(apivfs_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(apivfs_table), typeof(&*(apivfs_table))), sizeof( apivfs_table)/sizeof((apivfs_table)[0]), ((void)0))), ns_info->ignore_protect_paths); |
| 1276 | if (r < 0) |
| 1277 | goto finish; |
| 1278 | } |
| 1279 | |
| 1280 | assert(mounts + n_mounts == m)do { if ((__builtin_expect(!!(!(mounts + n_mounts == m)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("mounts + n_mounts == m" ), "../src/core/namespace.c", 1280, __PRETTY_FUNCTION__); } while (0); |
| 1281 | |
| 1282 | /* Prepend the root directory where that's necessary */ |
| 1283 | r = prefix_where_needed(mounts, n_mounts, root); |
| 1284 | if (r < 0) |
| 1285 | goto finish; |
| 1286 | |
| 1287 | normalize_mounts(root_directory, mounts, &n_mounts); |
| 1288 | } |
| 1289 | |
| 1290 | if (unshare(CLONE_NEWNS0x00020000) < 0) { |
| 1291 | r = -errno(*__errno_location ()); |
| 1292 | goto finish; |
| 1293 | } |
| 1294 | |
| 1295 | if (make_slave) { |
| 1296 | /* Remount / as SLAVE so that nothing now mounted in the namespace |
| 1297 | shows up in the parent */ |
| 1298 | if (mount(NULL((void*)0), "/", NULL((void*)0), MS_SLAVE(1<<19)|MS_REC16384, NULL((void*)0)) < 0) { |
| 1299 | r = -errno(*__errno_location ()); |
| 1300 | goto finish; |
| 1301 | } |
| 1302 | } |
| 1303 | |
| 1304 | if (root_image) { |
| 1305 | /* A root image is specified, mount it to the right place */ |
| 1306 | r = dissected_image_mount(dissected_image, root, UID_INVALID((uid_t) -1), dissect_image_flags); |
| 1307 | if (r < 0) |
| 1308 | goto finish; |
| 1309 | |
| 1310 | if (decrypted_image) { |
| 1311 | r = decrypted_image_relinquish(decrypted_image); |
| 1312 | if (r < 0) |
| 1313 | goto finish; |
| 1314 | } |
| 1315 | |
| 1316 | loop_device_relinquish(loop_device); |
| 1317 | |
| 1318 | } else if (root_directory) { |
| 1319 | |
| 1320 | /* A root directory is specified. Turn its directory into bind mount, if it isn't one yet. */ |
| 1321 | r = path_is_mount_point(root, NULL((void*)0), AT_SYMLINK_FOLLOW0x400); |
| 1322 | if (r < 0) |
| 1323 | goto finish; |
| 1324 | if (r == 0) { |
| 1325 | if (mount(root, root, NULL((void*)0), MS_BIND4096|MS_REC16384, NULL((void*)0)) < 0) { |
| 1326 | r = -errno(*__errno_location ()); |
| 1327 | goto finish; |
| 1328 | } |
| 1329 | } |
| 1330 | |
| 1331 | } else if (root) { |
| 1332 | |
| 1333 | /* Let's mount the main root directory to the root directory to use */ |
| 1334 | if (mount("/", root, NULL((void*)0), MS_BIND4096|MS_REC16384, NULL((void*)0)) < 0) { |
| 1335 | r = -errno(*__errno_location ()); |
| 1336 | goto finish; |
| 1337 | } |
| 1338 | } |
| 1339 | |
| 1340 | /* Try to set up the new root directory before mounting anything else there. */ |
| 1341 | if (root_image || root_directory) |
| 1342 | (void) base_filesystem_create(root, UID_INVALID((uid_t) -1), GID_INVALID((gid_t) -1)); |
| 1343 | |
| 1344 | if (n_mounts > 0) { |
| 1345 | _cleanup_fclose___attribute__((cleanup(fclosep))) FILE *proc_self_mountinfo = NULL((void*)0); |
| 1346 | char **blacklist; |
| 1347 | size_t j; |
| 1348 | |
| 1349 | /* Open /proc/self/mountinfo now as it may become unavailable if we mount anything on top of /proc. |
| 1350 | * For example, this is the case with the option: 'InaccessiblePaths=/proc' */ |
| 1351 | proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); |
| 1352 | if (!proc_self_mountinfo) { |
| 1353 | r = -errno(*__errno_location ()); |
| 1354 | goto finish; |
| 1355 | } |
| 1356 | |
| 1357 | /* First round, establish all mounts we need */ |
| 1358 | for (;;) { |
| 1359 | bool_Bool again = false0; |
| 1360 | |
| 1361 | for (m = mounts; m < mounts + n_mounts; ++m) { |
| 1362 | |
| 1363 | if (m->applied) |
| 1364 | continue; |
| 1365 | |
| 1366 | r = follow_symlink(root, m); |
| 1367 | if (r < 0) |
| 1368 | goto finish; |
| 1369 | if (r == 0) { |
| 1370 | /* We hit a symlinked mount point. The entry got rewritten and might point to a |
| 1371 | * very different place now. Let's normalize the changed list, and start from |
| 1372 | * the beginning. After all to mount the entry at the new location we might |
| 1373 | * need some other mounts first */ |
| 1374 | again = true1; |
| 1375 | break; |
| 1376 | } |
| 1377 | |
| 1378 | r = apply_mount(root, m); |
| 1379 | if (r < 0) |
| 1380 | goto finish; |
| 1381 | |
| 1382 | m->applied = true1; |
| 1383 | } |
| 1384 | |
| 1385 | if (!again) |
| 1386 | break; |
| 1387 | |
| 1388 | normalize_mounts(root_directory, mounts, &n_mounts); |
| 1389 | } |
| 1390 | |
| 1391 | /* Create a blacklist we can pass to bind_mount_recursive() */ |
| 1392 | blacklist = newa(char*, n_mounts+1)({ do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof (char*), n_mounts+1))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD , ("!size_multiply_overflow(sizeof(char*), n_mounts+1)"), "../src/core/namespace.c" , 1392, __PRETTY_FUNCTION__); } while (0); (char**) __builtin_alloca (sizeof(char*)*(n_mounts+1)); }); |
| 1393 | for (j = 0; j < n_mounts; j++) |
| 1394 | blacklist[j] = (char*) mount_entry_path(mounts+j); |
| 1395 | blacklist[j] = NULL((void*)0); |
| 1396 | |
| 1397 | /* Second round, flip the ro bits if necessary. */ |
| 1398 | for (m = mounts; m < mounts + n_mounts; ++m) { |
| 1399 | r = make_read_only(m, blacklist, proc_self_mountinfo); |
| 1400 | if (r < 0) |
| 1401 | goto finish; |
| 1402 | } |
| 1403 | } |
| 1404 | |
| 1405 | if (root) { |
| 1406 | /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */ |
| 1407 | r = mount_move_root(root); |
| 1408 | if (r < 0) |
| 1409 | goto finish; |
| 1410 | } |
| 1411 | |
| 1412 | /* Remount / as the desired mode. Note that this will not |
| 1413 | * reestablish propagation from our side to the host, since |
| 1414 | * what's disconnected is disconnected. */ |
| 1415 | if (mount(NULL((void*)0), "/", NULL((void*)0), mount_flags | MS_REC16384, NULL((void*)0)) < 0) { |
| 1416 | r = -errno(*__errno_location ()); |
| 1417 | goto finish; |
| 1418 | } |
| 1419 | |
| 1420 | r = 0; |
| 1421 | |
| 1422 | finish: |
| 1423 | for (m = mounts; m < mounts + n_mounts; m++) |
| 1424 | mount_entry_done(m); |
| 1425 | |
| 1426 | return r; |
| 1427 | } |
| 1428 | |
| 1429 | void bind_mount_free_many(BindMount *b, size_t n) { |
| 1430 | size_t i; |
| 1431 | |
| 1432 | assert(b || n == 0)do { if ((__builtin_expect(!!(!(b || n == 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("b || n == 0"), "../src/core/namespace.c" , 1432, __PRETTY_FUNCTION__); } while (0); |
| 1433 | |
| 1434 | for (i = 0; i < n; i++) { |
| 1435 | free(b[i].source); |
| 1436 | free(b[i].destination); |
| 1437 | } |
| 1438 | |
| 1439 | free(b); |
| 1440 | } |
| 1441 | |
| 1442 | int bind_mount_add(BindMount **b, size_t *n, const BindMount *item) { |
| 1443 | _cleanup_free___attribute__((cleanup(freep))) char *s = NULL((void*)0), *d = NULL((void*)0); |
| 1444 | BindMount *c; |
| 1445 | |
| 1446 | assert(b)do { if ((__builtin_expect(!!(!(b)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("b"), "../src/core/namespace.c", 1446, __PRETTY_FUNCTION__ ); } while (0); |
| 1447 | assert(n)do { if ((__builtin_expect(!!(!(n)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("n"), "../src/core/namespace.c", 1447, __PRETTY_FUNCTION__ ); } while (0); |
| 1448 | assert(item)do { if ((__builtin_expect(!!(!(item)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("item"), "../src/core/namespace.c", 1448 , __PRETTY_FUNCTION__); } while (0); |
| 1449 | |
| 1450 | s = strdup(item->source); |
| 1451 | if (!s) |
| 1452 | return -ENOMEM12; |
| 1453 | |
| 1454 | d = strdup(item->destination); |
| 1455 | if (!d) |
| 1456 | return -ENOMEM12; |
| 1457 | |
| 1458 | c = reallocarray(*b, *n + 1, sizeof(BindMount)); |
| 1459 | if (!c) |
| 1460 | return -ENOMEM12; |
| 1461 | |
| 1462 | *b = c; |
| 1463 | |
| 1464 | c[(*n) ++] = (BindMount) { |
| 1465 | .source = TAKE_PTR(s)({ typeof(s) _ptr_ = (s); (s) = ((void*)0); _ptr_; }), |
| 1466 | .destination = TAKE_PTR(d)({ typeof(d) _ptr_ = (d); (d) = ((void*)0); _ptr_; }), |
| 1467 | .read_only = item->read_only, |
| 1468 | .recursive = item->recursive, |
| 1469 | .ignore_enoent = item->ignore_enoent, |
| 1470 | }; |
| 1471 | |
| 1472 | return 0; |
| 1473 | } |
| 1474 | |
| 1475 | void temporary_filesystem_free_many(TemporaryFileSystem *t, size_t n) { |
| 1476 | size_t i; |
| 1477 | |
| 1478 | assert(t || n == 0)do { if ((__builtin_expect(!!(!(t || n == 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t || n == 0"), "../src/core/namespace.c" , 1478, __PRETTY_FUNCTION__); } while (0); |
| 1479 | |
| 1480 | for (i = 0; i < n; i++) { |
| 1481 | free(t[i].path); |
| 1482 | free(t[i].options); |
| 1483 | } |
| 1484 | |
| 1485 | free(t); |
| 1486 | } |
| 1487 | |
| 1488 | int temporary_filesystem_add( |
| 1489 | TemporaryFileSystem **t, |
| 1490 | size_t *n, |
| 1491 | const char *path, |
| 1492 | const char *options) { |
| 1493 | |
| 1494 | _cleanup_free___attribute__((cleanup(freep))) char *p = NULL((void*)0), *o = NULL((void*)0); |
| 1495 | TemporaryFileSystem *c; |
| 1496 | |
| 1497 | assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t"), "../src/core/namespace.c", 1497, __PRETTY_FUNCTION__ ); } while (0); |
| 1498 | assert(n)do { if ((__builtin_expect(!!(!(n)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("n"), "../src/core/namespace.c", 1498, __PRETTY_FUNCTION__ ); } while (0); |
| 1499 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/core/namespace.c", 1499 , __PRETTY_FUNCTION__); } while (0); |
| 1500 | |
| 1501 | p = strdup(path); |
| 1502 | if (!p) |
| 1503 | return -ENOMEM12; |
| 1504 | |
| 1505 | if (!isempty(options)) { |
| 1506 | o = strdup(options); |
| 1507 | if (!o) |
| 1508 | return -ENOMEM12; |
| 1509 | } |
| 1510 | |
| 1511 | c = reallocarray(*t, *n + 1, sizeof(TemporaryFileSystem)); |
| 1512 | if (!c) |
| 1513 | return -ENOMEM12; |
| 1514 | |
| 1515 | *t = c; |
| 1516 | |
| 1517 | c[(*n) ++] = (TemporaryFileSystem) { |
| 1518 | .path = TAKE_PTR(p)({ typeof(p) _ptr_ = (p); (p) = ((void*)0); _ptr_; }), |
| 1519 | .options = TAKE_PTR(o)({ typeof(o) _ptr_ = (o); (o) = ((void*)0); _ptr_; }), |
| 1520 | }; |
| 1521 | |
| 1522 | return 0; |
| 1523 | } |
| 1524 | |
| 1525 | static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) { |
| 1526 | _cleanup_free___attribute__((cleanup(freep))) char *x = NULL((void*)0); |
| 1527 | char bid[SD_ID128_STRING_MAX33]; |
| 1528 | sd_id128_t boot_id; |
| 1529 | int r; |
| 1530 | |
| 1531 | assert(id)do { if ((__builtin_expect(!!(!(id)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("id"), "../src/core/namespace.c", 1531, __PRETTY_FUNCTION__ ); } while (0); |
| 1532 | assert(prefix)do { if ((__builtin_expect(!!(!(prefix)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("prefix"), "../src/core/namespace.c", 1532 , __PRETTY_FUNCTION__); } while (0); |
| 1533 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/core/namespace.c", 1533 , __PRETTY_FUNCTION__); } while (0); |
| 1534 | |
| 1535 | /* We include the boot id in the directory so that after a |
| 1536 | * reboot we can easily identify obsolete directories. */ |
| 1537 | |
| 1538 | r = sd_id128_get_boot(&boot_id); |
| 1539 | if (r < 0) |
| 1540 | return r; |
| 1541 | |
| 1542 | x = strjoin(prefix, "/systemd-private-", sd_id128_to_string(boot_id, bid), "-", id, "-XXXXXX")strjoin_real((prefix), "/systemd-private-", sd_id128_to_string (boot_id, bid), "-", id, "-XXXXXX", ((void*)0)); |
| 1543 | if (!x) |
| 1544 | return -ENOMEM12; |
| 1545 | |
| 1546 | RUN_WITH_UMASK(0077)for (__attribute__((cleanup(_reset_umask_))) struct _umask_struct_ _saved_umask_ = { umask(0077), 0 }; !_saved_umask_.quit ; _saved_umask_ .quit = 1) |
| 1547 | if (!mkdtemp(x)) |
| 1548 | return -errno(*__errno_location ()); |
| 1549 | |
| 1550 | RUN_WITH_UMASK(0000)for (__attribute__((cleanup(_reset_umask_))) struct _umask_struct_ _saved_umask_ = { umask(0000), 0 }; !_saved_umask_.quit ; _saved_umask_ .quit = 1) { |
| 1551 | char *y; |
| 1552 | |
| 1553 | y = strjoina(x, "/tmp")({ const char *_appendees_[] = { x, "/tmp" }; char *_d_, *_p_ ; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); |
| 1554 | |
| 1555 | if (mkdir(y, 0777 | S_ISVTX01000) < 0) |
| 1556 | return -errno(*__errno_location ()); |
| 1557 | } |
| 1558 | |
| 1559 | *path = TAKE_PTR(x)({ typeof(x) _ptr_ = (x); (x) = ((void*)0); _ptr_; }); |
| 1560 | |
| 1561 | return 0; |
| 1562 | } |
| 1563 | |
| 1564 | int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) { |
| 1565 | char *a, *b; |
| 1566 | int r; |
| 1567 | |
| 1568 | assert(id)do { if ((__builtin_expect(!!(!(id)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("id"), "../src/core/namespace.c", 1568, __PRETTY_FUNCTION__ ); } while (0); |
| 1569 | assert(tmp_dir)do { if ((__builtin_expect(!!(!(tmp_dir)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("tmp_dir"), "../src/core/namespace.c", 1569 , __PRETTY_FUNCTION__); } while (0); |
| 1570 | assert(var_tmp_dir)do { if ((__builtin_expect(!!(!(var_tmp_dir)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("var_tmp_dir"), "../src/core/namespace.c" , 1570, __PRETTY_FUNCTION__); } while (0); |
| 1571 | |
| 1572 | r = setup_one_tmp_dir(id, "/tmp", &a); |
| 1573 | if (r < 0) |
| 1574 | return r; |
| 1575 | |
| 1576 | r = setup_one_tmp_dir(id, "/var/tmp", &b); |
| 1577 | if (r < 0) { |
| 1578 | char *t; |
| 1579 | |
| 1580 | t = strjoina(a, "/tmp")({ const char *_appendees_[] = { a, "/tmp" }; char *_d_, *_p_ ; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); |
| 1581 | rmdir(t); |
| 1582 | rmdir(a); |
| 1583 | |
| 1584 | free(a); |
| 1585 | return r; |
| 1586 | } |
| 1587 | |
| 1588 | *tmp_dir = a; |
| 1589 | *var_tmp_dir = b; |
| 1590 | |
| 1591 | return 0; |
| 1592 | } |
| 1593 | |
| 1594 | int setup_netns(int netns_storage_socket[2]) { |
| 1595 | _cleanup_close___attribute__((cleanup(closep))) int netns = -1; |
| 1596 | int r, q; |
| 1597 | |
| 1598 | assert(netns_storage_socket)do { if ((__builtin_expect(!!(!(netns_storage_socket)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("netns_storage_socket"), "../src/core/namespace.c" , 1598, __PRETTY_FUNCTION__); } while (0); |
| 1599 | assert(netns_storage_socket[0] >= 0)do { if ((__builtin_expect(!!(!(netns_storage_socket[0] >= 0)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("netns_storage_socket[0] >= 0" ), "../src/core/namespace.c", 1599, __PRETTY_FUNCTION__); } while (0); |
| 1600 | assert(netns_storage_socket[1] >= 0)do { if ((__builtin_expect(!!(!(netns_storage_socket[1] >= 0)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("netns_storage_socket[1] >= 0" ), "../src/core/namespace.c", 1600, __PRETTY_FUNCTION__); } while (0); |
| 1601 | |
| 1602 | /* We use the passed socketpair as a storage buffer for our |
| 1603 | * namespace reference fd. Whatever process runs this first |
| 1604 | * shall create a new namespace, all others should just join |
| 1605 | * it. To serialize that we use a file lock on the socket |
| 1606 | * pair. |
| 1607 | * |
| 1608 | * It's a bit crazy, but hey, works great! */ |
| 1609 | |
| 1610 | if (lockf(netns_storage_socket[0], F_LOCK1, 0) < 0) |
| 1611 | return -errno(*__errno_location ()); |
| 1612 | |
| 1613 | netns = receive_one_fd(netns_storage_socket[0], MSG_DONTWAITMSG_DONTWAIT); |
| 1614 | if (netns == -EAGAIN11) { |
| 1615 | /* Nothing stored yet, so let's create a new namespace */ |
| 1616 | |
| 1617 | if (unshare(CLONE_NEWNET0x40000000) < 0) { |
| 1618 | r = -errno(*__errno_location ()); |
| 1619 | goto fail; |
| 1620 | } |
| 1621 | |
| 1622 | loopback_setup(); |
| 1623 | |
| 1624 | netns = open("/proc/self/ns/net", O_RDONLY00|O_CLOEXEC02000000|O_NOCTTY0400); |
| 1625 | if (netns < 0) { |
| 1626 | r = -errno(*__errno_location ()); |
| 1627 | goto fail; |
| 1628 | } |
| 1629 | |
| 1630 | r = 1; |
| 1631 | |
| 1632 | } else if (netns < 0) { |
| 1633 | r = netns; |
| 1634 | goto fail; |
| 1635 | |
| 1636 | } else { |
| 1637 | /* Yay, found something, so let's join the namespace */ |
| 1638 | if (setns(netns, CLONE_NEWNET0x40000000) < 0) { |
| 1639 | r = -errno(*__errno_location ()); |
| 1640 | goto fail; |
| 1641 | } |
| 1642 | |
| 1643 | r = 0; |
| 1644 | } |
| 1645 | |
| 1646 | q = send_one_fd(netns_storage_socket[1], netns, MSG_DONTWAIT)send_one_fd_iov_sa(netns_storage_socket[1], netns, ((void*)0) , 0, ((void*)0), 0, MSG_DONTWAIT); |
| 1647 | if (q < 0) { |
| 1648 | r = q; |
| 1649 | goto fail; |
| 1650 | } |
| 1651 | |
| 1652 | fail: |
| 1653 | (void) lockf(netns_storage_socket[0], F_ULOCK0, 0); |
| 1654 | return r; |
| 1655 | } |
| 1656 | |
| 1657 | bool_Bool ns_type_supported(NamespaceType type) { |
| 1658 | const char *t, *ns_proc; |
| 1659 | |
| 1660 | t = namespace_type_to_string(type); |
| 1661 | if (!t) /* Don't know how to translate this? Then it's not supported */ |
| 1662 | return false0; |
| 1663 | |
| 1664 | ns_proc = strjoina("/proc/self/ns/", t)({ const char *_appendees_[] = { "/proc/self/ns/", t }; char * _d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); |
| 1665 | return access(ns_proc, F_OK0) == 0; |
| 1666 | } |
| 1667 | |
| 1668 | static const char *const protect_home_table[_PROTECT_HOME_MAX] = { |
| 1669 | [PROTECT_HOME_NO] = "no", |
| 1670 | [PROTECT_HOME_YES] = "yes", |
| 1671 | [PROTECT_HOME_READ_ONLY] = "read-only", |
| 1672 | [PROTECT_HOME_TMPFS] = "tmpfs", |
| 1673 | }; |
| 1674 | |
| 1675 | DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_home, ProtectHome, PROTECT_HOME_YES)const char *protect_home_to_string(ProtectHome i) { if (i < 0 || i >= (ProtectHome) __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(protect_home_table), typeof (&*(protect_home_table))), sizeof(protect_home_table)/sizeof ((protect_home_table)[0]), ((void)0)))) return ((void*)0); return protect_home_table[i]; } ProtectHome protect_home_from_string (const char *s) { int b; if (!s) return -1; b = parse_boolean (s); if (b == 0) return (ProtectHome) 0; else if (b > 0) return PROTECT_HOME_YES; return (ProtectHome) string_table_lookup(protect_home_table , __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(protect_home_table), typeof(&*(protect_home_table ))), sizeof(protect_home_table)/sizeof((protect_home_table)[0 ]), ((void)0))), s); }; |
| 1676 | |
| 1677 | static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = { |
| 1678 | [PROTECT_SYSTEM_NO] = "no", |
| 1679 | [PROTECT_SYSTEM_YES] = "yes", |
| 1680 | [PROTECT_SYSTEM_FULL] = "full", |
| 1681 | [PROTECT_SYSTEM_STRICT] = "strict", |
| 1682 | }; |
| 1683 | |
| 1684 | DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_system, ProtectSystem, PROTECT_SYSTEM_YES)const char *protect_system_to_string(ProtectSystem i) { if (i < 0 || i >= (ProtectSystem) __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(protect_system_table), typeof(&*(protect_system_table))), sizeof(protect_system_table )/sizeof((protect_system_table)[0]), ((void)0)))) return ((void *)0); return protect_system_table[i]; } ProtectSystem protect_system_from_string (const char *s) { int b; if (!s) return -1; b = parse_boolean (s); if (b == 0) return (ProtectSystem) 0; else if (b > 0) return PROTECT_SYSTEM_YES; return (ProtectSystem) string_table_lookup (protect_system_table, __extension__ (__builtin_choose_expr( ! __builtin_types_compatible_p(typeof(protect_system_table), typeof (&*(protect_system_table))), sizeof(protect_system_table) /sizeof((protect_system_table)[0]), ((void)0))), s); }; |
| 1685 | |
| 1686 | static const char* const namespace_type_table[] = { |
| 1687 | [NAMESPACE_MOUNT] = "mnt", |
| 1688 | [NAMESPACE_CGROUP] = "cgroup", |
| 1689 | [NAMESPACE_UTS] = "uts", |
| 1690 | [NAMESPACE_IPC] = "ipc", |
| 1691 | [NAMESPACE_USER] = "user", |
| 1692 | [NAMESPACE_PID] = "pid", |
| 1693 | [NAMESPACE_NET] = "net", |
| 1694 | }; |
| 1695 | |
| 1696 | DEFINE_STRING_TABLE_LOOKUP(namespace_type, NamespaceType)const char *namespace_type_to_string(NamespaceType i) { if (i < 0 || i >= (NamespaceType) __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(namespace_type_table), typeof(&*(namespace_type_table))), sizeof(namespace_type_table )/sizeof((namespace_type_table)[0]), ((void)0)))) return ((void *)0); return namespace_type_table[i]; } NamespaceType namespace_type_from_string (const char *s) { return (NamespaceType) string_table_lookup( namespace_type_table, __extension__ (__builtin_choose_expr( ! __builtin_types_compatible_p(typeof(namespace_type_table), typeof (&*(namespace_type_table))), sizeof(namespace_type_table) /sizeof((namespace_type_table)[0]), ((void)0))), s); }; |