| File: | build-scan/../src/nspawn/nspawn-mount.c |
| Warning: | line 320, column 25 Potential leak of memory pointed to by 'destination' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
| 2 | ||||
| 3 | #include <sys/mount.h> | |||
| 4 | #include <linux1/magic.h> | |||
| 5 | ||||
| 6 | #include "alloc-util.h" | |||
| 7 | #include "escape.h" | |||
| 8 | #include "fd-util.h" | |||
| 9 | #include "fileio.h" | |||
| 10 | #include "fs-util.h" | |||
| 11 | #include "label.h" | |||
| 12 | #include "mkdir.h" | |||
| 13 | #include "mount-util.h" | |||
| 14 | #include "nspawn-mount.h" | |||
| 15 | #include "parse-util.h" | |||
| 16 | #include "path-util.h" | |||
| 17 | #include "rm-rf.h" | |||
| 18 | #include "set.h" | |||
| 19 | #include "stat-util.h" | |||
| 20 | #include "string-util.h" | |||
| 21 | #include "strv.h" | |||
| 22 | #include "user-util.h" | |||
| 23 | #include "util.h" | |||
| 24 | ||||
| 25 | CustomMount* custom_mount_add(CustomMount **l, size_t *n, CustomMountType t) { | |||
| 26 | CustomMount *c, *ret; | |||
| 27 | ||||
| 28 | assert(l)do { if ((__builtin_expect(!!(!(l)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("l"), "../src/nspawn/nspawn-mount.c", 28 , __PRETTY_FUNCTION__); } while (0); | |||
| 29 | assert(n)do { if ((__builtin_expect(!!(!(n)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("n"), "../src/nspawn/nspawn-mount.c", 29 , __PRETTY_FUNCTION__); } while (0); | |||
| 30 | assert(t >= 0)do { if ((__builtin_expect(!!(!(t >= 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t >= 0"), "../src/nspawn/nspawn-mount.c" , 30, __PRETTY_FUNCTION__); } while (0); | |||
| 31 | assert(t < _CUSTOM_MOUNT_TYPE_MAX)do { if ((__builtin_expect(!!(!(t < _CUSTOM_MOUNT_TYPE_MAX )),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("t < _CUSTOM_MOUNT_TYPE_MAX" ), "../src/nspawn/nspawn-mount.c", 31, __PRETTY_FUNCTION__); } while (0); | |||
| 32 | ||||
| 33 | c = reallocarray(*l, *n + 1, sizeof(CustomMount)); | |||
| 34 | if (!c) | |||
| 35 | return NULL((void*)0); | |||
| 36 | ||||
| 37 | *l = c; | |||
| 38 | ret = *l + *n; | |||
| 39 | (*n)++; | |||
| 40 | ||||
| 41 | *ret = (CustomMount) { .type = t }; | |||
| 42 | ||||
| 43 | return ret; | |||
| 44 | } | |||
| 45 | ||||
| 46 | void custom_mount_free_all(CustomMount *l, size_t n) { | |||
| 47 | size_t i; | |||
| 48 | ||||
| 49 | for (i = 0; i < n; i++) { | |||
| 50 | CustomMount *m = l + i; | |||
| 51 | ||||
| 52 | free(m->source); | |||
| 53 | free(m->destination); | |||
| 54 | free(m->options); | |||
| 55 | ||||
| 56 | if (m->work_dir) { | |||
| 57 | (void) rm_rf(m->work_dir, REMOVE_ROOT|REMOVE_PHYSICAL); | |||
| 58 | free(m->work_dir); | |||
| 59 | } | |||
| 60 | ||||
| 61 | if (m->rm_rf_tmpdir) { | |||
| 62 | (void) rm_rf(m->rm_rf_tmpdir, REMOVE_ROOT|REMOVE_PHYSICAL); | |||
| 63 | free(m->rm_rf_tmpdir); | |||
| 64 | } | |||
| 65 | ||||
| 66 | strv_free(m->lower); | |||
| 67 | } | |||
| 68 | ||||
| 69 | free(l); | |||
| 70 | } | |||
| 71 | ||||
| 72 | static int custom_mount_compare(const void *a, const void *b) { | |||
| 73 | const CustomMount *x = a, *y = b; | |||
| 74 | int r; | |||
| 75 | ||||
| 76 | r = path_compare(x->destination, y->destination); | |||
| 77 | if (r != 0) | |||
| 78 | return r; | |||
| 79 | ||||
| 80 | if (x->type < y->type) | |||
| 81 | return -1; | |||
| 82 | if (x->type > y->type) | |||
| 83 | return 1; | |||
| 84 | ||||
| 85 | return 0; | |||
| 86 | } | |||
| 87 | ||||
| 88 | static bool_Bool source_path_is_valid(const char *p) { | |||
| 89 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/nspawn/nspawn-mount.c", 89 , __PRETTY_FUNCTION__); } while (0); | |||
| 90 | ||||
| 91 | if (*p == '+') | |||
| 92 | p++; | |||
| 93 | ||||
| 94 | return path_is_absolute(p); | |||
| 95 | } | |||
| 96 | ||||
| 97 | static char *resolve_source_path(const char *dest, const char *source) { | |||
| 98 | ||||
| 99 | if (!source) | |||
| 100 | return NULL((void*)0); | |||
| 101 | ||||
| 102 | if (source[0] == '+') | |||
| 103 | return prefix_root(dest, source + 1); | |||
| 104 | ||||
| 105 | return strdup(source); | |||
| 106 | } | |||
| 107 | ||||
| 108 | int custom_mount_prepare_all(const char *dest, CustomMount *l, size_t n) { | |||
| 109 | size_t i; | |||
| 110 | int r; | |||
| 111 | ||||
| 112 | /* Prepare all custom mounts. This will make source we know all temporary directories. This is called in the | |||
| 113 | * parent process, so that we know the temporary directories to remove on exit before we fork off the | |||
| 114 | * children. */ | |||
| 115 | ||||
| 116 | assert(l || n == 0)do { if ((__builtin_expect(!!(!(l || n == 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("l || n == 0"), "../src/nspawn/nspawn-mount.c" , 116, __PRETTY_FUNCTION__); } while (0); | |||
| 117 | ||||
| 118 | /* Order the custom mounts, and make sure we have a working directory */ | |||
| 119 | qsort_safe(l, n, sizeof(CustomMount), custom_mount_compare); | |||
| 120 | ||||
| 121 | for (i = 0; i < n; i++) { | |||
| 122 | CustomMount *m = l + i; | |||
| 123 | ||||
| 124 | if (m->source) { | |||
| 125 | char *s; | |||
| 126 | ||||
| 127 | s = resolve_source_path(dest, m->source); | |||
| 128 | if (!s) | |||
| 129 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 129, __func__); | |||
| 130 | ||||
| 131 | free_and_replace(m->source, s)({ free(m->source); (m->source) = (s); (s) = ((void*)0) ; 0; }); | |||
| 132 | } else { | |||
| 133 | /* No source specified? In that case, use a throw-away temporary directory in /var/tmp */ | |||
| 134 | ||||
| 135 | m->rm_rf_tmpdir = strdup("/var/tmp/nspawn-temp-XXXXXX"); | |||
| 136 | if (!m->rm_rf_tmpdir) | |||
| 137 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 137, __func__); | |||
| 138 | ||||
| 139 | if (!mkdtemp(m->rm_rf_tmpdir)) { | |||
| 140 | m->rm_rf_tmpdir = mfree(m->rm_rf_tmpdir); | |||
| 141 | return log_error_errno(errno, "Failed to acquire temporary directory: %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/nspawn/nspawn-mount.c", 141, __func__ , "Failed to acquire temporary directory: %m") : -abs(_e); }); | |||
| 142 | } | |||
| 143 | ||||
| 144 | m->source = strjoin(m->rm_rf_tmpdir, "/src")strjoin_real((m->rm_rf_tmpdir), "/src", ((void*)0)); | |||
| 145 | if (!m->source) | |||
| 146 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 146, __func__); | |||
| 147 | ||||
| 148 | if (mkdir(m->source, 0755) < 0) | |||
| 149 | return log_error_errno(errno, "Failed to create %s: %m", m->source)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/nspawn/nspawn-mount.c", 149, __func__ , "Failed to create %s: %m", m->source) : -abs(_e); }); | |||
| 150 | } | |||
| 151 | ||||
| 152 | if (m->type == CUSTOM_MOUNT_OVERLAY) { | |||
| 153 | char **j; | |||
| 154 | ||||
| 155 | STRV_FOREACH(j, m->lower)for ((j) = (m->lower); (j) && *(j); (j)++) { | |||
| 156 | char *s; | |||
| 157 | ||||
| 158 | s = resolve_source_path(dest, *j); | |||
| 159 | if (!s) | |||
| 160 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 160, __func__); | |||
| 161 | ||||
| 162 | free_and_replace(*j, s)({ free(*j); (*j) = (s); (s) = ((void*)0); 0; }); | |||
| 163 | } | |||
| 164 | ||||
| 165 | if (m->work_dir) { | |||
| 166 | char *s; | |||
| 167 | ||||
| 168 | s = resolve_source_path(dest, m->work_dir); | |||
| 169 | if (!s) | |||
| 170 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 170, __func__); | |||
| 171 | ||||
| 172 | free_and_replace(m->work_dir, s)({ free(m->work_dir); (m->work_dir) = (s); (s) = ((void *)0); 0; }); | |||
| 173 | } else { | |||
| 174 | assert(m->source)do { if ((__builtin_expect(!!(!(m->source)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m->source"), "../src/nspawn/nspawn-mount.c" , 174, __PRETTY_FUNCTION__); } while (0); | |||
| 175 | ||||
| 176 | r = tempfn_random(m->source, NULL((void*)0), &m->work_dir); | |||
| 177 | if (r < 0) | |||
| 178 | return log_error_errno(r, "Failed to acquire working directory: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 178, __func__, "Failed to acquire working directory: %m" ) : -abs(_e); }); | |||
| 179 | } | |||
| 180 | ||||
| 181 | (void) mkdir_label(m->work_dir, 0700); | |||
| 182 | } | |||
| 183 | } | |||
| 184 | ||||
| 185 | return 0; | |||
| 186 | } | |||
| 187 | ||||
| 188 | int bind_mount_parse(CustomMount **l, size_t *n, const char *s, bool_Bool read_only) { | |||
| 189 | _cleanup_free___attribute__((cleanup(freep))) char *source = NULL((void*)0), *destination = NULL((void*)0), *opts = NULL((void*)0); | |||
| 190 | const char *p = s; | |||
| 191 | CustomMount *m; | |||
| 192 | int r; | |||
| 193 | ||||
| 194 | assert(l)do { if ((__builtin_expect(!!(!(l)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("l"), "../src/nspawn/nspawn-mount.c", 194 , __PRETTY_FUNCTION__); } while (0); | |||
| 195 | assert(n)do { if ((__builtin_expect(!!(!(n)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("n"), "../src/nspawn/nspawn-mount.c", 195 , __PRETTY_FUNCTION__); } while (0); | |||
| 196 | ||||
| 197 | r = extract_many_words(&p, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL((void*)0)); | |||
| 198 | if (r < 0) | |||
| 199 | return r; | |||
| 200 | if (r == 0) | |||
| 201 | return -EINVAL22; | |||
| 202 | if (r == 1) { | |||
| 203 | destination = strdup(source[0] == '+' ? source+1 : source); | |||
| 204 | if (!destination) | |||
| 205 | return -ENOMEM12; | |||
| 206 | } | |||
| 207 | if (r == 2 && !isempty(p)) { | |||
| 208 | opts = strdup(p); | |||
| 209 | if (!opts) | |||
| 210 | return -ENOMEM12; | |||
| 211 | } | |||
| 212 | ||||
| 213 | if (isempty(source)) | |||
| 214 | source = NULL((void*)0); | |||
| 215 | else if (!source_path_is_valid(source)) | |||
| 216 | return -EINVAL22; | |||
| 217 | ||||
| 218 | if (!path_is_absolute(destination)) | |||
| 219 | return -EINVAL22; | |||
| 220 | ||||
| 221 | m = custom_mount_add(l, n, CUSTOM_MOUNT_BIND); | |||
| 222 | if (!m) | |||
| 223 | return -ENOMEM12; | |||
| 224 | ||||
| 225 | m->source = source; | |||
| 226 | m->destination = destination; | |||
| 227 | m->read_only = read_only; | |||
| 228 | m->options = opts; | |||
| 229 | ||||
| 230 | source = destination = opts = NULL((void*)0); | |||
| 231 | return 0; | |||
| 232 | } | |||
| 233 | ||||
| 234 | int tmpfs_mount_parse(CustomMount **l, size_t *n, const char *s) { | |||
| 235 | _cleanup_free___attribute__((cleanup(freep))) char *path = NULL((void*)0), *opts = NULL((void*)0); | |||
| 236 | const char *p = s; | |||
| 237 | CustomMount *m; | |||
| 238 | int r; | |||
| 239 | ||||
| 240 | assert(l)do { if ((__builtin_expect(!!(!(l)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("l"), "../src/nspawn/nspawn-mount.c", 240 , __PRETTY_FUNCTION__); } while (0); | |||
| 241 | assert(n)do { if ((__builtin_expect(!!(!(n)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("n"), "../src/nspawn/nspawn-mount.c", 241 , __PRETTY_FUNCTION__); } while (0); | |||
| 242 | assert(s)do { if ((__builtin_expect(!!(!(s)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("s"), "../src/nspawn/nspawn-mount.c", 242 , __PRETTY_FUNCTION__); } while (0); | |||
| 243 | ||||
| 244 | r = extract_first_word(&p, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS); | |||
| 245 | if (r < 0) | |||
| 246 | return r; | |||
| 247 | if (r == 0) | |||
| 248 | return -EINVAL22; | |||
| 249 | ||||
| 250 | if (isempty(p)) | |||
| 251 | opts = strdup("mode=0755"); | |||
| 252 | else | |||
| 253 | opts = strdup(p); | |||
| 254 | if (!opts) | |||
| 255 | return -ENOMEM12; | |||
| 256 | ||||
| 257 | if (!path_is_absolute(path)) | |||
| 258 | return -EINVAL22; | |||
| 259 | ||||
| 260 | m = custom_mount_add(l, n, CUSTOM_MOUNT_TMPFS); | |||
| 261 | if (!m) | |||
| 262 | return -ENOMEM12; | |||
| 263 | ||||
| 264 | m->destination = TAKE_PTR(path)({ typeof(path) _ptr_ = (path); (path) = ((void*)0); _ptr_; } ); | |||
| 265 | m->options = TAKE_PTR(opts)({ typeof(opts) _ptr_ = (opts); (opts) = ((void*)0); _ptr_; } ); | |||
| 266 | ||||
| 267 | return 0; | |||
| 268 | } | |||
| 269 | ||||
| 270 | int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool_Bool read_only) { | |||
| 271 | _cleanup_free___attribute__((cleanup(freep))) char *upper = NULL((void*)0), *destination = NULL((void*)0); | |||
| 272 | _cleanup_strv_free___attribute__((cleanup(strv_freep))) char **lower = NULL((void*)0); | |||
| 273 | CustomMount *m; | |||
| 274 | int k; | |||
| 275 | ||||
| 276 | k = strv_split_extract(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS); | |||
| 277 | if (k < 0) | |||
| ||||
| 278 | return k; | |||
| 279 | if (k < 2) | |||
| 280 | return -EADDRNOTAVAIL99; | |||
| 281 | if (k == 2) { | |||
| 282 | /* If two parameters are specified, the first one is the lower, the second one the upper directory. And | |||
| 283 | * we'll also define the destination mount point the same as the upper. */ | |||
| 284 | ||||
| 285 | if (!source_path_is_valid(lower[0]) || | |||
| 286 | !source_path_is_valid(lower[1])) | |||
| 287 | return -EINVAL22; | |||
| 288 | ||||
| 289 | upper = TAKE_PTR(lower[1])({ typeof(lower[1]) _ptr_ = (lower[1]); (lower[1]) = ((void*) 0); _ptr_; }); | |||
| 290 | ||||
| 291 | destination = strdup(upper[0] == '+' ? upper+1 : upper); /* take the destination without "+" prefix */ | |||
| 292 | if (!destination) | |||
| 293 | return -ENOMEM12; | |||
| 294 | } else { | |||
| 295 | char **i; | |||
| 296 | ||||
| 297 | /* If more than two parameters are specified, the last one is the destination, the second to last one | |||
| 298 | * the "upper", and all before that the "lower" directories. */ | |||
| 299 | ||||
| 300 | destination = lower[k - 1]; | |||
| 301 | upper = TAKE_PTR(lower[k - 2])({ typeof(lower[k - 2]) _ptr_ = (lower[k - 2]); (lower[k - 2] ) = ((void*)0); _ptr_; }); | |||
| 302 | ||||
| 303 | STRV_FOREACH(i, lower)for ((i) = (lower); (i) && *(i); (i)++) | |||
| 304 | if (!source_path_is_valid(*i)) | |||
| 305 | return -EINVAL22; | |||
| 306 | ||||
| 307 | /* If the upper directory is unspecified, then let's create it automatically as a throw-away directory | |||
| 308 | * in /var/tmp */ | |||
| 309 | if (isempty(upper)) | |||
| 310 | upper = NULL((void*)0); | |||
| 311 | else if (!source_path_is_valid(upper)) | |||
| 312 | return -EINVAL22; | |||
| 313 | ||||
| 314 | if (!path_is_absolute(destination)) | |||
| 315 | return -EINVAL22; | |||
| 316 | } | |||
| 317 | ||||
| 318 | m = custom_mount_add(l, n, CUSTOM_MOUNT_OVERLAY); | |||
| 319 | if (!m
| |||
| 320 | return -ENOMEM12; | |||
| ||||
| 321 | ||||
| 322 | m->destination = TAKE_PTR(destination)({ typeof(destination) _ptr_ = (destination); (destination) = ((void*)0); _ptr_; }); | |||
| 323 | m->source = TAKE_PTR(upper)({ typeof(upper) _ptr_ = (upper); (upper) = ((void*)0); _ptr_ ; }); | |||
| 324 | m->lower = TAKE_PTR(lower)({ typeof(lower) _ptr_ = (lower); (lower) = ((void*)0); _ptr_ ; }); | |||
| 325 | m->read_only = read_only; | |||
| 326 | ||||
| 327 | return 0; | |||
| 328 | } | |||
| 329 | ||||
| 330 | static int tmpfs_patch_options( | |||
| 331 | const char *options, | |||
| 332 | bool_Bool userns, | |||
| 333 | uid_t uid_shift, uid_t uid_range, | |||
| 334 | bool_Bool patch_ids, | |||
| 335 | const char *selinux_apifs_context, | |||
| 336 | char **ret) { | |||
| 337 | ||||
| 338 | char *buf = NULL((void*)0); | |||
| 339 | ||||
| 340 | if ((userns && uid_shift != 0) || patch_ids) { | |||
| 341 | assert(uid_shift != UID_INVALID)do { if ((__builtin_expect(!!(!(uid_shift != ((uid_t) -1))),0 ))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("uid_shift != UID_INVALID" ), "../src/nspawn/nspawn-mount.c", 341, __PRETTY_FUNCTION__); } while (0); | |||
| 342 | ||||
| 343 | if (asprintf(&buf, "%s%suid=" UID_FMT"%" "u" ",gid=" UID_FMT"%" "u", | |||
| 344 | strempty(options), options ? "," : "", | |||
| 345 | uid_shift, uid_shift) < 0) | |||
| 346 | return -ENOMEM12; | |||
| 347 | ||||
| 348 | options = buf; | |||
| 349 | } | |||
| 350 | ||||
| 351 | #if HAVE_SELINUX1 | |||
| 352 | if (selinux_apifs_context) { | |||
| 353 | char *t; | |||
| 354 | ||||
| 355 | t = strjoin(strempty(options), options ? "," : "",strjoin_real((strempty(options)), options ? "," : "", "context=\"" , selinux_apifs_context, "\"", ((void*)0)) | |||
| 356 | "context=\"", selinux_apifs_context, "\"")strjoin_real((strempty(options)), options ? "," : "", "context=\"" , selinux_apifs_context, "\"", ((void*)0)); | |||
| 357 | free(buf); | |||
| 358 | if (!t) | |||
| 359 | return -ENOMEM12; | |||
| 360 | ||||
| 361 | buf = t; | |||
| 362 | } | |||
| 363 | #endif | |||
| 364 | ||||
| 365 | if (!buf && options) { | |||
| 366 | buf = strdup(options); | |||
| 367 | if (!buf) | |||
| 368 | return -ENOMEM12; | |||
| 369 | } | |||
| 370 | *ret = buf; | |||
| 371 | ||||
| 372 | return !!buf; | |||
| 373 | } | |||
| 374 | ||||
| 375 | int mount_sysfs(const char *dest, MountSettingsMask mount_settings) { | |||
| 376 | const char *full, *top, *x; | |||
| 377 | int r; | |||
| 378 | unsigned long extra_flags = 0; | |||
| 379 | ||||
| 380 | top = prefix_roota(dest, "/sys")({ const char* _path = ("/sys"), *_root = (dest), *_ret; char *_p, *_n; size_t _l; while (_path[0] == '/' && _path [1] == '/') _path ++; if (empty_or_root(_root)) _ret = _path; else { _l = strlen(_root) + 1 + strlen(_path) + 1; _n = __builtin_alloca (_l); _p = stpcpy(_n, _root); while (_p > _n && _p [-1] == '/') _p--; if (_path[0] != '/') *(_p++) = '/'; strcpy (_p, _path); _ret = _n; } _ret; }); | |||
| 381 | r = path_is_fs_type(top, SYSFS_MAGIC0x62656572); | |||
| 382 | if (r < 0) | |||
| 383 | return log_error_errno(r, "Failed to determine filesystem type of %s: %m", top)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 383, __func__, "Failed to determine filesystem type of %s: %m" , top) : -abs(_e); }); | |||
| 384 | /* /sys might already be mounted as sysfs by the outer child in the | |||
| 385 | * !netns case. In this case, it's all good. Don't touch it because we | |||
| 386 | * don't have the right to do so, see https://github.com/systemd/systemd/issues/1555. | |||
| 387 | */ | |||
| 388 | if (r > 0) | |||
| 389 | return 0; | |||
| 390 | ||||
| 391 | full = prefix_roota(top, "/full")({ const char* _path = ("/full"), *_root = (top), *_ret; char *_p, *_n; size_t _l; while (_path[0] == '/' && _path [1] == '/') _path ++; if (empty_or_root(_root)) _ret = _path; else { _l = strlen(_root) + 1 + strlen(_path) + 1; _n = __builtin_alloca (_l); _p = stpcpy(_n, _root); while (_p > _n && _p [-1] == '/') _p--; if (_path[0] != '/') *(_p++) = '/'; strcpy (_p, _path); _ret = _n; } _ret; }); | |||
| 392 | ||||
| 393 | (void) mkdir(full, 0755); | |||
| 394 | ||||
| 395 | if (mount_settings & MOUNT_APPLY_APIVFS_RO) | |||
| 396 | extra_flags |= MS_RDONLYMS_RDONLY; | |||
| 397 | ||||
| 398 | r = mount_verbose(LOG_ERR3, "sysfs", full, "sysfs", | |||
| 399 | MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV|extra_flags, NULL((void*)0)); | |||
| 400 | if (r < 0) | |||
| 401 | return r; | |||
| 402 | ||||
| 403 | FOREACH_STRING(x, "block", "bus", "class", "dev", "devices", "kernel")for (char **_l = ({ char **_ll = ((char**) ((const char*[]) { "block", "bus", "class", "dev", "devices", "kernel", ((void* )0) })); x = _ll ? _ll[0] : ((void*)0); _ll; }); _l && *_l; x = ({ _l ++; _l[0]; })) { | |||
| 404 | _cleanup_free___attribute__((cleanup(freep))) char *from = NULL((void*)0), *to = NULL((void*)0); | |||
| 405 | ||||
| 406 | from = prefix_root(full, x); | |||
| 407 | if (!from) | |||
| 408 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 408, __func__); | |||
| 409 | ||||
| 410 | to = prefix_root(top, x); | |||
| 411 | if (!to) | |||
| 412 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 412, __func__); | |||
| 413 | ||||
| 414 | (void) mkdir(to, 0755); | |||
| 415 | ||||
| 416 | r = mount_verbose(LOG_ERR3, from, to, NULL((void*)0), MS_BINDMS_BIND, NULL((void*)0)); | |||
| 417 | if (r < 0) | |||
| 418 | return r; | |||
| 419 | ||||
| 420 | r = mount_verbose(LOG_ERR3, NULL((void*)0), to, NULL((void*)0), | |||
| 421 | MS_BINDMS_BIND|MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV|MS_REMOUNTMS_REMOUNT|extra_flags, NULL((void*)0)); | |||
| 422 | if (r < 0) | |||
| 423 | return r; | |||
| 424 | } | |||
| 425 | ||||
| 426 | r = umount_verbose(full); | |||
| 427 | if (r < 0) | |||
| 428 | return r; | |||
| 429 | ||||
| 430 | if (rmdir(full) < 0) | |||
| 431 | return log_error_errno(errno, "Failed to remove %s: %m", full)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/nspawn/nspawn-mount.c", 431, __func__ , "Failed to remove %s: %m", full) : -abs(_e); }); | |||
| 432 | ||||
| 433 | /* Create mountpoint for cgroups. Otherwise we are not allowed since we | |||
| 434 | * remount /sys read-only. | |||
| 435 | */ | |||
| 436 | if (cg_ns_supported()) { | |||
| 437 | x = prefix_roota(top, "/fs/cgroup")({ const char* _path = ("/fs/cgroup"), *_root = (top), *_ret; char *_p, *_n; size_t _l; while (_path[0] == '/' && _path [1] == '/') _path ++; if (empty_or_root(_root)) _ret = _path; else { _l = strlen(_root) + 1 + strlen(_path) + 1; _n = __builtin_alloca (_l); _p = stpcpy(_n, _root); while (_p > _n && _p [-1] == '/') _p--; if (_path[0] != '/') *(_p++) = '/'; strcpy (_p, _path); _ret = _n; } _ret; }); | |||
| 438 | (void) mkdir_p(x, 0755); | |||
| 439 | } | |||
| 440 | ||||
| 441 | return mount_verbose(LOG_ERR3, NULL((void*)0), top, NULL((void*)0), | |||
| 442 | MS_BINDMS_BIND|MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV|MS_REMOUNTMS_REMOUNT|extra_flags, NULL((void*)0)); | |||
| 443 | } | |||
| 444 | ||||
| 445 | static int mkdir_userns(const char *path, mode_t mode, MountSettingsMask mask, uid_t uid_shift) { | |||
| 446 | int r; | |||
| 447 | ||||
| 448 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/nspawn/nspawn-mount.c", 448, __PRETTY_FUNCTION__); } while (0); | |||
| 449 | ||||
| 450 | r = mkdir_errno_wrapper(path, mode); | |||
| 451 | if (r < 0 && r != -EEXIST17) | |||
| 452 | return r; | |||
| 453 | ||||
| 454 | if ((mask & MOUNT_USE_USERNS) == 0) | |||
| 455 | return 0; | |||
| 456 | ||||
| 457 | if (mask & MOUNT_IN_USERNS) | |||
| 458 | return 0; | |||
| 459 | ||||
| 460 | if (lchown(path, uid_shift, uid_shift) < 0) | |||
| 461 | return -errno(*__errno_location ()); | |||
| 462 | ||||
| 463 | return 0; | |||
| 464 | } | |||
| 465 | ||||
| 466 | static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, MountSettingsMask mask, uid_t uid_shift) { | |||
| 467 | const char *p, *e; | |||
| 468 | int r; | |||
| 469 | ||||
| 470 | assert(path)do { if ((__builtin_expect(!!(!(path)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("path"), "../src/nspawn/nspawn-mount.c", 470, __PRETTY_FUNCTION__); } while (0); | |||
| 471 | ||||
| 472 | if (prefix && !path_startswith(path, prefix)) | |||
| 473 | return -ENOTDIR20; | |||
| 474 | ||||
| 475 | /* create every parent directory in the path, except the last component */ | |||
| 476 | p = path + strspn(path, "/"); | |||
| 477 | for (;;) { | |||
| 478 | char t[strlen(path) + 1]; | |||
| 479 | ||||
| 480 | e = p + strcspn(p, "/"); | |||
| 481 | p = e + strspn(e, "/"); | |||
| 482 | ||||
| 483 | /* Is this the last component? If so, then we're done */ | |||
| 484 | if (*p == 0) | |||
| 485 | break; | |||
| 486 | ||||
| 487 | memcpy(t, path, e - path); | |||
| 488 | t[e-path] = 0; | |||
| 489 | ||||
| 490 | if (prefix && path_startswith(prefix, t)) | |||
| 491 | continue; | |||
| 492 | ||||
| 493 | r = mkdir_userns(t, mode, mask, uid_shift); | |||
| 494 | if (r < 0) | |||
| 495 | return r; | |||
| 496 | } | |||
| 497 | ||||
| 498 | return mkdir_userns(path, mode, mask, uid_shift); | |||
| 499 | } | |||
| 500 | ||||
| 501 | int mount_all(const char *dest, | |||
| 502 | MountSettingsMask mount_settings, | |||
| 503 | uid_t uid_shift, uid_t uid_range, | |||
| 504 | const char *selinux_apifs_context) { | |||
| 505 | ||||
| 506 | #define PROC_INACCESSIBLE(path){ ((void*)0), (path), ((void*)0), ((void*)0), MS_BIND, MOUNT_IN_USERNS |MOUNT_APPLY_APIVFS_RO|MOUNT_INACCESSIBLE_REG }, { ((void*)0) , (path), ((void*)0), ((void*)0), MS_BIND|MS_RDONLY|MS_NOSUID |MS_NOEXEC|MS_NODEV|MS_REMOUNT, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO } \ | |||
| 507 | { NULL((void*)0), (path), NULL((void*)0), NULL((void*)0), MS_BINDMS_BIND, \ | |||
| 508 | MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO|MOUNT_INACCESSIBLE_REG }, /* Bind mount first ... */ \ | |||
| 509 | { NULL((void*)0), (path), NULL((void*)0), NULL((void*)0), MS_BINDMS_BIND|MS_RDONLYMS_RDONLY|MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV|MS_REMOUNTMS_REMOUNT, \ | |||
| 510 | MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO } /* Then, make it r/o */ | |||
| 511 | ||||
| 512 | #define PROC_READ_ONLY(path){ (path), (path), ((void*)0), ((void*)0), MS_BIND, MOUNT_IN_USERNS |MOUNT_APPLY_APIVFS_RO }, { ((void*)0), (path), ((void*)0), ( (void*)0), MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT , MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO } \ | |||
| 513 | { (path), (path), NULL((void*)0), NULL((void*)0), MS_BINDMS_BIND, \ | |||
| 514 | MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* Bind mount first ... */ \ | |||
| 515 | { NULL((void*)0), (path), NULL((void*)0), NULL((void*)0), MS_BINDMS_BIND|MS_RDONLYMS_RDONLY|MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV|MS_REMOUNTMS_REMOUNT, \ | |||
| 516 | MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO } /* Then, make it r/o */ | |||
| 517 | ||||
| 518 | typedef struct MountPoint { | |||
| 519 | const char *what; | |||
| 520 | const char *where; | |||
| 521 | const char *type; | |||
| 522 | const char *options; | |||
| 523 | unsigned long flags; | |||
| 524 | MountSettingsMask mount_settings; | |||
| 525 | } MountPoint; | |||
| 526 | ||||
| 527 | static const MountPoint mount_table[] = { | |||
| 528 | /* First we list inner child mounts (i.e. mounts applied *after* entering user namespacing) */ | |||
| 529 | { "proc", "/proc", "proc", NULL((void*)0), MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV, | |||
| 530 | MOUNT_FATAL|MOUNT_IN_USERNS }, | |||
| 531 | ||||
| 532 | { "/proc/sys", "/proc/sys", NULL((void*)0), NULL((void*)0), MS_BINDMS_BIND, | |||
| 533 | MOUNT_FATAL|MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* Bind mount first ... */ | |||
| 534 | ||||
| 535 | { "/proc/sys/net", "/proc/sys/net", NULL((void*)0), NULL((void*)0), MS_BINDMS_BIND, | |||
| 536 | MOUNT_FATAL|MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO|MOUNT_APPLY_APIVFS_NETNS }, /* (except for this) */ | |||
| 537 | ||||
| 538 | { NULL((void*)0), "/proc/sys", NULL((void*)0), NULL((void*)0), MS_BINDMS_BIND|MS_RDONLYMS_RDONLY|MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV|MS_REMOUNTMS_REMOUNT, | |||
| 539 | MOUNT_FATAL|MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* ... then, make it r/o */ | |||
| 540 | ||||
| 541 | /* Make these files inaccessible to container payloads: they potentially leak information about kernel | |||
| 542 | * internals or the host's execution environment to the container */ | |||
| 543 | PROC_INACCESSIBLE("/proc/kallsyms"){ ((void*)0), ("/proc/kallsyms"), ((void*)0), ((void*)0), MS_BIND , MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO|MOUNT_INACCESSIBLE_REG }, { ((void*)0), ("/proc/kallsyms"), ((void*)0), ((void*)0), MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, MOUNT_IN_USERNS |MOUNT_APPLY_APIVFS_RO }, | |||
| 544 | PROC_INACCESSIBLE("/proc/kcore"){ ((void*)0), ("/proc/kcore"), ((void*)0), ((void*)0), MS_BIND , MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO|MOUNT_INACCESSIBLE_REG }, { ((void*)0), ("/proc/kcore"), ((void*)0), ((void*)0), MS_BIND |MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, MOUNT_IN_USERNS |MOUNT_APPLY_APIVFS_RO }, | |||
| 545 | PROC_INACCESSIBLE("/proc/keys"){ ((void*)0), ("/proc/keys"), ((void*)0), ((void*)0), MS_BIND , MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO|MOUNT_INACCESSIBLE_REG }, { ((void*)0), ("/proc/keys"), ((void*)0), ((void*)0), MS_BIND |MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, MOUNT_IN_USERNS |MOUNT_APPLY_APIVFS_RO }, | |||
| 546 | PROC_INACCESSIBLE("/proc/sysrq-trigger"){ ((void*)0), ("/proc/sysrq-trigger"), ((void*)0), ((void*)0) , MS_BIND, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO|MOUNT_INACCESSIBLE_REG }, { ((void*)0), ("/proc/sysrq-trigger"), ((void*)0), ((void *)0), MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT , MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, | |||
| 547 | PROC_INACCESSIBLE("/proc/timer_list"){ ((void*)0), ("/proc/timer_list"), ((void*)0), ((void*)0), MS_BIND , MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO|MOUNT_INACCESSIBLE_REG }, { ((void*)0), ("/proc/timer_list"), ((void*)0), ((void*)0 ), MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, | |||
| 548 | ||||
| 549 | /* Make these directories read-only to container payloads: they show hardware information, and in some | |||
| 550 | * cases contain tunables the container really shouldn't have access to. */ | |||
| 551 | PROC_READ_ONLY("/proc/acpi"){ ("/proc/acpi"), ("/proc/acpi"), ((void*)0), ((void*)0), MS_BIND , MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, { ((void*)0), ("/proc/acpi" ), ((void*)0), ((void*)0), MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC |MS_NODEV|MS_REMOUNT, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, | |||
| 552 | PROC_READ_ONLY("/proc/apm"){ ("/proc/apm"), ("/proc/apm"), ((void*)0), ((void*)0), MS_BIND , MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, { ((void*)0), ("/proc/apm" ), ((void*)0), ((void*)0), MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC |MS_NODEV|MS_REMOUNT, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, | |||
| 553 | PROC_READ_ONLY("/proc/asound"){ ("/proc/asound"), ("/proc/asound"), ((void*)0), ((void*)0), MS_BIND, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, { ((void*) 0), ("/proc/asound"), ((void*)0), ((void*)0), MS_BIND|MS_RDONLY |MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, | |||
| 554 | PROC_READ_ONLY("/proc/bus"){ ("/proc/bus"), ("/proc/bus"), ((void*)0), ((void*)0), MS_BIND , MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, { ((void*)0), ("/proc/bus" ), ((void*)0), ((void*)0), MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC |MS_NODEV|MS_REMOUNT, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, | |||
| 555 | PROC_READ_ONLY("/proc/fs"){ ("/proc/fs"), ("/proc/fs"), ((void*)0), ((void*)0), MS_BIND , MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, { ((void*)0), ("/proc/fs" ), ((void*)0), ((void*)0), MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC |MS_NODEV|MS_REMOUNT, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, | |||
| 556 | PROC_READ_ONLY("/proc/irq"){ ("/proc/irq"), ("/proc/irq"), ((void*)0), ((void*)0), MS_BIND , MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, { ((void*)0), ("/proc/irq" ), ((void*)0), ((void*)0), MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC |MS_NODEV|MS_REMOUNT, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, | |||
| 557 | PROC_READ_ONLY("/proc/scsi"){ ("/proc/scsi"), ("/proc/scsi"), ((void*)0), ((void*)0), MS_BIND , MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, { ((void*)0), ("/proc/scsi" ), ((void*)0), ((void*)0), MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC |MS_NODEV|MS_REMOUNT, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, | |||
| 558 | ||||
| 559 | /* Then we list outer child mounts (i.e. mounts applied *before* entering user namespacing) */ | |||
| 560 | { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_NOSUIDMS_NOSUID|MS_NODEVMS_NODEV|MS_STRICTATIMEMS_STRICTATIME, | |||
| 561 | MOUNT_FATAL }, | |||
| 562 | { "tmpfs", "/sys", "tmpfs", "mode=755", MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV, | |||
| 563 | MOUNT_FATAL|MOUNT_APPLY_APIVFS_NETNS }, | |||
| 564 | { "sysfs", "/sys", "sysfs", NULL((void*)0), MS_RDONLYMS_RDONLY|MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV, | |||
| 565 | MOUNT_FATAL|MOUNT_APPLY_APIVFS_RO }, /* skipped if above was mounted */ | |||
| 566 | { "sysfs", "/sys", "sysfs", NULL((void*)0), MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV, | |||
| 567 | MOUNT_FATAL }, /* skipped if above was mounted */ | |||
| 568 | { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUIDMS_NOSUID|MS_STRICTATIMEMS_STRICTATIME, | |||
| 569 | MOUNT_FATAL }, | |||
| 570 | { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUIDMS_NOSUID|MS_NODEVMS_NODEV|MS_STRICTATIMEMS_STRICTATIME, | |||
| 571 | MOUNT_FATAL }, | |||
| 572 | { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUIDMS_NOSUID|MS_NODEVMS_NODEV|MS_STRICTATIMEMS_STRICTATIME, | |||
| 573 | MOUNT_FATAL }, | |||
| 574 | ||||
| 575 | #if HAVE_SELINUX1 | |||
| 576 | { "/sys/fs/selinux", "/sys/fs/selinux", NULL((void*)0), NULL((void*)0), MS_BINDMS_BIND, | |||
| 577 | 0 }, /* Bind mount first */ | |||
| 578 | { NULL((void*)0), "/sys/fs/selinux", NULL((void*)0), NULL((void*)0), MS_BINDMS_BIND|MS_RDONLYMS_RDONLY|MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV|MS_REMOUNTMS_REMOUNT, | |||
| 579 | 0 }, /* Then, make it r/o */ | |||
| 580 | #endif | |||
| 581 | }; | |||
| 582 | ||||
| 583 | _cleanup_(unlink_and_freep)__attribute__((cleanup(unlink_and_freep))) char *inaccessible = NULL((void*)0); | |||
| 584 | bool_Bool use_userns = (mount_settings & MOUNT_USE_USERNS); | |||
| 585 | bool_Bool netns = (mount_settings & MOUNT_APPLY_APIVFS_NETNS); | |||
| 586 | bool_Bool ro = (mount_settings & MOUNT_APPLY_APIVFS_RO); | |||
| 587 | bool_Bool in_userns = (mount_settings & MOUNT_IN_USERNS); | |||
| 588 | size_t k; | |||
| 589 | int r; | |||
| 590 | ||||
| 591 | for (k = 0; k < ELEMENTSOF(mount_table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(mount_table), typeof(&*(mount_table))), sizeof(mount_table )/sizeof((mount_table)[0]), ((void)0))); k++) { | |||
| 592 | _cleanup_free___attribute__((cleanup(freep))) char *where = NULL((void*)0), *options = NULL((void*)0); | |||
| 593 | const char *o, *what; | |||
| 594 | bool_Bool fatal = (mount_table[k].mount_settings & MOUNT_FATAL); | |||
| 595 | ||||
| 596 | if (in_userns != (bool_Bool)(mount_table[k].mount_settings & MOUNT_IN_USERNS)) | |||
| 597 | continue; | |||
| 598 | ||||
| 599 | if (!netns && (bool_Bool)(mount_table[k].mount_settings & MOUNT_APPLY_APIVFS_NETNS)) | |||
| 600 | continue; | |||
| 601 | ||||
| 602 | if (!ro && (bool_Bool)(mount_table[k].mount_settings & MOUNT_APPLY_APIVFS_RO)) | |||
| 603 | continue; | |||
| 604 | ||||
| 605 | r = chase_symlinks(mount_table[k].where, dest, CHASE_NONEXISTENT|CHASE_PREFIX_ROOT, &where); | |||
| 606 | if (r < 0) | |||
| 607 | return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, mount_table[k].where)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 607, __func__, "Failed to resolve %s/%s: %m" , dest, mount_table[k].where) : -abs(_e); }); | |||
| 608 | ||||
| 609 | if (mount_table[k].mount_settings & MOUNT_INACCESSIBLE_REG) { | |||
| 610 | ||||
| 611 | if (!inaccessible) { | |||
| 612 | _cleanup_free___attribute__((cleanup(freep))) char *np = NULL((void*)0); | |||
| 613 | ||||
| 614 | r = tempfn_random_child(NULL((void*)0), "inaccessible", &np); | |||
| 615 | if (r < 0) | |||
| 616 | return log_error_errno(r, "Failed to generate inaccessible file node path: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 616, __func__, "Failed to generate inaccessible file node path: %m" ) : -abs(_e); }); | |||
| 617 | ||||
| 618 | r = touch_file(np, false0, USEC_INFINITY((usec_t) -1), UID_INVALID((uid_t) -1), GID_INVALID((gid_t) -1), 0000); | |||
| 619 | if (r < 0) | |||
| 620 | return log_error_errno(r, "Failed to create inaccessible file node '%s': %m", np)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 620, __func__, "Failed to create inaccessible file node '%s': %m" , np) : -abs(_e); }); | |||
| 621 | ||||
| 622 | inaccessible = TAKE_PTR(np)({ typeof(np) _ptr_ = (np); (np) = ((void*)0); _ptr_; }); | |||
| 623 | } | |||
| 624 | ||||
| 625 | what = inaccessible; | |||
| 626 | } else | |||
| 627 | what = mount_table[k].what; | |||
| 628 | ||||
| 629 | r = path_is_mount_point(where, NULL((void*)0), 0); | |||
| 630 | if (r < 0 && r != -ENOENT2) | |||
| 631 | return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 631, __func__, "Failed to detect whether %s is a mount point: %m" , where) : -abs(_e); }); | |||
| 632 | ||||
| 633 | /* Skip this entry if it is not a remount. */ | |||
| 634 | if (what && r > 0) | |||
| 635 | continue; | |||
| 636 | ||||
| 637 | r = mkdir_userns_p(dest, where, 0755, mount_settings, uid_shift); | |||
| 638 | if (r < 0 && r != -EEXIST17) { | |||
| 639 | if (fatal && r != -EROFS30) | |||
| 640 | return log_error_errno(r, "Failed to create directory %s: %m", where)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 640, __func__, "Failed to create directory %s: %m" , where) : -abs(_e); }); | |||
| 641 | ||||
| 642 | log_debug_errno(r, "Failed to create directory %s: %m", where)({ 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/nspawn/nspawn-mount.c", 642, __func__, "Failed to create directory %s: %m" , where) : -abs(_e); }); | |||
| 643 | /* If we failed mkdir() or chown() due to the root | |||
| 644 | * directory being read only, attempt to mount this fs | |||
| 645 | * anyway and let mount_verbose log any errors */ | |||
| 646 | if (r != -EROFS30) | |||
| 647 | continue; | |||
| 648 | } | |||
| 649 | ||||
| 650 | o = mount_table[k].options; | |||
| 651 | if (streq_ptr(mount_table[k].type, "tmpfs")) { | |||
| 652 | if (in_userns) | |||
| 653 | r = tmpfs_patch_options(o, use_userns, 0, uid_range, true1, selinux_apifs_context, &options); | |||
| 654 | else | |||
| 655 | r = tmpfs_patch_options(o, use_userns, uid_shift, uid_range, false0, selinux_apifs_context, &options); | |||
| 656 | if (r < 0) | |||
| 657 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 657, __func__); | |||
| 658 | if (r > 0) | |||
| 659 | o = options; | |||
| 660 | } | |||
| 661 | ||||
| 662 | r = mount_verbose(fatal ? LOG_ERR3 : LOG_DEBUG7, | |||
| 663 | what, | |||
| 664 | where, | |||
| 665 | mount_table[k].type, | |||
| 666 | mount_table[k].flags, | |||
| 667 | o); | |||
| 668 | if (r < 0 && fatal) | |||
| 669 | return r; | |||
| 670 | } | |||
| 671 | ||||
| 672 | return 0; | |||
| 673 | } | |||
| 674 | ||||
| 675 | static int mount_bind(const char *dest, CustomMount *m) { | |||
| 676 | ||||
| 677 | _cleanup_free___attribute__((cleanup(freep))) char *where = NULL((void*)0); | |||
| 678 | struct stat source_st, dest_st; | |||
| 679 | int r; | |||
| 680 | ||||
| 681 | assert(dest)do { if ((__builtin_expect(!!(!(dest)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("dest"), "../src/nspawn/nspawn-mount.c", 681, __PRETTY_FUNCTION__); } while (0); | |||
| 682 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/nspawn/nspawn-mount.c", 682 , __PRETTY_FUNCTION__); } while (0); | |||
| 683 | ||||
| 684 | if (stat(m->source, &source_st) < 0) | |||
| 685 | return log_error_errno(errno, "Failed to stat %s: %m", m->source)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/nspawn/nspawn-mount.c", 685, __func__ , "Failed to stat %s: %m", m->source) : -abs(_e); }); | |||
| 686 | ||||
| 687 | r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where); | |||
| 688 | if (r < 0) | |||
| 689 | return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 689, __func__, "Failed to resolve %s/%s: %m" , dest, m->destination) : -abs(_e); }); | |||
| 690 | if (r > 0) { /* Path exists already? */ | |||
| 691 | ||||
| 692 | if (stat(where, &dest_st) < 0) | |||
| 693 | return log_error_errno(errno, "Failed to stat %s: %m", where)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/nspawn/nspawn-mount.c", 693, __func__ , "Failed to stat %s: %m", where) : -abs(_e); }); | |||
| 694 | ||||
| 695 | if (S_ISDIR(source_st.st_mode)((((source_st.st_mode)) & 0170000) == (0040000)) && !S_ISDIR(dest_st.st_mode)((((dest_st.st_mode)) & 0170000) == (0040000))) { | |||
| 696 | log_error("Cannot bind mount directory %s on file %s.", m->source, where)({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 696, __func__, "Cannot bind mount directory %s on file %s." , m->source, where) : -abs(_e); }); | |||
| 697 | return -EINVAL22; | |||
| 698 | } | |||
| 699 | ||||
| 700 | if (!S_ISDIR(source_st.st_mode)((((source_st.st_mode)) & 0170000) == (0040000)) && S_ISDIR(dest_st.st_mode)((((dest_st.st_mode)) & 0170000) == (0040000))) { | |||
| 701 | log_error("Cannot bind mount file %s on directory %s.", m->source, where)({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 701, __func__, "Cannot bind mount file %s on directory %s." , m->source, where) : -abs(_e); }); | |||
| 702 | return -EINVAL22; | |||
| 703 | } | |||
| 704 | ||||
| 705 | } else { /* Path doesn't exist yet? */ | |||
| 706 | r = mkdir_parents_label(where, 0755); | |||
| 707 | if (r < 0) | |||
| 708 | return log_error_errno(r, "Failed to make parents of %s: %m", where)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 708, __func__, "Failed to make parents of %s: %m" , where) : -abs(_e); }); | |||
| 709 | ||||
| 710 | /* Create the mount point. Any non-directory file can be | |||
| 711 | * mounted on any non-directory file (regular, fifo, socket, | |||
| 712 | * char, block). | |||
| 713 | */ | |||
| 714 | if (S_ISDIR(source_st.st_mode)((((source_st.st_mode)) & 0170000) == (0040000))) | |||
| 715 | r = mkdir_label(where, 0755); | |||
| 716 | else | |||
| 717 | r = touch(where); | |||
| 718 | if (r < 0) | |||
| 719 | return log_error_errno(r, "Failed to create mount point %s: %m", where)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 719, __func__, "Failed to create mount point %s: %m" , where) : -abs(_e); }); | |||
| 720 | ||||
| 721 | } | |||
| 722 | ||||
| 723 | r = mount_verbose(LOG_ERR3, m->source, where, NULL((void*)0), MS_BINDMS_BIND | MS_RECMS_REC, m->options); | |||
| 724 | if (r < 0) | |||
| 725 | return r; | |||
| 726 | ||||
| 727 | if (m->read_only) { | |||
| 728 | r = bind_remount_recursive(where, true1, NULL((void*)0)); | |||
| 729 | if (r < 0) | |||
| 730 | return log_error_errno(r, "Read-only bind mount failed: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 730, __func__, "Read-only bind mount failed: %m" ) : -abs(_e); }); | |||
| 731 | } | |||
| 732 | ||||
| 733 | return 0; | |||
| 734 | } | |||
| 735 | ||||
| 736 | static int mount_tmpfs( | |||
| 737 | const char *dest, | |||
| 738 | CustomMount *m, | |||
| 739 | bool_Bool userns, uid_t uid_shift, uid_t uid_range, | |||
| 740 | const char *selinux_apifs_context) { | |||
| 741 | ||||
| 742 | const char *options; | |||
| 743 | _cleanup_free___attribute__((cleanup(freep))) char *buf = NULL((void*)0), *where = NULL((void*)0); | |||
| 744 | int r; | |||
| 745 | ||||
| 746 | assert(dest)do { if ((__builtin_expect(!!(!(dest)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("dest"), "../src/nspawn/nspawn-mount.c", 746, __PRETTY_FUNCTION__); } while (0); | |||
| 747 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/nspawn/nspawn-mount.c", 747 , __PRETTY_FUNCTION__); } while (0); | |||
| 748 | ||||
| 749 | r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where); | |||
| 750 | if (r < 0) | |||
| 751 | return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 751, __func__, "Failed to resolve %s/%s: %m" , dest, m->destination) : -abs(_e); }); | |||
| 752 | if (r == 0) { /* Doesn't exist yet? */ | |||
| 753 | r = mkdir_p_label(where, 0755); | |||
| 754 | if (r < 0) | |||
| 755 | return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 755, __func__, "Creating mount point for tmpfs %s failed: %m" , where) : -abs(_e); }); | |||
| 756 | } | |||
| 757 | ||||
| 758 | r = tmpfs_patch_options(m->options, userns, uid_shift, uid_range, false0, selinux_apifs_context, &buf); | |||
| 759 | if (r < 0) | |||
| 760 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 760, __func__); | |||
| 761 | options = r > 0 ? buf : m->options; | |||
| 762 | ||||
| 763 | return mount_verbose(LOG_ERR3, "tmpfs", where, "tmpfs", MS_NODEVMS_NODEV|MS_STRICTATIMEMS_STRICTATIME, options); | |||
| 764 | } | |||
| 765 | ||||
| 766 | static char *joined_and_escaped_lower_dirs(char **lower) { | |||
| 767 | _cleanup_strv_free___attribute__((cleanup(strv_freep))) char **sv = NULL((void*)0); | |||
| 768 | ||||
| 769 | sv = strv_copy(lower); | |||
| 770 | if (!sv) | |||
| 771 | return NULL((void*)0); | |||
| 772 | ||||
| 773 | strv_reverse(sv); | |||
| 774 | ||||
| 775 | if (!strv_shell_escape(sv, ",:")) | |||
| 776 | return NULL((void*)0); | |||
| 777 | ||||
| 778 | return strv_join(sv, ":"); | |||
| 779 | } | |||
| 780 | ||||
| 781 | static int mount_overlay(const char *dest, CustomMount *m) { | |||
| 782 | ||||
| 783 | _cleanup_free___attribute__((cleanup(freep))) char *lower = NULL((void*)0), *where = NULL((void*)0), *escaped_source = NULL((void*)0); | |||
| 784 | const char *options; | |||
| 785 | int r; | |||
| 786 | ||||
| 787 | assert(dest)do { if ((__builtin_expect(!!(!(dest)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("dest"), "../src/nspawn/nspawn-mount.c", 787, __PRETTY_FUNCTION__); } while (0); | |||
| 788 | assert(m)do { if ((__builtin_expect(!!(!(m)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("m"), "../src/nspawn/nspawn-mount.c", 788 , __PRETTY_FUNCTION__); } while (0); | |||
| 789 | ||||
| 790 | r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where); | |||
| 791 | if (r < 0) | |||
| 792 | return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 792, __func__, "Failed to resolve %s/%s: %m" , dest, m->destination) : -abs(_e); }); | |||
| 793 | if (r == 0) { /* Doesn't exist yet? */ | |||
| 794 | r = mkdir_label(where, 0755); | |||
| 795 | if (r < 0) | |||
| 796 | return log_error_errno(r, "Creating mount point for overlay %s failed: %m", where)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 796, __func__, "Creating mount point for overlay %s failed: %m" , where) : -abs(_e); }); | |||
| 797 | } | |||
| 798 | ||||
| 799 | (void) mkdir_p_label(m->source, 0755); | |||
| 800 | ||||
| 801 | lower = joined_and_escaped_lower_dirs(m->lower); | |||
| 802 | if (!lower) | |||
| 803 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 803, __func__); | |||
| 804 | ||||
| 805 | escaped_source = shell_escape(m->source, ",:"); | |||
| 806 | if (!escaped_source) | |||
| 807 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 807, __func__); | |||
| 808 | ||||
| 809 | if (m->read_only) | |||
| 810 | options = strjoina("lowerdir=", escaped_source, ":", lower)({ const char *_appendees_[] = { "lowerdir=", escaped_source, ":", lower }; 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_; }); | |||
| 811 | else { | |||
| 812 | _cleanup_free___attribute__((cleanup(freep))) char *escaped_work_dir = NULL((void*)0); | |||
| 813 | ||||
| 814 | escaped_work_dir = shell_escape(m->work_dir, ",:"); | |||
| 815 | if (!escaped_work_dir) | |||
| 816 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 816, __func__); | |||
| 817 | ||||
| 818 | options = strjoina("lowerdir=", lower, ",upperdir=", escaped_source, ",workdir=", escaped_work_dir)({ const char *_appendees_[] = { "lowerdir=", lower, ",upperdir=" , escaped_source, ",workdir=", escaped_work_dir }; 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_; }); | |||
| 819 | } | |||
| 820 | ||||
| 821 | return mount_verbose(LOG_ERR3, "overlay", where, "overlay", m->read_only ? MS_RDONLYMS_RDONLY : 0, options); | |||
| 822 | } | |||
| 823 | ||||
| 824 | int mount_custom( | |||
| 825 | const char *dest, | |||
| 826 | CustomMount *mounts, size_t n, | |||
| 827 | bool_Bool userns, uid_t uid_shift, uid_t uid_range, | |||
| 828 | const char *selinux_apifs_context) { | |||
| 829 | ||||
| 830 | size_t i; | |||
| 831 | int r; | |||
| 832 | ||||
| 833 | assert(dest)do { if ((__builtin_expect(!!(!(dest)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("dest"), "../src/nspawn/nspawn-mount.c", 833, __PRETTY_FUNCTION__); } while (0); | |||
| 834 | ||||
| 835 | for (i = 0; i < n; i++) { | |||
| 836 | CustomMount *m = mounts + i; | |||
| 837 | ||||
| 838 | switch (m->type) { | |||
| 839 | ||||
| 840 | case CUSTOM_MOUNT_BIND: | |||
| 841 | r = mount_bind(dest, m); | |||
| 842 | break; | |||
| 843 | ||||
| 844 | case CUSTOM_MOUNT_TMPFS: | |||
| 845 | r = mount_tmpfs(dest, m, userns, uid_shift, uid_range, selinux_apifs_context); | |||
| 846 | break; | |||
| 847 | ||||
| 848 | case CUSTOM_MOUNT_OVERLAY: | |||
| 849 | r = mount_overlay(dest, m); | |||
| 850 | break; | |||
| 851 | ||||
| 852 | default: | |||
| 853 | assert_not_reached("Unknown custom mount type")do { log_assert_failed_unreachable_realm(LOG_REALM_SYSTEMD, ( "Unknown custom mount type"), "../src/nspawn/nspawn-mount.c", 853, __PRETTY_FUNCTION__); } while (0); | |||
| 854 | } | |||
| 855 | ||||
| 856 | if (r < 0) | |||
| 857 | return r; | |||
| 858 | } | |||
| 859 | ||||
| 860 | return 0; | |||
| 861 | } | |||
| 862 | ||||
| 863 | /* Retrieve existing subsystems. This function is called in a new cgroup | |||
| 864 | * namespace. | |||
| 865 | */ | |||
| 866 | static int get_process_controllers(Set **ret) { | |||
| 867 | _cleanup_set_free_free___attribute__((cleanup(set_free_freep))) Set *controllers = NULL((void*)0); | |||
| 868 | _cleanup_fclose___attribute__((cleanup(fclosep))) FILE *f = NULL((void*)0); | |||
| 869 | int r; | |||
| 870 | ||||
| 871 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/nspawn/nspawn-mount.c", 871 , __PRETTY_FUNCTION__); } while (0); | |||
| 872 | ||||
| 873 | controllers = set_new(&string_hash_ops)internal_set_new(&string_hash_ops ); | |||
| 874 | if (!controllers) | |||
| 875 | return -ENOMEM12; | |||
| 876 | ||||
| 877 | f = fopen("/proc/self/cgroup", "re"); | |||
| 878 | if (!f) | |||
| 879 | return errno(*__errno_location ()) == ENOENT2 ? -ESRCH3 : -errno(*__errno_location ()); | |||
| 880 | ||||
| 881 | for (;;) { | |||
| 882 | _cleanup_free___attribute__((cleanup(freep))) char *line = NULL((void*)0); | |||
| 883 | char *e, *l; | |||
| 884 | ||||
| 885 | r = read_line(f, LONG_LINE_MAX(1U*1024U*1024U), &line); | |||
| 886 | if (r < 0) | |||
| 887 | return r; | |||
| 888 | if (r == 0) | |||
| 889 | break; | |||
| 890 | ||||
| 891 | l = strchr(line, ':'); | |||
| 892 | if (!l) | |||
| 893 | continue; | |||
| 894 | ||||
| 895 | l++; | |||
| 896 | e = strchr(l, ':'); | |||
| 897 | if (!e) | |||
| 898 | continue; | |||
| 899 | ||||
| 900 | *e = 0; | |||
| 901 | ||||
| 902 | if (STR_IN_SET(l, "", "name=systemd", "name=unified")(!!strv_find((((char**) ((const char*[]) { "", "name=systemd" , "name=unified", ((void*)0) }))), (l)))) | |||
| 903 | continue; | |||
| 904 | ||||
| 905 | r = set_put_strdup(controllers, l); | |||
| 906 | if (r < 0) | |||
| 907 | return r; | |||
| 908 | } | |||
| 909 | ||||
| 910 | *ret = TAKE_PTR(controllers)({ typeof(controllers) _ptr_ = (controllers); (controllers) = ((void*)0); _ptr_; }); | |||
| 911 | ||||
| 912 | return 0; | |||
| 913 | } | |||
| 914 | ||||
| 915 | static int mount_legacy_cgroup_hierarchy( | |||
| 916 | const char *dest, | |||
| 917 | const char *controller, | |||
| 918 | const char *hierarchy, | |||
| 919 | bool_Bool read_only) { | |||
| 920 | ||||
| 921 | const char *to, *fstype, *opts; | |||
| 922 | int r; | |||
| 923 | ||||
| 924 | to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy)({ const char *_appendees_[] = { strempty(dest), "/sys/fs/cgroup/" , hierarchy }; 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_; }); | |||
| 925 | ||||
| 926 | r = path_is_mount_point(to, dest, 0); | |||
| 927 | if (r < 0 && r != -ENOENT2) | |||
| 928 | return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 928, __func__, "Failed to determine if %s is mounted already: %m" , to) : -abs(_e); }); | |||
| 929 | if (r > 0) | |||
| 930 | return 0; | |||
| 931 | ||||
| 932 | mkdir_p(to, 0755); | |||
| 933 | ||||
| 934 | /* The superblock mount options of the mount point need to be | |||
| 935 | * identical to the hosts', and hence writable... */ | |||
| 936 | if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID)(strcmp((controller),("name=unified")) == 0)) { | |||
| 937 | fstype = "cgroup2"; | |||
| 938 | opts = NULL((void*)0); | |||
| 939 | } else if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_LEGACY)(strcmp((controller),("name=systemd")) == 0)) { | |||
| 940 | fstype = "cgroup"; | |||
| 941 | opts = "none,name=systemd,xattr"; | |||
| 942 | } else { | |||
| 943 | fstype = "cgroup"; | |||
| 944 | opts = controller; | |||
| 945 | } | |||
| 946 | ||||
| 947 | r = mount_verbose(LOG_ERR3, "cgroup", to, fstype, MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV, opts); | |||
| 948 | if (r < 0) | |||
| 949 | return r; | |||
| 950 | ||||
| 951 | /* ... hence let's only make the bind mount read-only, not the superblock. */ | |||
| 952 | if (read_only) { | |||
| 953 | r = mount_verbose(LOG_ERR3, NULL((void*)0), to, NULL((void*)0), | |||
| 954 | MS_BINDMS_BIND|MS_REMOUNTMS_REMOUNT|MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV|MS_RDONLYMS_RDONLY, NULL((void*)0)); | |||
| 955 | if (r < 0) | |||
| 956 | return r; | |||
| 957 | } | |||
| 958 | ||||
| 959 | return 1; | |||
| 960 | } | |||
| 961 | ||||
| 962 | /* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */ | |||
| 963 | static int mount_legacy_cgns_supported( | |||
| 964 | const char *dest, | |||
| 965 | CGroupUnified unified_requested, | |||
| 966 | bool_Bool userns, | |||
| 967 | uid_t uid_shift, | |||
| 968 | uid_t uid_range, | |||
| 969 | const char *selinux_apifs_context) { | |||
| 970 | ||||
| 971 | _cleanup_set_free_free___attribute__((cleanup(set_free_freep))) Set *controllers = NULL((void*)0); | |||
| 972 | const char *cgroup_root = "/sys/fs/cgroup", *c; | |||
| 973 | int r; | |||
| 974 | ||||
| 975 | (void) mkdir_p(cgroup_root, 0755); | |||
| 976 | ||||
| 977 | /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */ | |||
| 978 | r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW0x400); | |||
| 979 | if (r < 0) | |||
| 980 | return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 980, __func__, "Failed to determine if /sys/fs/cgroup is already mounted: %m" ) : -abs(_e); }); | |||
| 981 | if (r == 0) { | |||
| 982 | _cleanup_free___attribute__((cleanup(freep))) char *options = NULL((void*)0); | |||
| 983 | ||||
| 984 | /* When cgroup namespaces are enabled and user namespaces are | |||
| 985 | * used then the mount of the cgroupfs is done *inside* the new | |||
| 986 | * user namespace. We're root in the new user namespace and the | |||
| 987 | * kernel will happily translate our uid/gid to the correct | |||
| 988 | * uid/gid as seen from e.g. /proc/1/mountinfo. So we simply | |||
| 989 | * pass uid 0 and not uid_shift to tmpfs_patch_options(). | |||
| 990 | */ | |||
| 991 | r = tmpfs_patch_options("mode=755", userns, 0, uid_range, true1, selinux_apifs_context, &options); | |||
| 992 | if (r < 0) | |||
| 993 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 993, __func__); | |||
| 994 | ||||
| 995 | r = mount_verbose(LOG_ERR3, "tmpfs", cgroup_root, "tmpfs", | |||
| 996 | MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV|MS_STRICTATIMEMS_STRICTATIME, options); | |||
| 997 | if (r < 0) | |||
| 998 | return r; | |||
| 999 | } | |||
| 1000 | ||||
| 1001 | r = cg_all_unified(); | |||
| 1002 | if (r < 0) | |||
| 1003 | return r; | |||
| 1004 | if (r > 0) | |||
| 1005 | goto skip_controllers; | |||
| 1006 | ||||
| 1007 | r = get_process_controllers(&controllers); | |||
| 1008 | if (r < 0) | |||
| 1009 | return log_error_errno(r, "Failed to determine cgroup controllers: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 1009, __func__, "Failed to determine cgroup controllers: %m" ) : -abs(_e); }); | |||
| 1010 | ||||
| 1011 | for (;;) { | |||
| 1012 | _cleanup_free___attribute__((cleanup(freep))) const char *controller = NULL((void*)0); | |||
| 1013 | ||||
| 1014 | controller = set_steal_first(controllers); | |||
| 1015 | if (!controller) | |||
| 1016 | break; | |||
| 1017 | ||||
| 1018 | r = mount_legacy_cgroup_hierarchy("", controller, controller, !userns); | |||
| 1019 | if (r < 0) | |||
| 1020 | return r; | |||
| 1021 | ||||
| 1022 | /* When multiple hierarchies are co-mounted, make their | |||
| 1023 | * constituting individual hierarchies a symlink to the | |||
| 1024 | * co-mount. | |||
| 1025 | */ | |||
| 1026 | c = controller; | |||
| 1027 | for (;;) { | |||
| 1028 | _cleanup_free___attribute__((cleanup(freep))) char *target = NULL((void*)0), *tok = NULL((void*)0); | |||
| 1029 | ||||
| 1030 | r = extract_first_word(&c, &tok, ",", 0); | |||
| 1031 | if (r < 0) | |||
| 1032 | return log_error_errno(r, "Failed to extract co-mounted cgroup controller: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 1032, __func__, "Failed to extract co-mounted cgroup controller: %m" ) : -abs(_e); }); | |||
| 1033 | if (r == 0) | |||
| 1034 | break; | |||
| 1035 | ||||
| 1036 | if (streq(controller, tok)(strcmp((controller),(tok)) == 0)) | |||
| 1037 | break; | |||
| 1038 | ||||
| 1039 | target = prefix_root("/sys/fs/cgroup/", tok); | |||
| 1040 | if (!target) | |||
| 1041 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 1041, __func__); | |||
| 1042 | ||||
| 1043 | r = symlink_idempotent(controller, target); | |||
| 1044 | if (r == -EINVAL22) | |||
| 1045 | return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 1045, __func__, "Invalid existing symlink for combined hierarchy: %m" ) : -abs(_e); }); | |||
| 1046 | if (r < 0) | |||
| 1047 | return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 1047, __func__, "Failed to create symlink for combined hierarchy: %m" ) : -abs(_e); }); | |||
| 1048 | } | |||
| 1049 | } | |||
| 1050 | ||||
| 1051 | skip_controllers: | |||
| 1052 | if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) { | |||
| 1053 | r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_HYBRID"name=unified", "unified", false0); | |||
| 1054 | if (r < 0) | |||
| 1055 | return r; | |||
| 1056 | } | |||
| 1057 | ||||
| 1058 | r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_LEGACY"name=systemd", "systemd", false0); | |||
| 1059 | if (r < 0) | |||
| 1060 | return r; | |||
| 1061 | ||||
| 1062 | if (!userns) | |||
| 1063 | return mount_verbose(LOG_ERR3, NULL((void*)0), cgroup_root, NULL((void*)0), | |||
| 1064 | MS_REMOUNTMS_REMOUNT|MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV|MS_STRICTATIMEMS_STRICTATIME|MS_RDONLYMS_RDONLY, "mode=755"); | |||
| 1065 | ||||
| 1066 | return 0; | |||
| 1067 | } | |||
| 1068 | ||||
| 1069 | /* Mount legacy cgroup hierarchy when cgroup namespaces are unsupported. */ | |||
| 1070 | static int mount_legacy_cgns_unsupported( | |||
| 1071 | const char *dest, | |||
| 1072 | CGroupUnified unified_requested, | |||
| 1073 | bool_Bool userns, | |||
| 1074 | uid_t uid_shift, | |||
| 1075 | uid_t uid_range, | |||
| 1076 | const char *selinux_apifs_context) { | |||
| 1077 | ||||
| 1078 | _cleanup_set_free_free___attribute__((cleanup(set_free_freep))) Set *controllers = NULL((void*)0); | |||
| 1079 | const char *cgroup_root; | |||
| 1080 | int r; | |||
| 1081 | ||||
| 1082 | cgroup_root = prefix_roota(dest, "/sys/fs/cgroup")({ const char* _path = ("/sys/fs/cgroup"), *_root = (dest), * _ret; char *_p, *_n; size_t _l; while (_path[0] == '/' && _path[1] == '/') _path ++; if (empty_or_root(_root)) _ret = _path ; else { _l = strlen(_root) + 1 + strlen(_path) + 1; _n = __builtin_alloca (_l); _p = stpcpy(_n, _root); while (_p > _n && _p [-1] == '/') _p--; if (_path[0] != '/') *(_p++) = '/'; strcpy (_p, _path); _ret = _n; } _ret; }); | |||
| 1083 | ||||
| 1084 | (void) mkdir_p(cgroup_root, 0755); | |||
| 1085 | ||||
| 1086 | /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */ | |||
| 1087 | r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW0x400); | |||
| 1088 | if (r < 0) | |||
| 1089 | return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 1089, __func__, "Failed to determine if /sys/fs/cgroup is already mounted: %m" ) : -abs(_e); }); | |||
| 1090 | if (r == 0) { | |||
| 1091 | _cleanup_free___attribute__((cleanup(freep))) char *options = NULL((void*)0); | |||
| 1092 | ||||
| 1093 | r = tmpfs_patch_options("mode=755", userns, uid_shift, uid_range, false0, selinux_apifs_context, &options); | |||
| 1094 | if (r < 0) | |||
| 1095 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 1095, __func__); | |||
| 1096 | ||||
| 1097 | r = mount_verbose(LOG_ERR3, "tmpfs", cgroup_root, "tmpfs", | |||
| 1098 | MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV|MS_STRICTATIMEMS_STRICTATIME, options); | |||
| 1099 | if (r < 0) | |||
| 1100 | return r; | |||
| 1101 | } | |||
| 1102 | ||||
| 1103 | r = cg_all_unified(); | |||
| 1104 | if (r < 0) | |||
| 1105 | return r; | |||
| 1106 | if (r > 0) | |||
| 1107 | goto skip_controllers; | |||
| 1108 | ||||
| 1109 | r = cg_kernel_controllers(&controllers); | |||
| 1110 | if (r < 0) | |||
| 1111 | return log_error_errno(r, "Failed to determine cgroup controllers: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 1111, __func__, "Failed to determine cgroup controllers: %m" ) : -abs(_e); }); | |||
| 1112 | ||||
| 1113 | for (;;) { | |||
| 1114 | _cleanup_free___attribute__((cleanup(freep))) char *controller = NULL((void*)0), *origin = NULL((void*)0), *combined = NULL((void*)0); | |||
| 1115 | ||||
| 1116 | controller = set_steal_first(controllers); | |||
| 1117 | if (!controller) | |||
| 1118 | break; | |||
| 1119 | ||||
| 1120 | origin = prefix_root("/sys/fs/cgroup/", controller); | |||
| 1121 | if (!origin) | |||
| 1122 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 1122, __func__); | |||
| 1123 | ||||
| 1124 | r = readlink_malloc(origin, &combined); | |||
| 1125 | if (r == -EINVAL22) { | |||
| 1126 | /* Not a symbolic link, but directly a single cgroup hierarchy */ | |||
| 1127 | ||||
| 1128 | r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true1); | |||
| 1129 | if (r < 0) | |||
| 1130 | return r; | |||
| 1131 | ||||
| 1132 | } else if (r < 0) | |||
| 1133 | return log_error_errno(r, "Failed to read link %s: %m", origin)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 1133, __func__, "Failed to read link %s: %m" , origin) : -abs(_e); }); | |||
| 1134 | else { | |||
| 1135 | _cleanup_free___attribute__((cleanup(freep))) char *target = NULL((void*)0); | |||
| 1136 | ||||
| 1137 | target = prefix_root(dest, origin); | |||
| 1138 | if (!target) | |||
| 1139 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 1139, __func__); | |||
| 1140 | ||||
| 1141 | /* A symbolic link, a combination of controllers in one hierarchy */ | |||
| 1142 | ||||
| 1143 | if (!filename_is_valid(combined)) { | |||
| 1144 | log_warning("Ignoring invalid combined hierarchy %s.", combined)({ int _level = (((4))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 1144, __func__, "Ignoring invalid combined hierarchy %s." , combined) : -abs(_e); }); | |||
| 1145 | continue; | |||
| 1146 | } | |||
| 1147 | ||||
| 1148 | r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true1); | |||
| 1149 | if (r < 0) | |||
| 1150 | return r; | |||
| 1151 | ||||
| 1152 | r = symlink_idempotent(combined, target); | |||
| 1153 | if (r == -EINVAL22) | |||
| 1154 | return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 1154, __func__, "Invalid existing symlink for combined hierarchy: %m" ) : -abs(_e); }); | |||
| 1155 | if (r < 0) | |||
| 1156 | return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 1156, __func__, "Failed to create symlink for combined hierarchy: %m" ) : -abs(_e); }); | |||
| 1157 | } | |||
| 1158 | } | |||
| 1159 | ||||
| 1160 | skip_controllers: | |||
| 1161 | if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) { | |||
| 1162 | r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_HYBRID"name=unified", "unified", false0); | |||
| 1163 | if (r < 0) | |||
| 1164 | return r; | |||
| 1165 | } | |||
| 1166 | ||||
| 1167 | r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_LEGACY"name=systemd", "systemd", false0); | |||
| 1168 | if (r < 0) | |||
| 1169 | return r; | |||
| 1170 | ||||
| 1171 | return mount_verbose(LOG_ERR3, NULL((void*)0), cgroup_root, NULL((void*)0), | |||
| 1172 | MS_REMOUNTMS_REMOUNT|MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV|MS_STRICTATIMEMS_STRICTATIME|MS_RDONLYMS_RDONLY, "mode=755"); | |||
| 1173 | } | |||
| 1174 | ||||
| 1175 | static int mount_unified_cgroups(const char *dest) { | |||
| 1176 | const char *p; | |||
| 1177 | int r; | |||
| 1178 | ||||
| 1179 | assert(dest)do { if ((__builtin_expect(!!(!(dest)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("dest"), "../src/nspawn/nspawn-mount.c", 1179, __PRETTY_FUNCTION__); } while (0); | |||
| 1180 | ||||
| 1181 | p = prefix_roota(dest, "/sys/fs/cgroup")({ const char* _path = ("/sys/fs/cgroup"), *_root = (dest), * _ret; char *_p, *_n; size_t _l; while (_path[0] == '/' && _path[1] == '/') _path ++; if (empty_or_root(_root)) _ret = _path ; else { _l = strlen(_root) + 1 + strlen(_path) + 1; _n = __builtin_alloca (_l); _p = stpcpy(_n, _root); while (_p > _n && _p [-1] == '/') _p--; if (_path[0] != '/') *(_p++) = '/'; strcpy (_p, _path); _ret = _n; } _ret; }); | |||
| 1182 | ||||
| 1183 | (void) mkdir_p(p, 0755); | |||
| 1184 | ||||
| 1185 | r = path_is_mount_point(p, dest, AT_SYMLINK_FOLLOW0x400); | |||
| 1186 | if (r < 0) | |||
| 1187 | return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 1187, __func__, "Failed to determine if %s is mounted already: %m" , p) : -abs(_e); }); | |||
| 1188 | if (r > 0) { | |||
| 1189 | p = prefix_roota(dest, "/sys/fs/cgroup/cgroup.procs")({ const char* _path = ("/sys/fs/cgroup/cgroup.procs"), *_root = (dest), *_ret; char *_p, *_n; size_t _l; while (_path[0] == '/' && _path[1] == '/') _path ++; if (empty_or_root( _root)) _ret = _path; else { _l = strlen(_root) + 1 + strlen( _path) + 1; _n = __builtin_alloca (_l); _p = stpcpy(_n, _root ); while (_p > _n && _p[-1] == '/') _p--; if (_path [0] != '/') *(_p++) = '/'; strcpy(_p, _path); _ret = _n; } _ret ; }); | |||
| 1190 | if (access(p, F_OK0) >= 0) | |||
| 1191 | return 0; | |||
| 1192 | if (errno(*__errno_location ()) != ENOENT2) | |||
| 1193 | return log_error_errno(errno, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/nspawn/nspawn-mount.c", 1193, __func__ , "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m" , p) : -abs(_e); }); | |||
| 1194 | ||||
| 1195 | log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p)({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 1195, __func__, "%s is already mounted but not a unified cgroup hierarchy. Refusing." , p) : -abs(_e); }); | |||
| 1196 | return -EINVAL22; | |||
| 1197 | } | |||
| 1198 | ||||
| 1199 | return mount_verbose(LOG_ERR3, "cgroup", p, "cgroup2", MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV, NULL((void*)0)); | |||
| 1200 | } | |||
| 1201 | ||||
| 1202 | int mount_cgroups( | |||
| 1203 | const char *dest, | |||
| 1204 | CGroupUnified unified_requested, | |||
| 1205 | bool_Bool userns, | |||
| 1206 | uid_t uid_shift, | |||
| 1207 | uid_t uid_range, | |||
| 1208 | const char *selinux_apifs_context, | |||
| 1209 | bool_Bool use_cgns) { | |||
| 1210 | ||||
| 1211 | if (unified_requested >= CGROUP_UNIFIED_ALL) | |||
| 1212 | return mount_unified_cgroups(dest); | |||
| 1213 | if (use_cgns) | |||
| 1214 | return mount_legacy_cgns_supported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context); | |||
| 1215 | ||||
| 1216 | return mount_legacy_cgns_unsupported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context); | |||
| 1217 | } | |||
| 1218 | ||||
| 1219 | static int mount_systemd_cgroup_writable_one(const char *root, const char *own) { | |||
| 1220 | int r; | |||
| 1221 | ||||
| 1222 | assert(root)do { if ((__builtin_expect(!!(!(root)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("root"), "../src/nspawn/nspawn-mount.c", 1222, __PRETTY_FUNCTION__); } while (0); | |||
| 1223 | assert(own)do { if ((__builtin_expect(!!(!(own)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("own"), "../src/nspawn/nspawn-mount.c", 1223 , __PRETTY_FUNCTION__); } while (0); | |||
| 1224 | ||||
| 1225 | /* Make our own cgroup a (writable) bind mount */ | |||
| 1226 | r = mount_verbose(LOG_ERR3, own, own, NULL((void*)0), MS_BINDMS_BIND, NULL((void*)0)); | |||
| 1227 | if (r < 0) | |||
| 1228 | return r; | |||
| 1229 | ||||
| 1230 | /* And then remount the systemd cgroup root read-only */ | |||
| 1231 | return mount_verbose(LOG_ERR3, NULL((void*)0), root, NULL((void*)0), | |||
| 1232 | MS_BINDMS_BIND|MS_REMOUNTMS_REMOUNT|MS_NOSUIDMS_NOSUID|MS_NOEXECMS_NOEXEC|MS_NODEVMS_NODEV|MS_RDONLYMS_RDONLY, NULL((void*)0)); | |||
| 1233 | } | |||
| 1234 | ||||
| 1235 | int mount_systemd_cgroup_writable( | |||
| 1236 | const char *dest, | |||
| 1237 | CGroupUnified unified_requested) { | |||
| 1238 | ||||
| 1239 | _cleanup_free___attribute__((cleanup(freep))) char *own_cgroup_path = NULL((void*)0); | |||
| 1240 | const char *root, *own; | |||
| 1241 | int r; | |||
| 1242 | ||||
| 1243 | assert(dest)do { if ((__builtin_expect(!!(!(dest)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("dest"), "../src/nspawn/nspawn-mount.c", 1243, __PRETTY_FUNCTION__); } while (0); | |||
| 1244 | ||||
| 1245 | r = cg_pid_get_path(NULL((void*)0), 0, &own_cgroup_path); | |||
| 1246 | if (r < 0) | |||
| 1247 | return log_error_errno(r, "Failed to determine our own cgroup path: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 1247, __func__, "Failed to determine our own cgroup path: %m" ) : -abs(_e); }); | |||
| 1248 | ||||
| 1249 | /* If we are living in the top-level, then there's nothing to do... */ | |||
| 1250 | if (path_equal(own_cgroup_path, "/")) | |||
| 1251 | return 0; | |||
| 1252 | ||||
| 1253 | if (unified_requested >= CGROUP_UNIFIED_ALL) { | |||
| 1254 | ||||
| 1255 | root = prefix_roota(dest, "/sys/fs/cgroup")({ const char* _path = ("/sys/fs/cgroup"), *_root = (dest), * _ret; char *_p, *_n; size_t _l; while (_path[0] == '/' && _path[1] == '/') _path ++; if (empty_or_root(_root)) _ret = _path ; else { _l = strlen(_root) + 1 + strlen(_path) + 1; _n = __builtin_alloca (_l); _p = stpcpy(_n, _root); while (_p > _n && _p [-1] == '/') _p--; if (_path[0] != '/') *(_p++) = '/'; strcpy (_p, _path); _ret = _n; } _ret; }); | |||
| 1256 | own = strjoina(root, own_cgroup_path)({ const char *_appendees_[] = { root, own_cgroup_path }; 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_; }); | |||
| 1257 | ||||
| 1258 | } else { | |||
| 1259 | ||||
| 1260 | if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) { | |||
| 1261 | root = prefix_roota(dest, "/sys/fs/cgroup/unified")({ const char* _path = ("/sys/fs/cgroup/unified"), *_root = ( dest), *_ret; char *_p, *_n; size_t _l; while (_path[0] == '/' && _path[1] == '/') _path ++; if (empty_or_root(_root )) _ret = _path; else { _l = strlen(_root) + 1 + strlen(_path ) + 1; _n = __builtin_alloca (_l); _p = stpcpy(_n, _root); while (_p > _n && _p[-1] == '/') _p--; if (_path[0] != '/' ) *(_p++) = '/'; strcpy(_p, _path); _ret = _n; } _ret; }); | |||
| 1262 | own = strjoina(root, own_cgroup_path)({ const char *_appendees_[] = { root, own_cgroup_path }; 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_; }); | |||
| 1263 | ||||
| 1264 | r = mount_systemd_cgroup_writable_one(root, own); | |||
| 1265 | if (r < 0) | |||
| 1266 | return r; | |||
| 1267 | } | |||
| 1268 | ||||
| 1269 | root = prefix_roota(dest, "/sys/fs/cgroup/systemd")({ const char* _path = ("/sys/fs/cgroup/systemd"), *_root = ( dest), *_ret; char *_p, *_n; size_t _l; while (_path[0] == '/' && _path[1] == '/') _path ++; if (empty_or_root(_root )) _ret = _path; else { _l = strlen(_root) + 1 + strlen(_path ) + 1; _n = __builtin_alloca (_l); _p = stpcpy(_n, _root); while (_p > _n && _p[-1] == '/') _p--; if (_path[0] != '/' ) *(_p++) = '/'; strcpy(_p, _path); _ret = _n; } _ret; }); | |||
| 1270 | own = strjoina(root, own_cgroup_path)({ const char *_appendees_[] = { root, own_cgroup_path }; 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_; }); | |||
| 1271 | } | |||
| 1272 | ||||
| 1273 | return mount_systemd_cgroup_writable_one(root, own); | |||
| 1274 | } | |||
| 1275 | ||||
| 1276 | int setup_volatile_state( | |||
| 1277 | const char *directory, | |||
| 1278 | VolatileMode mode, | |||
| 1279 | bool_Bool userns, uid_t uid_shift, uid_t uid_range, | |||
| 1280 | const char *selinux_apifs_context) { | |||
| 1281 | ||||
| 1282 | _cleanup_free___attribute__((cleanup(freep))) char *buf = NULL((void*)0); | |||
| 1283 | const char *p, *options; | |||
| 1284 | int r; | |||
| 1285 | ||||
| 1286 | assert(directory)do { if ((__builtin_expect(!!(!(directory)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("directory"), "../src/nspawn/nspawn-mount.c" , 1286, __PRETTY_FUNCTION__); } while (0); | |||
| 1287 | ||||
| 1288 | if (mode != VOLATILE_STATE) | |||
| 1289 | return 0; | |||
| 1290 | ||||
| 1291 | /* --volatile=state means we simply overmount /var | |||
| 1292 | with a tmpfs, and the rest read-only. */ | |||
| 1293 | ||||
| 1294 | r = bind_remount_recursive(directory, true1, NULL((void*)0)); | |||
| 1295 | if (r < 0) | |||
| 1296 | return log_error_errno(r, "Failed to remount %s read-only: %m", directory)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 1296, __func__, "Failed to remount %s read-only: %m" , directory) : -abs(_e); }); | |||
| 1297 | ||||
| 1298 | p = prefix_roota(directory, "/var")({ const char* _path = ("/var"), *_root = (directory), *_ret; char *_p, *_n; size_t _l; while (_path[0] == '/' && _path [1] == '/') _path ++; if (empty_or_root(_root)) _ret = _path; else { _l = strlen(_root) + 1 + strlen(_path) + 1; _n = __builtin_alloca (_l); _p = stpcpy(_n, _root); while (_p > _n && _p [-1] == '/') _p--; if (_path[0] != '/') *(_p++) = '/'; strcpy (_p, _path); _ret = _n; } _ret; }); | |||
| 1299 | r = mkdir(p, 0755); | |||
| 1300 | if (r < 0 && errno(*__errno_location ()) != EEXIST17) | |||
| 1301 | return log_error_errno(errno, "Failed to create %s: %m", directory)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/nspawn/nspawn-mount.c", 1301, __func__ , "Failed to create %s: %m", directory) : -abs(_e); }); | |||
| 1302 | ||||
| 1303 | options = "mode=755"; | |||
| 1304 | r = tmpfs_patch_options(options, userns, uid_shift, uid_range, false0, selinux_apifs_context, &buf); | |||
| 1305 | if (r < 0) | |||
| 1306 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 1306, __func__); | |||
| 1307 | if (r > 0) | |||
| 1308 | options = buf; | |||
| 1309 | ||||
| 1310 | return mount_verbose(LOG_ERR3, "tmpfs", p, "tmpfs", MS_STRICTATIMEMS_STRICTATIME, options); | |||
| 1311 | } | |||
| 1312 | ||||
| 1313 | int setup_volatile( | |||
| 1314 | const char *directory, | |||
| 1315 | VolatileMode mode, | |||
| 1316 | bool_Bool userns, uid_t uid_shift, uid_t uid_range, | |||
| 1317 | const char *selinux_apifs_context) { | |||
| 1318 | ||||
| 1319 | bool_Bool tmpfs_mounted = false0, bind_mounted = false0; | |||
| 1320 | char template[] = "/tmp/nspawn-volatile-XXXXXX"; | |||
| 1321 | _cleanup_free___attribute__((cleanup(freep))) char *buf = NULL((void*)0); | |||
| 1322 | const char *f, *t, *options; | |||
| 1323 | int r; | |||
| 1324 | ||||
| 1325 | assert(directory)do { if ((__builtin_expect(!!(!(directory)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("directory"), "../src/nspawn/nspawn-mount.c" , 1325, __PRETTY_FUNCTION__); } while (0); | |||
| 1326 | ||||
| 1327 | if (mode != VOLATILE_YES) | |||
| 1328 | return 0; | |||
| 1329 | ||||
| 1330 | /* --volatile=yes means we mount a tmpfs to the root dir, and | |||
| 1331 | the original /usr to use inside it, and that read-only. */ | |||
| 1332 | ||||
| 1333 | if (!mkdtemp(template)) | |||
| 1334 | return log_error_errno(errno, "Failed to create temporary directory: %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/nspawn/nspawn-mount.c", 1334, __func__ , "Failed to create temporary directory: %m") : -abs(_e); }); | |||
| 1335 | ||||
| 1336 | options = "mode=755"; | |||
| 1337 | r = tmpfs_patch_options(options, userns, uid_shift, uid_range, false0, selinux_apifs_context, &buf); | |||
| 1338 | if (r < 0) | |||
| 1339 | return log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/nspawn/nspawn-mount.c" , 1339, __func__); | |||
| 1340 | if (r > 0) | |||
| 1341 | options = buf; | |||
| 1342 | ||||
| 1343 | r = mount_verbose(LOG_ERR3, "tmpfs", template, "tmpfs", MS_STRICTATIMEMS_STRICTATIME, options); | |||
| 1344 | if (r < 0) | |||
| 1345 | goto fail; | |||
| 1346 | ||||
| 1347 | tmpfs_mounted = true1; | |||
| 1348 | ||||
| 1349 | f = prefix_roota(directory, "/usr")({ const char* _path = ("/usr"), *_root = (directory), *_ret; char *_p, *_n; size_t _l; while (_path[0] == '/' && _path [1] == '/') _path ++; if (empty_or_root(_root)) _ret = _path; else { _l = strlen(_root) + 1 + strlen(_path) + 1; _n = __builtin_alloca (_l); _p = stpcpy(_n, _root); while (_p > _n && _p [-1] == '/') _p--; if (_path[0] != '/') *(_p++) = '/'; strcpy (_p, _path); _ret = _n; } _ret; }); | |||
| 1350 | t = prefix_roota(template, "/usr")({ const char* _path = ("/usr"), *_root = (template), *_ret; char *_p, *_n; size_t _l; while (_path[0] == '/' && _path [1] == '/') _path ++; if (empty_or_root(_root)) _ret = _path; else { _l = strlen(_root) + 1 + strlen(_path) + 1; _n = __builtin_alloca (_l); _p = stpcpy(_n, _root); while (_p > _n && _p [-1] == '/') _p--; if (_path[0] != '/') *(_p++) = '/'; strcpy (_p, _path); _ret = _n; } _ret; }); | |||
| 1351 | ||||
| 1352 | r = mkdir(t, 0755); | |||
| 1353 | if (r < 0 && errno(*__errno_location ()) != EEXIST17) { | |||
| 1354 | r = log_error_errno(errno, "Failed to create %s: %m", t)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/nspawn/nspawn-mount.c", 1354, __func__ , "Failed to create %s: %m", t) : -abs(_e); }); | |||
| 1355 | goto fail; | |||
| 1356 | } | |||
| 1357 | ||||
| 1358 | r = mount_verbose(LOG_ERR3, f, t, NULL((void*)0), MS_BINDMS_BIND|MS_RECMS_REC, NULL((void*)0)); | |||
| 1359 | if (r < 0) | |||
| 1360 | goto fail; | |||
| 1361 | ||||
| 1362 | bind_mounted = true1; | |||
| 1363 | ||||
| 1364 | r = bind_remount_recursive(t, true1, NULL((void*)0)); | |||
| 1365 | if (r < 0) { | |||
| 1366 | log_error_errno(r, "Failed to remount %s read-only: %m", t)({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/nspawn/nspawn-mount.c", 1366, __func__, "Failed to remount %s read-only: %m" , t) : -abs(_e); }); | |||
| 1367 | goto fail; | |||
| 1368 | } | |||
| 1369 | ||||
| 1370 | r = mount_verbose(LOG_ERR3, template, directory, NULL((void*)0), MS_MOVEMS_MOVE, NULL((void*)0)); | |||
| 1371 | if (r < 0) | |||
| 1372 | goto fail; | |||
| 1373 | ||||
| 1374 | (void) rmdir(template); | |||
| 1375 | ||||
| 1376 | return 0; | |||
| 1377 | ||||
| 1378 | fail: | |||
| 1379 | if (bind_mounted) | |||
| 1380 | (void) umount_verbose(t); | |||
| 1381 | ||||
| 1382 | if (tmpfs_mounted) | |||
| 1383 | (void) umount_verbose(template); | |||
| 1384 | (void) rmdir(template); | |||
| 1385 | return r; | |||
| 1386 | } | |||
| 1387 | ||||
| 1388 | /* Expects *pivot_root_new and *pivot_root_old to be initialised to allocated memory or NULL. */ | |||
| 1389 | int pivot_root_parse(char **pivot_root_new, char **pivot_root_old, const char *s) { | |||
| 1390 | _cleanup_free___attribute__((cleanup(freep))) char *root_new = NULL((void*)0), *root_old = NULL((void*)0); | |||
| 1391 | const char *p = s; | |||
| 1392 | int r; | |||
| 1393 | ||||
| 1394 | assert(pivot_root_new)do { if ((__builtin_expect(!!(!(pivot_root_new)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("pivot_root_new"), "../src/nspawn/nspawn-mount.c" , 1394, __PRETTY_FUNCTION__); } while (0); | |||
| 1395 | assert(pivot_root_old)do { if ((__builtin_expect(!!(!(pivot_root_old)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("pivot_root_old"), "../src/nspawn/nspawn-mount.c" , 1395, __PRETTY_FUNCTION__); } while (0); | |||
| 1396 | ||||
| 1397 | r = extract_first_word(&p, &root_new, ":", EXTRACT_DONT_COALESCE_SEPARATORS); | |||
| 1398 | if (r < 0) | |||
| 1399 | return r; | |||
| 1400 | if (r == 0) | |||
| 1401 | return -EINVAL22; | |||
| 1402 | ||||
| 1403 | if (isempty(p)) | |||
| 1404 | root_old = NULL((void*)0); | |||
| 1405 | else { | |||
| 1406 | root_old = strdup(p); | |||
| 1407 | if (!root_old) | |||
| 1408 | return -ENOMEM12; | |||
| 1409 | } | |||
| 1410 | ||||
| 1411 | if (!path_is_absolute(root_new)) | |||
| 1412 | return -EINVAL22; | |||
| 1413 | if (root_old && !path_is_absolute(root_old)) | |||
| 1414 | return -EINVAL22; | |||
| 1415 | ||||
| 1416 | free_and_replace(*pivot_root_new, root_new)({ free(*pivot_root_new); (*pivot_root_new) = (root_new); (root_new ) = ((void*)0); 0; }); | |||
| 1417 | free_and_replace(*pivot_root_old, root_old)({ free(*pivot_root_old); (*pivot_root_old) = (root_old); (root_old ) = ((void*)0); 0; }); | |||
| 1418 | ||||
| 1419 | return 0; | |||
| 1420 | } | |||
| 1421 | ||||
| 1422 | int setup_pivot_root(const char *directory, const char *pivot_root_new, const char *pivot_root_old) { | |||
| 1423 | _cleanup_free___attribute__((cleanup(freep))) char *directory_pivot_root_new = NULL((void*)0); | |||
| 1424 | _cleanup_free___attribute__((cleanup(freep))) char *pivot_tmp_pivot_root_old = NULL((void*)0); | |||
| 1425 | char pivot_tmp[] = "/tmp/nspawn-pivot-XXXXXX"; | |||
| 1426 | bool_Bool remove_pivot_tmp = false0; | |||
| 1427 | int r; | |||
| 1428 | ||||
| 1429 | assert(directory)do { if ((__builtin_expect(!!(!(directory)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("directory"), "../src/nspawn/nspawn-mount.c" , 1429, __PRETTY_FUNCTION__); } while (0); | |||
| 1430 | ||||
| 1431 | if (!pivot_root_new) | |||
| 1432 | return 0; | |||
| 1433 | ||||
| 1434 | /* Pivot pivot_root_new to / and the existing / to pivot_root_old. | |||
| 1435 | * If pivot_root_old is NULL, the existing / disappears. | |||
| 1436 | * This requires a temporary directory, pivot_tmp, which is | |||
| 1437 | * not a child of either. | |||
| 1438 | * | |||
| 1439 | * This is typically used for OSTree-style containers, where | |||
| 1440 | * the root partition contains several sysroots which could be | |||
| 1441 | * run. Normally, one would be chosen by the bootloader and | |||
| 1442 | * pivoted to / by initramfs. | |||
| 1443 | * | |||
| 1444 | * For example, for an OSTree deployment, pivot_root_new | |||
| 1445 | * would be: /ostree/deploy/$os/deploy/$checksum. Note that this | |||
| 1446 | * code doesn’t do the /var mount which OSTree expects: use | |||
| 1447 | * --bind +/sysroot/ostree/deploy/$os/var:/var for that. | |||
| 1448 | * | |||
| 1449 | * So in the OSTree case, we’ll end up with something like: | |||
| 1450 | * - directory = /tmp/nspawn-root-123456 | |||
| 1451 | * - pivot_root_new = /ostree/deploy/os/deploy/123abc | |||
| 1452 | * - pivot_root_old = /sysroot | |||
| 1453 | * - directory_pivot_root_new = | |||
| 1454 | * /tmp/nspawn-root-123456/ostree/deploy/os/deploy/123abc | |||
| 1455 | * - pivot_tmp = /tmp/nspawn-pivot-123456 | |||
| 1456 | * - pivot_tmp_pivot_root_old = /tmp/nspawn-pivot-123456/sysroot | |||
| 1457 | * | |||
| 1458 | * Requires all file systems at directory and below to be mounted | |||
| 1459 | * MS_PRIVATE or MS_SLAVE so they can be moved. | |||
| 1460 | */ | |||
| 1461 | directory_pivot_root_new = prefix_root(directory, pivot_root_new); | |||
| 1462 | ||||
| 1463 | /* Remount directory_pivot_root_new to make it movable. */ | |||
| 1464 | r = mount_verbose(LOG_ERR3, directory_pivot_root_new, directory_pivot_root_new, NULL((void*)0), MS_BINDMS_BIND, NULL((void*)0)); | |||
| 1465 | if (r < 0) | |||
| 1466 | goto done; | |||
| 1467 | ||||
| 1468 | if (pivot_root_old) { | |||
| 1469 | if (!mkdtemp(pivot_tmp)) { | |||
| 1470 | r = log_error_errno(errno, "Failed to create temporary directory: %m")({ int _level = ((3)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/nspawn/nspawn-mount.c", 1470, __func__ , "Failed to create temporary directory: %m") : -abs(_e); }); | |||
| 1471 | goto done; | |||
| 1472 | } | |||
| 1473 | ||||
| 1474 | remove_pivot_tmp = true1; | |||
| 1475 | pivot_tmp_pivot_root_old = prefix_root(pivot_tmp, pivot_root_old); | |||
| 1476 | ||||
| 1477 | r = mount_verbose(LOG_ERR3, directory_pivot_root_new, pivot_tmp, NULL((void*)0), MS_MOVEMS_MOVE, NULL((void*)0)); | |||
| 1478 | if (r < 0) | |||
| 1479 | goto done; | |||
| 1480 | ||||
| 1481 | r = mount_verbose(LOG_ERR3, directory, pivot_tmp_pivot_root_old, NULL((void*)0), MS_MOVEMS_MOVE, NULL((void*)0)); | |||
| 1482 | if (r < 0) | |||
| 1483 | goto done; | |||
| 1484 | ||||
| 1485 | r = mount_verbose(LOG_ERR3, pivot_tmp, directory, NULL((void*)0), MS_MOVEMS_MOVE, NULL((void*)0)); | |||
| 1486 | if (r < 0) | |||
| 1487 | goto done; | |||
| 1488 | } else { | |||
| 1489 | r = mount_verbose(LOG_ERR3, directory_pivot_root_new, directory, NULL((void*)0), MS_MOVEMS_MOVE, NULL((void*)0)); | |||
| 1490 | if (r < 0) | |||
| 1491 | goto done; | |||
| 1492 | } | |||
| 1493 | ||||
| 1494 | done: | |||
| 1495 | if (remove_pivot_tmp) | |||
| 1496 | (void) rmdir(pivot_tmp); | |||
| 1497 | ||||
| 1498 | return r; | |||
| 1499 | } |