]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/fstab-generator/fstab-generator.c
basic/include: replace _Static_assert() with static_assert()
[thirdparty/systemd.git] / src / fstab-generator / fstab-generator.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
6b1dc2bd 2
07630cea 3#include <stdio.h>
8857aa74 4#include <stdlib.h>
6b1dc2bd
LP
5#include <unistd.h>
6
8857aa74
DDM
7#include "sd-bus.h"
8
b5efdb8a 9#include "alloc-util.h"
76d62b63 10#include "argv-util.h"
028a981c
ZJS
11#include "bus-error.h"
12#include "bus-locator.h"
a9399358 13#include "bus-unit-util.h"
76d62b63 14#include "bus-util.h"
f461a28d 15#include "chase.h"
6ac62485 16#include "creds-util.h"
6c51b49c 17#include "efi-loader.h"
905dd992 18#include "env-util.h"
8857aa74 19#include "errno-util.h"
6553db60 20#include "extract-word.h"
3ffd4af2 21#include "fd-util.h"
0d39fa9c 22#include "fileio.h"
d15d0333 23#include "fstab-util.h"
07630cea 24#include "generator.h"
77b8e92d 25#include "in-addr-util.h"
baa6a42d 26#include "initrd-util.h"
07630cea 27#include "log.h"
a4ef3e4d 28#include "main-func.h"
6b1dc2bd 29#include "mount-setup.h"
4349cd7c 30#include "mount-util.h"
049af8ad 31#include "mountpoint-util.h"
6bedfcbb 32#include "parse-util.h"
07630cea 33#include "path-util.h"
4e731273 34#include "proc-cmdline.h"
6b1dc2bd 35#include "special.h"
98bad05e 36#include "specifier.h"
8fcde012 37#include "stat-util.h"
07630cea 38#include "string-util.h"
059cb385 39#include "strv.h"
07630cea 40#include "unit-name.h"
689aede8 41#include "virt.h"
91214a37 42#include "volatile-util.h"
6b1dc2bd 43
d2194e15 44typedef enum MountPointFlags {
f872373a
LP
45 MOUNT_NOAUTO = 1 << 0,
46 MOUNT_NOFAIL = 1 << 1,
47 MOUNT_AUTOMOUNT = 1 << 2,
48 MOUNT_MAKEFS = 1 << 3,
49 MOUNT_GROWFS = 1 << 4,
50 MOUNT_RW_ONLY = 1 << 5,
51 MOUNT_PCRFS = 1 << 6,
52 MOUNT_QUOTA = 1 << 7,
53 MOUNT_VALIDATEFS = 1 << 8,
d2194e15 54} MountPointFlags;
4191418b 55
55365b0a 56typedef struct Mount {
45c535dd 57 bool for_initrd;
55365b0a
YW
58 char *what;
59 char *where;
60 char *fstype;
61 char *options;
62} Mount;
63
64static void mount_array_free(Mount *mounts, size_t n);
65
028a981c 66static bool arg_sysroot_check = false;
7a44c7e3
ZJS
67static const char *arg_dest = NULL;
68static const char *arg_dest_late = NULL;
e48fdd84 69static bool arg_fstab_enabled = true;
567a5307 70static bool arg_swap_enabled = true;
6db615c1
LP
71static char *arg_root_what = NULL;
72static char *arg_root_fstype = NULL;
73static char *arg_root_options = NULL;
2f3dfc6f 74static char *arg_root_hash = NULL;
6db615c1 75static int arg_root_rw = -1;
9f103625
TH
76static char *arg_usr_what = NULL;
77static char *arg_usr_fstype = NULL;
78static char *arg_usr_options = NULL;
c1b9e3df 79static char *arg_usr_hash = NULL;
91214a37 80static VolatileMode arg_volatile_mode = _VOLATILE_MODE_INVALID;
200268c6 81static bool arg_verity = true;
55365b0a
YW
82static Mount *arg_mounts = NULL;
83static size_t arg_n_mounts = 0;
6b1dc2bd 84
a4ef3e4d
YW
85STATIC_DESTRUCTOR_REGISTER(arg_root_what, freep);
86STATIC_DESTRUCTOR_REGISTER(arg_root_fstype, freep);
87STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep);
88STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
89STATIC_DESTRUCTOR_REGISTER(arg_usr_what, freep);
90STATIC_DESTRUCTOR_REGISTER(arg_usr_fstype, freep);
91STATIC_DESTRUCTOR_REGISTER(arg_usr_options, freep);
c1b9e3df 92STATIC_DESTRUCTOR_REGISTER(arg_usr_hash, freep);
55365b0a
YW
93STATIC_ARRAY_DESTRUCTOR_REGISTER(arg_mounts, arg_n_mounts, mount_array_free);
94
95static void mount_done(Mount *m) {
96 assert(m);
97
98 free(m->what);
99 free(m->where);
100 free(m->fstype);
101 free(m->options);
102}
103
104static void mount_array_free(Mount *mounts, size_t n) {
105 FOREACH_ARRAY(m, mounts, n)
106 mount_done(m);
107
108 free(mounts);
109}
110
45c535dd
YW
111static int mount_array_add_internal(
112 bool for_initrd,
113 char *in_what,
114 char *in_where,
115 const char *in_fstype,
06fadc42 116 char *in_options) {
45c535dd 117
55365b0a 118 _cleanup_free_ char *what = NULL, *where = NULL, *fstype = NULL, *options = NULL;
55365b0a
YW
119
120 /* This takes what and where. */
121
122 what = ASSERT_PTR(in_what);
123 where = in_where;
06fadc42 124 options = in_options;
55365b0a
YW
125
126 fstype = strdup(isempty(in_fstype) ? "auto" : in_fstype);
127 if (!fstype)
128 return -ENOMEM;
129
130 if (streq(fstype, "swap"))
131 where = mfree(where);
132
55365b0a
YW
133 if (!GREEDY_REALLOC(arg_mounts, arg_n_mounts + 1))
134 return -ENOMEM;
135
136 arg_mounts[arg_n_mounts++] = (Mount) {
45c535dd 137 .for_initrd = for_initrd,
55365b0a
YW
138 .what = TAKE_PTR(what),
139 .where = TAKE_PTR(where),
140 .fstype = TAKE_PTR(fstype),
141 .options = TAKE_PTR(options),
142 };
143
144 return 0;
145}
146
45c535dd 147static int mount_array_add(bool for_initrd, const char *str) {
55365b0a
YW
148 _cleanup_free_ char *what = NULL, *where = NULL, *fstype = NULL, *options = NULL;
149 int r;
150
151 assert(str);
152
153 r = extract_many_words(&str, ":", EXTRACT_CUNESCAPE | EXTRACT_DONT_COALESCE_SEPARATORS,
4f495126 154 &what, &where, &fstype, &options);
55365b0a
YW
155 if (r < 0)
156 return r;
157 if (r < 2)
158 return -EINVAL;
159 if (!isempty(str))
160 return -EINVAL;
161
06fadc42 162 return mount_array_add_internal(for_initrd, TAKE_PTR(what), TAKE_PTR(where), fstype, TAKE_PTR(options));
55365b0a
YW
163}
164
45c535dd 165static int mount_array_add_swap(bool for_initrd, const char *str) {
55365b0a
YW
166 _cleanup_free_ char *what = NULL, *options = NULL;
167 int r;
168
169 assert(str);
170
171 r = extract_many_words(&str, ":", EXTRACT_CUNESCAPE | EXTRACT_DONT_COALESCE_SEPARATORS,
4f495126 172 &what, &options);
55365b0a
YW
173 if (r < 0)
174 return r;
175 if (r < 1)
176 return -EINVAL;
177 if (!isempty(str))
178 return -EINVAL;
179
06fadc42 180 return mount_array_add_internal(for_initrd, TAKE_PTR(what), NULL, "swap", TAKE_PTR(options));
55365b0a 181}
a4ef3e4d 182
d5cc4be2
LP
183static int write_options(FILE *f, const char *options) {
184 _cleanup_free_ char *o = NULL;
185
27db64bc
MY
186 assert(f);
187
d5cc4be2
LP
188 if (isempty(options))
189 return 0;
190
191 if (streq(options, "defaults"))
192 return 0;
193
98bad05e 194 o = specifier_escape(options);
d5cc4be2
LP
195 if (!o)
196 return log_oom();
197
198 fprintf(f, "Options=%s\n", o);
199 return 1;
200}
201
19d0833b
LP
202static int write_what(FILE *f, const char *what) {
203 _cleanup_free_ char *w = NULL;
204
27db64bc
MY
205 assert(f);
206 assert(what);
207
98bad05e 208 w = specifier_escape(what);
19d0833b
LP
209 if (!w)
210 return log_oom();
211
212 fprintf(f, "What=%s\n", w);
213 return 1;
214}
215
5607d856 216static int add_swap(
7772c177 217 const char *source,
5607d856 218 const char *what,
cfeb4d37 219 const char *options,
d2194e15 220 MountPointFlags flags) {
5607d856 221
fb883e75 222 _cleanup_free_ char *name = NULL;
7fd1b19b 223 _cleanup_fclose_ FILE *f = NULL;
bf1d7ba7 224 int r;
6b1dc2bd
LP
225
226 assert(what);
6b1dc2bd 227
00b4ffde 228 if (access("/proc/swaps", F_OK) < 0) {
55365b0a 229 log_info("Swap not supported, ignoring swap entry for %s.", what);
00b4ffde
LP
230 return 0;
231 }
232
75f86906 233 if (detect_container() > 0) {
55365b0a 234 log_info("Running in a container, ignoring swap entry for %s.", what);
689aede8
LP
235 return 0;
236 }
237
028a981c
ZJS
238 if (arg_sysroot_check) {
239 log_info("%s should be enabled in the initrd, will request daemon-reload.", what);
240 return true;
241 }
242
f872373a 243 log_debug("Found swap entry what=%s makefs=%s growfs=%s pcrfs=%s validatefs=%s noauto=%s nofail=%s",
256604cc 244 what,
f872373a
LP
245 yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS),
246 yes_no(flags & MOUNT_PCRFS), yes_no(flags & MOUNT_VALIDATEFS),
256604cc
YW
247 yes_no(flags & MOUNT_NOAUTO), yes_no(flags & MOUNT_NOFAIL));
248
7410616c
LP
249 r = unit_name_from_path(what, ".swap", &name);
250 if (r < 0)
251 return log_error_errno(r, "Failed to generate unit name: %m");
6b1dc2bd 252
7772c177 253 r = generator_open_unit_file(arg_dest, source, name, &f);
fb883e75
ZJS
254 if (r < 0)
255 return r;
0d536673 256
ed4ad488
ZJS
257 fprintf(f,
258 "[Unit]\n"
a7e88558
LP
259 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n"
260 "SourcePath=%s\n",
7772c177 261 source);
19d0833b 262
a7e88558
LP
263 r = generator_write_blockdev_dependency(f, what);
264 if (r < 0)
265 return r;
266
267 fprintf(f,
268 "\n"
269 "[Swap]\n");
270
19d0833b
LP
271 r = write_what(f, what);
272 if (r < 0)
273 return r;
6b1dc2bd 274
cfeb4d37 275 r = write_options(f, options);
d5cc4be2
LP
276 if (r < 0)
277 return r;
6b1dc2bd 278
47cb901e 279 r = fflush_and_check(f);
23bbb0de 280 if (r < 0)
fb883e75 281 return log_error_errno(r, "Failed to write unit file %s: %m", name);
6b1dc2bd 282
b3208b66 283 /* use what as where, to have a nicer error message */
9a097ece 284 r = generator_write_device_timeout(arg_dest, what, options, NULL);
b3208b66
ZJS
285 if (r < 0)
286 return r;
287
d2194e15 288 if (flags & MOUNT_MAKEFS) {
da495a03
ZJS
289 r = generator_hook_up_mkswap(arg_dest, what);
290 if (r < 0)
291 return r;
292 }
293
d2194e15 294 if (flags & MOUNT_GROWFS)
7f2806d5
ZJS
295 /* TODO: swap devices must be wiped and recreated */
296 log_warning("%s: growing swap devices is currently unsupported.", what);
04959faa
LP
297 if (flags & MOUNT_PCRFS)
298 log_warning("%s: measuring swap devices is currently unsupported.", what);
f872373a
LP
299 if (flags & MOUNT_VALIDATEFS)
300 log_warning("%s: validating swap devices is currently unsupported.", what);
7f2806d5 301
d2194e15 302 if (!(flags & MOUNT_NOAUTO)) {
630d30d3 303 r = generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET,
d2194e15 304 (flags & MOUNT_NOFAIL) ? "wants" : "requires", name);
630d30d3
ZJS
305 if (r < 0)
306 return r;
4e82fe52
TG
307 }
308
028a981c 309 return true;
6b1dc2bd
LP
310}
311
cfeb4d37
YW
312static bool mount_is_network(const char *fstype, const char *options) {
313 return fstab_test_option(options, "_netdev\0") ||
314 (fstype && fstype_is_network(fstype));
6b1dc2bd
LP
315}
316
22f5a825 317static bool mount_in_initrd(const char *where, const char *options, bool accept_root) {
cfeb4d37 318 return fstab_test_option(options, "x-initrd.mount\0") ||
22f5a825 319 (where && PATH_IN_SET(where, "/usr", accept_root ? "/" : NULL));
3d22d1ab
TG
320}
321
110773f6 322static int write_idle_timeout(FILE *f, const char *where, const char *opts) {
149e05c7
MY
323 return generator_write_unit_timeout(f, where, opts,
324 "x-systemd.idle-timeout\0", "TimeoutIdleSec");
110773f6
CH
325}
326
33a4c983
LP
327static int write_dependency(
328 FILE *f,
74467890 329 const char *where,
33a4c983
LP
330 const char *opts,
331 const char *filter,
74467890 332 const char* const *unit_settings) {
33a4c983 333
74467890
MY
334 _cleanup_strv_free_ char **unit_names = NULL;
335 _cleanup_free_ char *units = NULL;
3519d230
KZ
336 int r;
337
338 assert(f);
74467890
MY
339 assert(filter);
340 assert(unit_settings);
3519d230 341
74467890 342 r = fstab_filter_options(opts, filter, NULL, NULL, &unit_names, NULL);
3519d230 343 if (r < 0)
74467890 344 return log_error_errno(r, "Failed to parse options for '%s': %m", where);
3519d230
KZ
345 if (r == 0)
346 return 0;
347
74467890
MY
348 STRV_FOREACH(s, unit_names) {
349 _cleanup_free_ char *mangled = NULL;
3519d230 350
74467890 351 r = unit_name_mangle_with_suffix(*s, "as dependency", 0, ".mount", &mangled);
3519d230 352 if (r < 0)
74467890 353 return log_error_errno(r, "Failed to generate dependency unit name for '%s': %m", where);
33a4c983 354
74467890 355 if (!strextend_with_separator(&units, " ", mangled))
3519d230
KZ
356 return log_oom();
357 }
358
74467890
MY
359 STRV_FOREACH(setting, unit_settings)
360 fprintf(f, "%s=%s\n", *setting, units);
3519d230
KZ
361
362 return 0;
363}
364
74467890
MY
365static int write_after(FILE *f, const char *where, const char *opts) {
366 return write_dependency(f, where, opts,
367 "x-systemd.after\0", STRV_MAKE_CONST("After"));
ae325185
RB
368}
369
74467890
MY
370static int write_requires_after(FILE *f, const char *where, const char *opts) {
371 return write_dependency(f, where, opts,
372 "x-systemd.requires\0", STRV_MAKE_CONST("Requires", "After"));
ae325185
RB
373}
374
21aa180b 375static int write_wants_after(FILE *f, const char *where, const char *opts) {
376 return write_dependency(f, where, opts,
377 "x-systemd.wants\0", STRV_MAKE_CONST("Wants", "After"));
378}
379
74467890
MY
380static int write_before(FILE *f, const char *where, const char *opts) {
381 return write_dependency(f, where, opts,
382 "x-systemd.before\0", STRV_MAKE_CONST("Before"));
ae325185
RB
383}
384
74467890
MY
385static int write_mounts_for(
386 FILE *f,
387 const char *where,
388 const char *opts,
389 const char *filter,
390 const char *unit_setting) {
391
98bad05e 392 _cleanup_strv_free_ char **paths = NULL, **paths_escaped = NULL;
3519d230
KZ
393 int r;
394
395 assert(f);
74467890
MY
396 assert(where);
397 assert(filter);
398 assert(unit_setting);
3519d230 399
74467890 400 r = fstab_filter_options(opts, filter, NULL, NULL, &paths, NULL);
3519d230 401 if (r < 0)
74467890 402 return log_error_errno(r, "Failed to parse options for '%s': %m", where);
3519d230
KZ
403 if (r == 0)
404 return 0;
405
98bad05e
LP
406 r = specifier_escape_strv(paths, &paths_escaped);
407 if (r < 0)
74467890 408 return log_error_errno(r, "Failed to escape paths for '%s': %m", where);
98bad05e 409
74467890
MY
410 fprintf(f, "%s=", unit_setting);
411 fputstrv(f, paths_escaped, NULL, NULL);
412 fputc('\n', f);
3519d230
KZ
413
414 return 0;
415}
416
74467890 417static int write_extra_dependencies(FILE *f, const char *where, const char *opts) {
6371e69b
FB
418 int r;
419
420 assert(f);
421
74467890
MY
422 if (isempty(opts))
423 return 0;
424
425 r = write_after(f, where, opts);
426 if (r < 0)
427 return r;
428
429 r = write_requires_after(f, where, opts);
430 if (r < 0)
431 return r;
432
21aa180b 433 r = write_wants_after(f, where, opts);
434 if (r < 0)
435 return r;
436
74467890
MY
437 r = write_before(f, where, opts);
438 if (r < 0)
439 return r;
440
441 r = write_mounts_for(f, where, opts,
442 "x-systemd.requires-mounts-for\0", "RequiresMountsFor");
443 if (r < 0)
444 return r;
445
446 r = write_mounts_for(f, where, opts,
447 "x-systemd.wants-mounts-for\0", "WantsMountsFor");
448 if (r < 0)
449 return r;
6371e69b
FB
450
451 return 0;
452}
453
b3ee0148
MY
454static int mandatory_mount_drop_unapplicable_options(
455 MountPointFlags *flags,
456 const char *where,
457 const char *options,
458 char **ret_options) {
459
460 int r;
461
462 assert(flags);
463 assert(where);
b3ee0148
MY
464 assert(ret_options);
465
6a705f12
ZJS
466 if (!(*flags & (MOUNT_NOAUTO|MOUNT_NOFAIL|MOUNT_AUTOMOUNT)))
467 return strdup_to(ret_options, options);
b3ee0148
MY
468
469 log_debug("Mount '%s' is mandatory, ignoring 'noauto', 'nofail', and 'x-systemd.automount' options.",
470 where);
471
472 *flags &= ~(MOUNT_NOAUTO|MOUNT_NOFAIL|MOUNT_AUTOMOUNT);
473
474 r = fstab_filter_options(options, "noauto\0nofail\0x-systemd.automount\0", NULL, NULL, NULL, ret_options);
475 if (r < 0)
476 return r;
477
478 return 1;
479}
480
e8d2f6cd 481static int add_mount(
7772c177 482 const char *source,
91214a37 483 const char *dest,
e8d2f6cd
LP
484 const char *what,
485 const char *where,
634735b5 486 const char *original_where,
6db615c1 487 const char *fstype,
e8d2f6cd
LP
488 const char *opts,
489 int passno,
d2194e15 490 MountPointFlags flags,
b4bee568
LP
491 const char *target_unit,
492 const char *extra_after) {
e48fdd84 493
b3ee0148
MY
494 _cleanup_free_ char *name = NULL, *automount_name = NULL, *filtered = NULL, *where_escaped = NULL,
495 *opts_root_filtered = NULL;
be02c1cf 496 _cleanup_strv_free_ char **wanted_by = NULL, **required_by = NULL;
7fd1b19b 497 _cleanup_fclose_ FILE *f = NULL;
94192cda 498 int r;
6b1dc2bd
LP
499
500 assert(what);
501 assert(where);
da69e8e4 502 assert(target_unit);
5e398e54 503 assert(source);
6b1dc2bd 504
6db615c1 505 if (streq_ptr(fstype, "autofs"))
6b1dc2bd
LP
506 return 0;
507
508 if (!is_path(where)) {
509 log_warning("Mount point %s is not a valid path, ignoring.", where);
510 return 0;
511 }
512
513 if (mount_point_is_api(where) ||
514 mount_point_ignore(where))
515 return 0;
516
028a981c
ZJS
517 if (arg_sysroot_check) {
518 log_info("%s should be mounted in the initrd, will request daemon-reload.", where);
519 return true;
520 }
521
d6cef552 522 r = fstab_filter_options(opts, "x-systemd.wanted-by\0", NULL, NULL, &wanted_by, NULL);
be02c1cf
AR
523 if (r < 0)
524 return r;
525
d6cef552 526 r = fstab_filter_options(opts, "x-systemd.required-by\0", NULL, NULL, &required_by, NULL);
be02c1cf
AR
527 if (r < 0)
528 return r;
529
9f6c32ac 530 if (PATH_IN_SET(where, "/", "/usr")) {
b3ee0148
MY
531 r = mandatory_mount_drop_unapplicable_options(&flags, where, opts, &opts_root_filtered);
532 if (r < 0)
533 return r;
534 opts = opts_root_filtered;
535
be02c1cf 536 if (!strv_isempty(wanted_by))
9f6c32ac 537 log_debug("Ignoring 'x-systemd.wanted-by=' option for root/usr device.");
be02c1cf 538 if (!strv_isempty(required_by))
9f6c32ac 539 log_debug("Ignoring 'x-systemd.required-by=' option for root/usr device.");
2e852276 540
be02c1cf
AR
541 required_by = strv_free(required_by);
542 wanted_by = strv_free(wanted_by);
e48fdd84
LP
543 }
544
7410616c
LP
545 r = unit_name_from_path(where, ".mount", &name);
546 if (r < 0)
547 return log_error_errno(r, "Failed to generate unit name: %m");
6b1dc2bd 548
7772c177 549 r = generator_open_unit_file(dest, source, name, &f);
fb883e75
ZJS
550 if (r < 0)
551 return r;
0d536673 552
5e398e54 553 fprintf(f,
c3834f9b 554 "[Unit]\n"
a7e88558
LP
555 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n"
556 "SourcePath=%s\n",
c3834f9b 557 source);
6b1dc2bd 558
d2194e15 559 if (STRPTR_IN_SET(fstype, "nfs", "nfs4") && !(flags & MOUNT_AUTOMOUNT) &&
65e1dee7
N
560 fstab_test_yes_no_option(opts, "bg\0" "fg\0")) {
561 /* The default retry timeout that mount.nfs uses for 'bg' mounts
562 * is 10000 minutes, where as it uses 2 minutes for 'fg' mounts.
563 * As we are making 'bg' mounts look like an 'fg' mount to
564 * mount.nfs (so systemd can manage the job-control aspects of 'bg'),
565 * we need to explicitly preserve that default, and also ensure
566 * the systemd mount-timeout doesn't interfere.
5a12d1ca 567 * By placing these options first, they can be overridden by
65e1dee7 568 * settings in /etc/fstab. */
e66d2eee 569 opts = strjoina("x-systemd.mount-timeout=infinity,retry=10000,nofail,", opts, ",fg");
d2194e15 570 SET_FLAG(flags, MOUNT_NOFAIL, true);
65e1dee7
N
571 }
572
39a4c452
MY
573 if (!strv_isempty(wanted_by) || !strv_isempty(required_by)) {
574 /* If x-systemd.{wanted,required}-by= is specified, target_unit is not used */
575 target_unit = NULL;
576
577 /* Don't set default ordering dependencies on local-fs.target or remote-fs.target, but we
578 * still need to conflict with umount.target. */
579 fputs("DefaultDependencies=no\n"
580 "Conflicts=umount.target\n"
581 "Before=umount.target\n",
582 f);
583 }
584
74467890 585 r = write_extra_dependencies(f, where, opts);
6371e69b
FB
586 if (r < 0)
587 return r;
3519d230 588
da69e8e4 589 /* Order the mount unit we generate relative to target_unit, so that DefaultDependencies= on the
fa138f5e 590 * target unit won't affect us. */
39a4c452 591 if (target_unit && !FLAGS_SET(flags, MOUNT_NOFAIL))
da69e8e4 592 fprintf(f, "Before=%s\n", target_unit);
fa138f5e 593
b4bee568
LP
594 if (extra_after)
595 fprintf(f, "After=%s\n", extra_after);
596
e48fdd84 597 if (passno != 0) {
bed3ff8e 598 r = generator_write_fsck_deps(f, dest, what, where, fstype, opts);
e48fdd84
LP
599 if (r < 0)
600 return r;
601 }
64e70e4b 602
a7e88558
LP
603 r = generator_write_blockdev_dependency(f, what);
604 if (r < 0)
605 return r;
606
607 fprintf(f,
608 "\n"
609 "[Mount]\n");
610
b2efed52
ZJS
611 r = write_what(f, what);
612 if (r < 0)
613 return r;
614
634735b5
CW
615 if (original_where)
616 fprintf(f, "# Canonicalized from %s\n", original_where);
98bad05e
LP
617
618 where_escaped = specifier_escape(where);
619 if (!where_escaped)
620 return log_oom();
621 fprintf(f, "Where=%s\n", where_escaped);
6db615c1 622
98bad05e 623 if (!isempty(fstype) && !streq(fstype, "auto")) {
c2b2df60 624 _cleanup_free_ char *t = NULL;
98bad05e
LP
625
626 t = specifier_escape(fstype);
627 if (!t)
628 return -ENOMEM;
629
630 fprintf(f, "Type=%s\n", t);
631 }
6b1dc2bd 632
9a097ece 633 r = generator_write_device_timeout(dest, what, opts, &filtered);
29686440
ZJS
634 if (r < 0)
635 return r;
636
1d8ce413 637 r = generator_write_network_device_deps(dest, what, where, opts);
4195077a
MK
638 if (r < 0)
639 return r;
640
9cf22035
LF
641 if (in_initrd() && path_equal(where, "/sysroot") && is_device_path(what)) {
642 r = generator_write_initrd_root_device_deps(dest, what);
643 if (r < 0)
644 return r;
645 }
646
149e05c7 647 r = generator_write_mount_timeout(f, where, opts);
110773f6
CH
648 if (r < 0)
649 return r;
650
d5cc4be2
LP
651 r = write_options(f, filtered);
652 if (r < 0)
653 return r;
5e398e54 654
d2194e15 655 if (flags & MOUNT_RW_ONLY)
f42aa416
MH
656 fprintf(f, "ReadWriteOnly=yes\n");
657
4652c56c
ZJS
658 r = fflush_and_check(f);
659 if (r < 0)
fb883e75 660 return log_error_errno(r, "Failed to write unit file %s: %m", name);
6b1dc2bd 661
d2194e15 662 if (flags & MOUNT_MAKEFS) {
da495a03
ZJS
663 r = generator_hook_up_mkfs(dest, what, where, fstype);
664 if (r < 0)
665 return r;
666 }
667
d2194e15 668 if (flags & MOUNT_GROWFS) {
da69e8e4 669 r = generator_hook_up_growfs(dest, where, target_unit);
7f2806d5
ZJS
670 if (r < 0)
671 return r;
672 }
673
04959faa 674 if (flags & MOUNT_PCRFS) {
be8f478c 675 r = efi_measured_uki(LOG_WARNING);
2f6c52b9 676 if (r == 0)
6c51b49c 677 log_debug("Kernel stub did not measure kernel image into PCR, skipping userspace measurement, too.");
2f6c52b9 678 else if (r > 0) {
6c51b49c
LP
679 r = generator_hook_up_pcrfs(dest, where, target_unit);
680 if (r < 0)
681 return r;
682 }
04959faa
LP
683 }
684
f872373a
LP
685 if (flags & MOUNT_VALIDATEFS) {
686 r = generator_hook_up_validatefs(dest, where, target_unit);
687 if (r < 0)
688 return r;
689 }
690
fc5c6ecc
TB
691 if (flags & MOUNT_QUOTA) {
692 r = generator_hook_up_quotacheck(dest, what, where, target_unit, fstype);
693 if (r < 0) {
694 if (r != -EOPNOTSUPP)
695 return r;
696 } else {
697 r = generator_hook_up_quotaon(dest, where, target_unit);
698 if (r < 0)
699 return r;
700 }
701 }
702
39a4c452 703 if (FLAGS_SET(flags, MOUNT_AUTOMOUNT)) {
7410616c
LP
704 r = unit_name_from_path(where, ".automount", &automount_name);
705 if (r < 0)
706 return log_error_errno(r, "Failed to generate unit name: %m");
6b1dc2bd 707
8a7033ac 708 f = safe_fclose(f);
6b1dc2bd 709
7772c177 710 r = generator_open_unit_file(dest, source, automount_name, &f);
fb883e75
ZJS
711 if (r < 0)
712 return r;
0d536673 713
6b1dc2bd 714 fprintf(f,
6b1dc2bd 715 "[Unit]\n"
c3834f9b
LP
716 "SourcePath=%s\n"
717 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
700e07ff
HH
718 source);
719
700e07ff 720 fprintf(f,
bd10a84b 721 "\n"
6b1dc2bd
LP
722 "[Automount]\n"
723 "Where=%s\n",
98bad05e 724 where_escaped);
6b1dc2bd 725
336b5c61 726 r = write_idle_timeout(f, where, opts);
deb0a77c
MO
727 if (r < 0)
728 return r;
729
4652c56c
ZJS
730 r = fflush_and_check(f);
731 if (r < 0)
fb883e75 732 return log_error_errno(r, "Failed to write unit file %s: %m", automount_name);
39a4c452 733 }
6b1dc2bd 734
39a4c452
MY
735 if (target_unit) {
736 assert(strv_isempty(wanted_by));
737 assert(strv_isempty(required_by));
738
739 /* noauto has no effect if x-systemd.automount is used */
740 if (!FLAGS_SET(flags, MOUNT_NOAUTO) || automount_name) {
741 r = generator_add_symlink(dest, target_unit,
742 FLAGS_SET(flags, MOUNT_NOFAIL) ? "wants" : "requires",
743 automount_name ?: name);
744 if (r < 0)
745 return r;
746 }
747 } else {
748 const char *unit_name = automount_name ?: name;
749
750 STRV_FOREACH(s, wanted_by) {
751 r = generator_add_symlink(dest, *s, "wants", unit_name);
752 if (r < 0)
753 return r;
754 }
755
756 STRV_FOREACH(s, required_by) {
757 r = generator_add_symlink(dest, *s, "requires", unit_name);
758 if (r < 0)
759 return r;
760 }
761
762 if ((flags & (MOUNT_NOAUTO|MOUNT_NOFAIL)) != 0)
763 log_warning("x-systemd.wanted-by= and/or x-systemd.required-by= specified, 'noauto' and 'nofail' have no effect.");
6b1dc2bd
LP
764 }
765
028a981c
ZJS
766 return true;
767}
768
769static int do_daemon_reload(void) {
770 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
028a981c
ZJS
771 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
772 int r, k;
773
774 log_debug("Calling org.freedesktop.systemd1.Manager.Reload()...");
775
776 r = bus_connect_system_systemd(&bus);
777 if (r < 0)
778 return log_error_errno(r, "Failed to get D-Bus connection: %m");
779
a9399358 780 r = bus_service_manager_reload(bus);
028a981c 781 if (r < 0)
a9399358 782 return r;
028a981c
ZJS
783
784 /* We need to requeue the two targets so that any new units which previously were not part of the
785 * targets, and which we now added, will be started. */
786
787 r = 0;
788 FOREACH_STRING(unit, SPECIAL_INITRD_FS_TARGET, SPECIAL_SWAP_TARGET) {
789 log_info("Requesting %s/start/replace...", unit);
790
59228d0d 791 k = bus_call_method(bus, bus_systemd_mgr, "StartUnit", &error, NULL, "ss", unit, "replace");
028a981c
ZJS
792 if (k < 0) {
793 log_error_errno(k, "Failed to (re)start %s: %s", unit, bus_error_message(&error, r));
9ef648cc 794 RET_GATHER(r, k);
028a981c
ZJS
795 }
796 }
797
798 return r;
6b1dc2bd
LP
799}
800
99e3d476
ZJS
801static const char* sysroot_fstab_path(void) {
802 return getenv("SYSTEMD_SYSROOT_FSTAB") ?: "/sysroot/etc/fstab";
803}
804
cfeb4d37
YW
805static bool sysfs_check(void) {
806 static int cached = -1;
807 int r;
808
809 if (cached < 0) {
efb9b3ba 810 r = secure_getenv_bool("SYSTEMD_SYSFS_CHECK");
cfeb4d37
YW
811 if (r < 0 && r != -ENXIO)
812 log_debug_errno(r, "Failed to parse $SYSTEMD_SYSFS_CHECK, ignoring: %m");
813 cached = r != 0;
814 }
815
816 return cached;
817}
818
7369c037
MY
819static int add_sysusr_sysroot_usr_bind_mount(const char *source, bool validatefs) {
820 log_debug("Synthesizing entry what=/sysusr/usr where=/sysroot/usr opts=bind validatefs=%s", yes_no(validatefs));
821
dfce61dd 822 return add_mount(source,
b4bee568
LP
823 arg_dest,
824 "/sysusr/usr",
825 "/sysroot/usr",
826 /* original_where= */ NULL,
827 /* fstype= */ NULL,
828 "bind",
829 /* passno= */ 0,
7369c037 830 validatefs ? MOUNT_VALIDATEFS : 0,
b4bee568
LP
831 SPECIAL_INITRD_FS_TARGET,
832 /* extra_after= */ NULL);
dfce61dd
LF
833}
834
cfeb4d37
YW
835static MountPointFlags fstab_options_to_flags(const char *options, bool is_swap) {
836 MountPointFlags flags = 0;
837
c521ce42
MY
838 if (isempty(options))
839 return 0;
840
cfeb4d37
YW
841 if (fstab_test_option(options, "x-systemd.makefs\0"))
842 flags |= MOUNT_MAKEFS;
843 if (fstab_test_option(options, "x-systemd.growfs\0"))
844 flags |= MOUNT_GROWFS;
845 if (fstab_test_option(options, "x-systemd.pcrfs\0"))
846 flags |= MOUNT_PCRFS;
f872373a
LP
847 if (fstab_test_option(options, "x-systemd.validatefs\0"))
848 flags |= MOUNT_VALIDATEFS;
fc5c6ecc
TB
849 if (fstab_test_option(options, "usrquota\0" "grpquota\0" "quota\0" "usrjquota\0" "grpjquota\0" "prjquota\0"))
850 flags |= MOUNT_QUOTA;
cfeb4d37
YW
851 if (fstab_test_yes_no_option(options, "noauto\0" "auto\0"))
852 flags |= MOUNT_NOAUTO;
853 if (fstab_test_yes_no_option(options, "nofail\0" "fail\0"))
854 flags |= MOUNT_NOFAIL;
855
856 if (!is_swap) {
857 if (fstab_test_option(options, "x-systemd.rw-only\0"))
858 flags |= MOUNT_RW_ONLY;
859 if (fstab_test_option(options,
860 "comment=systemd.automount\0"
861 "x-systemd.automount\0"))
862 flags |= MOUNT_AUTOMOUNT;
863 }
864
865 return flags;
866}
867
8f88e573 868static int canonicalize_mount_path(const char *path, const char *type, bool prefix_sysroot, char **ret) {
b5fd3956
MY
869 _cleanup_free_ char *p = NULL;
870 bool changed;
871 int r;
872
873 assert(path);
874 assert(type);
875 assert(STR_IN_SET(type, "where", "what"));
876 assert(ret);
877
878 // FIXME: when chase() learns to chase non-existent paths, use this here and drop the prefixing with
879 // /sysroot on error below.
8f88e573 880 r = chase(path, prefix_sysroot ? "/sysroot" : NULL, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &p, NULL);
b5fd3956
MY
881 if (r < 0) {
882 log_debug_errno(r, "Failed to chase '%s', using as-is: %m", path);
883
8f88e573 884 if (prefix_sysroot)
b5fd3956
MY
885 p = path_join("/sysroot", path);
886 else
887 p = strdup(path);
888 if (!p)
889 return log_oom();
890
891 path_simplify(p);
892 }
893
894 changed = !streq(path, p);
895 if (changed)
896 log_debug("Canonicalized %s=%s to %s", type, path, p);
897
898 *ret = TAKE_PTR(p);
899 return changed;
900}
901
cfeb4d37
YW
902static int parse_fstab_one(
903 const char *source,
904 const char *what_original,
905 const char *where_original,
906 const char *fstype,
907 const char *options,
908 int passno,
8f88e573 909 bool prefix_sysroot,
22f5a825 910 bool accept_root, /* This takes an effect only when prefix_sysroot is true. */
55365b0a 911 bool use_swap_enabled) {
cfeb4d37 912
1e9b2e4f 913 _cleanup_free_ char *what = NULL, *where = NULL, *opts = NULL;
cfeb4d37 914 MountPointFlags flags;
b5fd3956 915 bool is_swap, where_changed;
cfeb4d37
YW
916 int r;
917
918 assert(what_original);
cfeb4d37 919 assert(fstype);
cfeb4d37 920
22f5a825 921 if (prefix_sysroot && !mount_in_initrd(where_original, options, accept_root))
cfeb4d37
YW
922 return 0;
923
256604cc 924 is_swap = streq_ptr(fstype, "swap");
55365b0a 925 if (is_swap && use_swap_enabled && !arg_swap_enabled) {
3aed2593 926 log_info("Swap unit generation disabled on kernel command line, ignoring swap entry for %s.", what_original);
94456233
YW
927 return 0;
928 }
256604cc 929
cfeb4d37
YW
930 what = fstab_node_to_udev_node(what_original);
931 if (!what)
932 return log_oom();
933
934 if (path_is_read_only_fs("/sys") > 0 &&
935 (streq(what, "sysfs") ||
936 (sysfs_check() && is_device_path(what)))) {
937 log_info("/sys/ is read-only (running in a container?), ignoring mount for %s.", what);
938 return 0;
939 }
940
256604cc
YW
941 flags = fstab_options_to_flags(options, is_swap);
942
943 if (is_swap)
944 return add_swap(source, what, options, flags);
945
239cce38
YW
946 if (passno < 0)
947 passno = is_device_path(what);
948
256604cc
YW
949 assert(where_original); /* 'where' is not necessary for swap entry. */
950
6742eca1 951 if (!is_path(where_original)) {
b5fd3956 952 log_warning("Mount point %s is not a valid path, ignoring.", where_original);
6742eca1
YW
953 return 0;
954 }
cfeb4d37 955
6742eca1
YW
956 /* Follow symlinks here; see 5261ba901845c084de5a8fd06500ed09bfb0bd80 which makes sense for
957 * mount units, but causes problems since it historically worked to have symlinks in e.g.
958 * /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case
959 * where a symlink refers to another mount target; this works assuming the sub-mountpoint
b5fd3956 960 * target is the final directory. */
8f88e573 961 r = canonicalize_mount_path(where_original, "where", prefix_sysroot, &where);
b5fd3956
MY
962 if (r < 0)
963 return r;
964 where_changed = r > 0;
cfeb4d37 965
8f88e573 966 if (prefix_sysroot && fstab_is_bind(options, fstype)) {
b5fd3956
MY
967 /* When in initrd, the source of bind mount needs to be prepended with /sysroot as well. */
968 _cleanup_free_ char *p = NULL;
6742eca1 969
8f88e573 970 r = canonicalize_mount_path(what, "what", prefix_sysroot, &p);
b5fd3956
MY
971 if (r < 0)
972 return r;
cfeb4d37 973
b5fd3956
MY
974 free_and_replace(what, p);
975 }
6742eca1 976
f872373a 977 log_debug("Found entry what=%s where=%s type=%s makefs=%s growfs=%s pcrfs=%s validatefs=%s noauto=%s nofail=%s",
cfeb4d37 978 what, where, strna(fstype),
f872373a
LP
979 yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS),
980 yes_no(flags & MOUNT_PCRFS), yes_no(flags & MOUNT_VALIDATEFS),
cfeb4d37
YW
981 yes_no(flags & MOUNT_NOAUTO), yes_no(flags & MOUNT_NOFAIL));
982
b5fd3956 983 bool is_sysroot = in_initrd() && path_equal(where, "/sysroot");
cfeb4d37
YW
984 /* See comment from add_sysroot_usr_mount() about the need for extra indirection in case /usr needs
985 * to be mounted in order for the root fs to be synthesized based on configuration included in /usr/,
986 * e.g. systemd-repart. */
b5fd3956 987 bool is_sysroot_usr = in_initrd() && path_equal(where, "/sysroot/usr");
cfeb4d37
YW
988
989 const char *target_unit =
cfeb4d37
YW
990 is_sysroot ? SPECIAL_INITRD_ROOT_FS_TARGET :
991 is_sysroot_usr ? SPECIAL_INITRD_USR_FS_TARGET :
b93d9e06 992 prefix_sysroot ? SPECIAL_INITRD_FS_TARGET :
cfeb4d37
YW
993 mount_is_network(fstype, options) ? SPECIAL_REMOTE_FS_TARGET :
994 SPECIAL_LOCAL_FS_TARGET;
995
b3ee0148
MY
996 /* nofail, noauto and x-systemd.automount don't make sense for critical filesystems we must mount in initrd. */
997 if (is_sysroot || is_sysroot_usr) {
998 r = mandatory_mount_drop_unapplicable_options(&flags, where, options, &opts);
1e9b2e4f
MS
999 if (r < 0)
1000 return r;
1e9b2e4f
MS
1001 options = opts;
1002 }
1003
cfeb4d37
YW
1004 r = add_mount(source,
1005 arg_dest,
1006 what,
b5fd3956
MY
1007 is_sysroot_usr ? "/sysusr/usr" : where,
1008 !is_sysroot_usr && where_changed ? where_original : NULL,
cfeb4d37
YW
1009 fstype,
1010 options,
1011 passno,
7369c037 1012 flags & ~(is_sysroot_usr ? MOUNT_VALIDATEFS : 0),
b4bee568
LP
1013 target_unit,
1014 /* extra_after= */ NULL);
cfeb4d37
YW
1015 if (r <= 0)
1016 return r;
1017
1018 if (is_sysroot_usr) {
7369c037 1019 r = add_sysusr_sysroot_usr_bind_mount(source, flags & MOUNT_VALIDATEFS);
cfeb4d37
YW
1020 if (r < 0)
1021 return r;
1022 }
1023
1024 return true;
1025}
1026
8f88e573 1027static int parse_fstab(bool prefix_sysroot) {
6db615c1 1028 _cleanup_endmntent_ FILE *f = NULL;
ed4ad488 1029 const char *fstab;
6b1dc2bd 1030 struct mntent *me;
cfeb4d37 1031 int r, ret = 0;
6b1dc2bd 1032
8f88e573 1033 if (prefix_sysroot)
99e3d476 1034 fstab = sysroot_fstab_path();
028a981c 1035 else {
99e3d476 1036 fstab = fstab_path();
028a981c
ZJS
1037 assert(!arg_sysroot_check);
1038 }
99e3d476 1039
ed4ad488 1040 log_debug("Parsing %s...", fstab);
01a0f7d0 1041
ed4ad488 1042 f = setmntent(fstab, "re");
6b1dc2bd
LP
1043 if (!f) {
1044 if (errno == ENOENT)
1045 return 0;
1046
ed4ad488 1047 return log_error_errno(errno, "Failed to open %s: %m", fstab);
6b1dc2bd
LP
1048 }
1049
1050 while ((me = getmntent(f))) {
55365b0a
YW
1051 r = parse_fstab_one(fstab,
1052 me->mnt_fsname, me->mnt_dir, me->mnt_type, me->mnt_opts, me->mnt_passno,
22f5a825
YW
1053 prefix_sysroot,
1054 /* accept_root = */ false,
1055 /* use_swap_enabled = */ true);
cfeb4d37
YW
1056 if (r < 0 && ret >= 0)
1057 ret = r;
1058 if (arg_sysroot_check && r > 0)
028a981c 1059 return true; /* We found a mount or swap that would be started… */
6b1dc2bd
LP
1060 }
1061
cfeb4d37 1062 return ret;
6b1dc2bd
LP
1063}
1064
fe721669 1065static int mount_source_is_nfsroot(const char *what) {
77b8e92d
YW
1066 union in_addr_union u;
1067 const char *sep, *a;
1068 int r;
1069
fe721669 1070 assert(what);
77b8e92d
YW
1071
1072 /* From dracut.cmdline(7).
1073 *
1074 * root=[<server-ip>:]<root-dir>[:<nfs-options>]
1075 * root=nfs:[<server-ip>:]<root-dir>[:<nfs-options>],
1076 * root=nfs4:[<server-ip>:]<root-dir>[:<nfs-options>],
1077 * root={dhcp|dhcp6}
1078 *
1079 * mount nfs share from <server-ip>:/<root-dir>, if no server-ip is given, use dhcp next_server.
1080 * If server-ip is an IPv6 address it has to be put in brackets, e.g. [2001:DB8::1]. NFS options
1081 * can be appended with the prefix ":" or "," and are separated by ",". */
1082
fe721669
LP
1083 if (path_equal(what, "/dev/nfs") ||
1084 STR_IN_SET(what, "dhcp", "dhcp6") ||
1085 STARTSWITH_SET(what, "nfs:", "nfs4:"))
77b8e92d
YW
1086 return true;
1087
1088 /* IPv6 address */
fe721669
LP
1089 if (what[0] == '[') {
1090 sep = strchr(what + 1, ']');
77b8e92d
YW
1091 if (!sep)
1092 return -EINVAL;
1093
fe721669 1094 a = strndupa_safe(what + 1, sep - what - 1);
77b8e92d
YW
1095
1096 r = in_addr_from_string(AF_INET6, a, &u);
1097 if (r < 0)
1098 return r;
1099
1100 return true;
1101 }
1102
1103 /* IPv4 address */
fe721669 1104 sep = strchr(what, ':');
77b8e92d 1105 if (sep) {
fe721669 1106 a = strndupa_safe(what, sep - what);
77b8e92d
YW
1107
1108 if (in_addr_from_string(AF_INET, a, &u) >= 0)
1109 return true;
1110 }
1111
1112 /* root directory without address */
fe721669 1113 return path_is_absolute(what) && !path_startswith(what, "/dev");
77b8e92d
YW
1114}
1115
fe721669 1116static bool validate_root_or_usr_mount_source(const char *what, const char *switch_name) {
7163e1ca 1117 int r;
5e398e54 1118
fe721669
LP
1119 assert(switch_name);
1120
1121 if (isempty(what)) {
1122 log_debug("No %s switch specified on the kernel command line.", switch_name);
1123 return false;
135b5212 1124 }
5e398e54 1125
c05a132f
LP
1126 if (streq(what, "off")) {
1127 log_debug("Skipping %s directory handling, as this was explicitly turned off.", switch_name);
1128 return false;
1129 }
1130
7852e301 1131 if (parse_gpt_auto_root(switch_name, what) > 0) {
4981427c 1132 /* This is handled by gpt-auto-generator */
fe721669
LP
1133 log_debug("Skipping %s directory handling, as gpt-auto was requested.", switch_name);
1134 return false;
1135 }
1136
1137 if (streq(what, "fstab")) {
1138 /* This is handled by parse_fstab() */
1139 log_debug("Using initrd's fstab for %s configuration.", switch_name);
1140 return false;
138f4c69
LP
1141 }
1142
fe721669 1143 r = mount_source_is_nfsroot(what);
77b8e92d 1144 if (r < 0)
fe721669 1145 log_debug_errno(r, "Failed to determine if the %s directory is on NFS, assuming not: %m", switch_name);
77b8e92d 1146 else if (r > 0) {
3ade16c9 1147 /* This is handled by the kernel or the initrd */
fe721669
LP
1148 log_debug("Skipping %s directory handling, as root on NFS was requested.", switch_name);
1149 return false;
155e1bb4
YW
1150 }
1151
fe721669
LP
1152 if (startswith(what, "cifs://")) {
1153 log_debug("Skipping %s directory handling, as root on CIFS was requested.", switch_name);
1154 return false;
155e1bb4
YW
1155 }
1156
fe721669
LP
1157 if (startswith(what, "iscsi:")) {
1158 log_debug("Skipping %s directory handling, as root on iSCSI was requested.", switch_name);
1159 return false;
155e1bb4
YW
1160 }
1161
fe721669
LP
1162 if (startswith(what, "live:")) {
1163 log_debug("Skipping %s directory handling, as root on live image was requested.", switch_name);
1164 return false;
3ade16c9
HH
1165 }
1166
fe721669
LP
1167 return true;
1168}
1169
1170static int add_sysroot_mount(void) {
1171 _cleanup_free_ char *what = NULL;
1172 const char *extra_opts = NULL, *fstype = NULL;
e844ee07 1173 bool default_rw = true;
fe721669
LP
1174 MountPointFlags flags;
1175
1176 if (!validate_root_or_usr_mount_source(arg_root_what, "root="))
1177 return 0;
1178
b4bee568
LP
1179 const char *bind = startswith(arg_root_what, "bind:");
1180 if (bind) {
1181 if (!path_is_valid(bind) || !path_is_absolute(bind)) {
1182 log_debug("Invalid root=bind: source path, ignoring: %s", bind);
1183 return 0;
1184 }
1185
1186 what = strdup(bind);
1187 if (!what)
1188 return log_oom();
1189
1190 extra_opts = "bind";
1191
1192 } else if (streq(arg_root_what, "tmpfs")) {
725ad3b0
LP
1193 /* If root=tmpfs is specified, then take this as shortcut for a writable tmpfs mount as root */
1194
1195 what = strdup("rootfs"); /* just a pretty name, to show up in /proc/self/mountinfo */
1196 if (!what)
1197 return log_oom();
1198
5a12d1ca 1199 fstype = arg_root_fstype ?: "tmpfs"; /* tmpfs, unless overridden */
d3a57a08
LB
1200 if (streq(fstype, "tmpfs") && !fstab_test_option(arg_root_options, "mode\0"))
1201 extra_opts = "mode=0755"; /* root directory should not be world/group writable, unless overridden */
725ad3b0
LP
1202 } else {
1203
1204 what = fstab_node_to_udev_node(arg_root_what);
1205 if (!what)
1206 return log_oom();
1207
1208 fstype = arg_root_fstype; /* if not specified explicitly, don't default to anything here */
5a12d1ca 1209 default_rw = false; /* read-only, unless overridden */
725ad3b0 1210 }
093c2cfe 1211
b4bee568
LP
1212 _cleanup_free_ char *combined_options = NULL;
1213 if (strdup_to(&combined_options, arg_root_options) < 0)
1214 return log_oom();
1215
1216 if (arg_root_rw >= 0 || !fstab_test_option(combined_options, "ro\0" "rw\0"))
1217 if (!strextend_with_separator(&combined_options, ",", arg_root_rw > 0 || (arg_root_rw < 0 && default_rw) ? "rw" : "ro"))
1218 return log_oom();
1219
1220 if (extra_opts)
1221 if (!strextend_with_separator(&combined_options, ",", extra_opts))
1222 return log_oom();
5e398e54 1223
fba85a0e 1224 log_debug("Found entry what=%s where=/sysroot type=%s opts=%s", what, strna(fstype), strempty(combined_options));
7163e1ca 1225
e844ee07
MY
1226 /* Only honor x-systemd.makefs and .validatefs here, others are not relevant in initrd/not used
1227 * at all (also see mandatory_mount_drop_unapplicable_options()) */
1228 flags = fstab_options_to_flags(combined_options, /* is_swap = */ false) & (MOUNT_MAKEFS|MOUNT_VALIDATEFS);
01fdfbb8 1229
7772c177
ZJS
1230 return add_mount("/proc/cmdline",
1231 arg_dest,
91214a37 1232 what,
6db615c1 1233 "/sysroot",
b4bee568 1234 /* original_where= */ NULL,
725ad3b0 1235 fstype,
b4bee568
LP
1236 combined_options,
1237 /* passno= */ is_device_path(what) ? 1 : 0,
1238 flags,
1239 SPECIAL_INITRD_ROOT_FS_TARGET,
1240 "imports.target");
5e398e54
TG
1241}
1242
2e852276 1243static int add_sysroot_usr_mount(void) {
9f103625 1244 _cleanup_free_ char *what = NULL;
b4bee568 1245 const char *extra_opts = NULL;
e844ee07 1246 bool makefs, validatefs;
29a24ab2
LP
1247 int r;
1248
1249 /* Returns 0 if we didn't do anything, > 0 if we either generated a unit for the /usr/ mount, or we
1250 * know for sure something else did */
9f103625
TH
1251
1252 if (!arg_usr_what && !arg_usr_fstype && !arg_usr_options)
1253 return 0;
1254
1255 if (arg_root_what && !arg_usr_what) {
40472036 1256 /* Copy over the root device, in case the /usr mount just differs in a mount option (consider btrfs subvolumes) */
9f103625 1257 arg_usr_what = strdup(arg_root_what);
9f103625
TH
1258 if (!arg_usr_what)
1259 return log_oom();
1260 }
1261
1262 if (arg_root_fstype && !arg_usr_fstype) {
1263 arg_usr_fstype = strdup(arg_root_fstype);
9f103625
TH
1264 if (!arg_usr_fstype)
1265 return log_oom();
1266 }
1267
1268 if (arg_root_options && !arg_usr_options) {
1269 arg_usr_options = strdup(arg_root_options);
9f103625
TH
1270 if (!arg_usr_options)
1271 return log_oom();
1272 }
1273
fe721669 1274 if (!validate_root_or_usr_mount_source(arg_usr_what, "mount.usr="))
9f103625
TH
1275 return 0;
1276
b4bee568
LP
1277 const char *bind = startswith(arg_usr_what, "bind:");
1278 if (bind) {
1279 if (!path_is_valid(bind) || !path_is_absolute(bind)) {
1280 log_debug("Invalid mount.usr=bind: source path, ignoring: %s", bind);
1281 return 0;
1282 }
1283
1284 what = strdup(bind);
1285 if (!what)
1286 return log_oom();
1287
1288 extra_opts = "bind";
1289 } else {
1290 what = fstab_node_to_udev_node(arg_usr_what);
1291 if (!what)
1292 return log_oom();
1293 }
1294
1295 _cleanup_free_ char *combined_options = NULL;
1296 if (strdup_to(&combined_options, arg_usr_options) < 0)
47be5f07 1297 return log_oom();
9f103625 1298
b4bee568
LP
1299 if (!fstab_test_option(combined_options, "ro\0" "rw\0"))
1300 if (!strextend_with_separator(&combined_options, ",", arg_root_rw > 0 ? "rw" : "ro"))
1301 return log_oom();
1302
1303 if (extra_opts)
1304 if (!strextend_with_separator(&combined_options, ",", extra_opts))
1305 return log_oom();
9f103625 1306
29a24ab2
LP
1307 /* When mounting /usr from the initrd, we add an extra level of indirection: we first mount the /usr/
1308 * partition to /sysusr/usr/, and then afterwards bind mount that to /sysroot/usr/. We do this so
1309 * that we can cover for systems that initially only have a /usr/ around and where the root fs needs
1310 * to be synthesized, based on configuration included in /usr/, e.g. systemd-repart. Software like
1311 * this should order itself after initrd-usr-fs.target and before initrd-fs.target; and it should
1312 * look into both /sysusr/ and /sysroot/ for the configuration data to apply. */
1313
b4bee568 1314 log_debug("Found entry what=%s where=/sysusr/usr type=%s opts=%s", what, strna(arg_usr_fstype), strempty(combined_options));
29a24ab2 1315
e844ee07
MY
1316 /* Only honor x-systemd.makefs and .validatefs here, others are not relevant in initrd/not used
1317 * at all (also see mandatory_mount_drop_unapplicable_options()) */
b4bee568 1318 makefs = fstab_test_option(combined_options, "x-systemd.makefs\0");
e844ee07 1319 validatefs = fstab_test_option(combined_options, "x-systemd.validatefs\0");
01fdfbb8 1320
7772c177
ZJS
1321 r = add_mount("/proc/cmdline",
1322 arg_dest,
29a24ab2
LP
1323 what,
1324 "/sysusr/usr",
b4bee568 1325 /* original_where= */ NULL,
29a24ab2 1326 arg_usr_fstype,
b4bee568
LP
1327 combined_options,
1328 /* passno= */ is_device_path(what) ? 1 : 0,
e844ee07 1329 makefs ? MOUNT_MAKEFS : 0,
b4bee568
LP
1330 SPECIAL_INITRD_USR_FS_TARGET,
1331 "imports.target");
29a24ab2
LP
1332 if (r < 0)
1333 return r;
1334
e844ee07 1335 r = add_sysusr_sysroot_usr_bind_mount("/proc/cmdline", validatefs);
29a24ab2
LP
1336 if (r < 0)
1337 return r;
1338
1339 return 1;
1340}
1341
1342static int add_sysroot_usr_mount_or_fallback(void) {
1343 int r;
1344
1345 r = add_sysroot_usr_mount();
1346 if (r != 0)
1347 return r;
1348
1349 /* OK, so we didn't write anything out for /sysusr/usr/ nor /sysroot/usr/. In this case, let's make
1350 * sure that initrd-usr-fs.target is at least ordered after sysroot.mount so that services that order
de47cd06 1351 * themselves after it get the guarantee that /usr/ is definitely mounted somewhere. */
29a24ab2
LP
1352
1353 return generator_add_symlink(
1354 arg_dest,
1355 SPECIAL_INITRD_USR_FS_TARGET,
1356 "requires",
1357 "sysroot.mount");
9f103625
TH
1358}
1359
91214a37 1360static int add_volatile_root(void) {
1de7f825 1361
91214a37 1362 /* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is
1de7f825 1363 * requested (or as an overlayfs), leaving only /usr from the root mount inside. */
91214a37 1364
1de7f825 1365 if (!IN_SET(arg_volatile_mode, VOLATILE_YES, VOLATILE_OVERLAY))
00bb366d 1366 return 0;
91214a37 1367
00bb366d 1368 return generator_add_symlink(arg_dest, SPECIAL_INITRD_ROOT_FS_TARGET, "requires",
835cf75a 1369 SYSTEM_DATA_UNIT_DIR "/" SPECIAL_VOLATILE_ROOT_SERVICE);
91214a37
LP
1370}
1371
1372static int add_volatile_var(void) {
1373
1374 if (arg_volatile_mode != VOLATILE_STATE)
1375 return 0;
1376
1377 /* If requested, mount /var as tmpfs, but do so only if there's nothing else defined for this. */
1378
7772c177
ZJS
1379 return add_mount("/proc/cmdline",
1380 arg_dest_late,
91214a37
LP
1381 "tmpfs",
1382 "/var",
b4bee568 1383 /* original_where= */ NULL,
91214a37 1384 "tmpfs",
7d85383e 1385 "mode=0755" TMPFS_LIMITS_VAR,
b4bee568
LP
1386 /* passno= */ 0,
1387 /* flags= */ 0,
1388 SPECIAL_LOCAL_FS_TARGET,
1389 /* extra_after= */ NULL);
91214a37
LP
1390}
1391
55365b0a 1392static int add_mounts_from_cmdline(void) {
ba2f3ec8 1393 int r = 0;
55365b0a
YW
1394
1395 /* Handle each entries found in cmdline as a fstab entry. */
1396
1397 FOREACH_ARRAY(m, arg_mounts, arg_n_mounts) {
45c535dd
YW
1398 if (m->for_initrd && !in_initrd())
1399 continue;
1400
ba2f3ec8
MY
1401 RET_GATHER(r, parse_fstab_one("/proc/cmdline",
1402 m->what,
1403 m->where,
1404 m->fstype,
1405 m->options,
1406 /* passno = */ -1,
1407 /* prefix_sysroot = */ !m->for_initrd && in_initrd(),
1408 /* accept_root = */ true,
1409 /* use_swap_enabled = */ false));
55365b0a
YW
1410 }
1411
ba2f3ec8 1412 return r;
55365b0a
YW
1413}
1414
dfd10549 1415static int add_mounts_from_creds(bool prefix_sysroot) {
6ac62485
LP
1416 _cleanup_free_ void *b = NULL;
1417 struct mntent *me;
6ac62485 1418 size_t bs;
ba2f3ec8 1419 int r;
6ac62485 1420
dfd10549
YW
1421 assert(in_initrd() || !prefix_sysroot);
1422
6ac62485 1423 r = read_credential_with_decryption(
dfd10549 1424 in_initrd() && !prefix_sysroot ? "fstab.extra.initrd" : "fstab.extra",
6ac62485
LP
1425 &b, &bs);
1426 if (r <= 0)
1427 return r;
1428
1429 _cleanup_fclose_ FILE *f = NULL;
1430 f = fmemopen_unlocked(b, bs, "r");
1431 if (!f)
1432 return log_oom();
1433
ba2f3ec8 1434 r = 0;
6ac62485 1435
ba2f3ec8
MY
1436 while ((me = getmntent(f)))
1437 RET_GATHER(r, parse_fstab_one("/run/credentials",
1438 me->mnt_fsname,
1439 me->mnt_dir,
1440 me->mnt_type,
1441 me->mnt_opts,
1442 me->mnt_passno,
1443 /* prefix_sysroot = */ prefix_sysroot,
1444 /* accept_root = */ true,
1445 /* use_swap_enabled = */ true));
1446
1447 return r;
6ac62485
LP
1448}
1449
96287a49 1450static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
74df0fca 1451 int r;
94734142 1452
a8d3315b
YW
1453 assert(key);
1454
9f103625
TH
1455 /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
1456 * instance should take precedence. In the case of multiple rootflags=
1457 * or usrflags= the arguments should be concatenated */
6db615c1 1458
1d84ad94 1459 if (STR_IN_SET(key, "fstab", "rd.fstab")) {
e48fdd84 1460
1d84ad94 1461 r = value ? parse_boolean(value) : 1;
141a79f4 1462 if (r < 0)
059cb385 1463 log_warning("Failed to parse fstab switch %s. Ignoring.", value);
141a79f4 1464 else
b564224b 1465 arg_fstab_enabled = fstab_set_enabled(r);
94734142 1466
1d84ad94
LP
1467 } else if (streq(key, "root")) {
1468
1469 if (proc_cmdline_value_missing(key, value))
1470 return 0;
6db615c1 1471
338da501 1472 return free_and_strdup_warn(&arg_root_what, empty_to_null(value));
6db615c1 1473
1d84ad94
LP
1474 } else if (streq(key, "rootfstype")) {
1475
1476 if (proc_cmdline_value_missing(key, value))
1477 return 0;
6db615c1 1478
338da501 1479 return free_and_strdup_warn(&arg_root_fstype, empty_to_null(value));
6db615c1 1480
1d84ad94 1481 } else if (streq(key, "rootflags")) {
6db615c1 1482
1d84ad94
LP
1483 if (proc_cmdline_value_missing(key, value))
1484 return 0;
1485
c2bc710b 1486 if (!strextend_with_separator(&arg_root_options, ",", value))
6db615c1
LP
1487 return log_oom();
1488
2f3dfc6f
LP
1489 } else if (streq(key, "roothash")) {
1490
1491 if (proc_cmdline_value_missing(key, value))
1492 return 0;
1493
338da501 1494 return free_and_strdup_warn(&arg_root_hash, empty_to_null(value));
6db615c1 1495
1d84ad94
LP
1496 } else if (streq(key, "mount.usr")) {
1497
1498 if (proc_cmdline_value_missing(key, value))
1499 return 0;
9f103625 1500
338da501 1501 return free_and_strdup_warn(&arg_usr_what, empty_to_null(value));
9f103625 1502
1d84ad94
LP
1503 } else if (streq(key, "mount.usrfstype")) {
1504
1505 if (proc_cmdline_value_missing(key, value))
1506 return 0;
9f103625 1507
338da501 1508 return free_and_strdup_warn(&arg_usr_fstype, empty_to_null(value));
9f103625 1509
1d84ad94 1510 } else if (streq(key, "mount.usrflags")) {
9f103625 1511
1d84ad94
LP
1512 if (proc_cmdline_value_missing(key, value))
1513 return 0;
1514
c2bc710b 1515 if (!strextend_with_separator(&arg_usr_options, ",", value))
9f103625
TH
1516 return log_oom();
1517
c1b9e3df
MB
1518 } else if (streq(key, "usrhash")) {
1519
1520 if (proc_cmdline_value_missing(key, value))
1521 return 0;
1522
338da501 1523 return free_and_strdup_warn(&arg_usr_hash, empty_to_null(value));
c1b9e3df 1524
6db615c1
LP
1525 } else if (streq(key, "rw") && !value)
1526 arg_root_rw = true;
1527 else if (streq(key, "ro") && !value)
1528 arg_root_rw = false;
91214a37
LP
1529 else if (streq(key, "systemd.volatile")) {
1530 VolatileMode m;
1531
1532 if (value) {
1533 m = volatile_mode_from_string(value);
1534 if (m < 0)
7211c853 1535 log_warning_errno(m, "Failed to parse systemd.volatile= argument: %s", value);
91214a37
LP
1536 else
1537 arg_volatile_mode = m;
1538 } else
1539 arg_volatile_mode = VOLATILE_YES;
567a5307 1540
1541 } else if (streq(key, "systemd.swap")) {
1542
1543 r = value ? parse_boolean(value) : 1;
1544 if (r < 0)
1545 log_warning("Failed to parse systemd.swap switch %s. Ignoring.", value);
1546 else
1547 arg_swap_enabled = r;
200268c6
DDM
1548
1549 } else if (streq(key, "systemd.verity")) {
1550
1551 r = value ? parse_boolean(value) : 1;
1552 if (r < 0)
1553 log_warning("Failed to parse systemd.verity= kernel command line switch %s. Ignoring.", value);
1554 else
1555 arg_verity = r;
55365b0a 1556
45c535dd 1557 } else if (STR_IN_SET(key, "systemd.mount-extra", "rd.systemd.mount-extra")) {
55365b0a
YW
1558
1559 if (proc_cmdline_value_missing(key, value))
1560 return 0;
1561
45c535dd 1562 r = mount_array_add(startswith(key, "rd."), value);
55365b0a
YW
1563 if (r < 0)
1564 log_warning("Failed to parse systemd.mount-extra= option, ignoring: %s", value);
1565
45c535dd 1566 } else if (STR_IN_SET(key, "systemd.swap-extra", "rd.systemd.swap-extra")) {
55365b0a
YW
1567
1568 if (proc_cmdline_value_missing(key, value))
1569 return 0;
1570
45c535dd 1571 r = mount_array_add_swap(startswith(key, "rd."), value);
55365b0a
YW
1572 if (r < 0)
1573 log_warning("Failed to parse systemd.swap-extra= option, ignoring: %s", value);
91214a37 1574 }
94734142 1575
d0aa9ce5 1576 return 0;
94734142
LP
1577}
1578
2130a2e5
LP
1579static int determine_device(
1580 char **what,
1581 int *rw,
1582 char **options,
1583 const char *hash,
1584 const char *name) {
2f3dfc6f 1585
c1b9e3df
MB
1586 assert(what);
1587 assert(name);
1588
1589 /* If we have a hash but no device then Verity is used, and we use the DM device. */
1590 if (*what)
2f3dfc6f
LP
1591 return 0;
1592
c1b9e3df 1593 if (!hash)
2f3dfc6f
LP
1594 return 0;
1595
200268c6
DDM
1596 if (!arg_verity)
1597 return 0;
1598
c1b9e3df
MB
1599 *what = path_join("/dev/mapper/", name);
1600 if (!*what)
2f3dfc6f
LP
1601 return log_oom();
1602
2130a2e5
LP
1603 /* Verity is always read-only */
1604 if (rw)
1605 *rw = false;
1606 if (options && !strextend_with_separator(options, ",", "ro"))
1607 return log_oom();
2f3dfc6f 1608
2130a2e5 1609 log_info("Using verity %s device %s.", name, *what);
2f3dfc6f
LP
1610 return 1;
1611}
1612
c1b9e3df 1613static int determine_root(void) {
2130a2e5 1614 return determine_device(&arg_root_what, &arg_root_rw, NULL, arg_root_hash, "root");
c1b9e3df
MB
1615}
1616
1617static int determine_usr(void) {
2130a2e5 1618 return determine_device(&arg_usr_what, NULL, &arg_usr_options, arg_usr_hash, "usr");
c1b9e3df
MB
1619}
1620
028a981c
ZJS
1621/* If arg_sysroot_check is false, run as generator in the usual fashion.
1622 * If it is true, check /sysroot/etc/fstab for any units that we'd want to mount
1623 * in the initrd, and call daemon-reload. We will get reinvoked as a generator,
1624 * with /sysroot/etc/fstab available, and then we can write additional units based
1625 * on that file. */
1626static int run_generator(void) {
4c7cc696 1627 int r;
6b1dc2bd 1628
1d84ad94 1629 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
b5884878 1630 if (r < 0)
da927ba9 1631 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
94734142 1632
2f3dfc6f 1633 (void) determine_root();
c1b9e3df 1634 (void) determine_usr();
2f3dfc6f 1635
028a981c 1636 if (arg_sysroot_check) {
8f88e573 1637 r = parse_fstab(/* prefix_sysroot = */ true);
028a981c
ZJS
1638 if (r == 0)
1639 log_debug("Nothing interesting found, not doing daemon-reload.");
1640 if (r > 0)
1641 r = do_daemon_reload();
1642 return r;
1643 }
1644
ba2f3ec8
MY
1645 r = 0;
1646
9f103625
TH
1647 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
1648 if (in_initrd()) {
ba2f3ec8 1649 RET_GATHER(r, add_sysroot_mount());
003cba39 1650
ba2f3ec8 1651 RET_GATHER(r, add_sysroot_usr_mount_or_fallback());
91214a37 1652
ba2f3ec8
MY
1653 RET_GATHER(r, add_volatile_root());
1654 } else
1655 RET_GATHER(r, add_volatile_var());
5e398e54 1656
e48fdd84
LP
1657 /* Honour /etc/fstab only when that's enabled */
1658 if (arg_fstab_enabled) {
e48fdd84 1659 /* Parse the local /etc/fstab, possibly from the initrd */
ba2f3ec8 1660 RET_GATHER(r, parse_fstab(/* prefix_sysroot = */ false));
ac4785b0 1661
e48fdd84 1662 /* If running in the initrd also parse the /etc/fstab from the host */
2572957e 1663 if (in_initrd())
ba2f3ec8 1664 RET_GATHER(r, parse_fstab(/* prefix_sysroot = */ true));
9b69569d 1665 else
ba2f3ec8 1666 RET_GATHER(r, generator_enable_remount_fs_service(arg_dest));
e48fdd84 1667 }
6b1dc2bd 1668
ba2f3ec8 1669 RET_GATHER(r, add_mounts_from_cmdline());
55365b0a 1670
ba2f3ec8 1671 RET_GATHER(r, add_mounts_from_creds(/* prefix_sysroot = */ false));
6ac62485 1672
ba2f3ec8
MY
1673 if (in_initrd())
1674 RET_GATHER(r, add_mounts_from_creds(/* prefix_sysroot = */ true));
dfd10549 1675
ba2f3ec8 1676 return r;
6b1dc2bd 1677}
a4ef3e4d 1678
028a981c
ZJS
1679static int run(int argc, char **argv) {
1680 arg_sysroot_check = invoked_as(argv, "systemd-sysroot-fstab-check");
1681
1682 if (arg_sysroot_check) {
1683 /* Run as in systemd-sysroot-fstab-check mode */
1684 log_setup();
1685
1686 if (strv_length(argv) > 1)
1687 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1688 "This program takes no arguments.");
1689 if (!in_initrd())
1690 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1691 "This program is only useful in the initrd.");
1692 } else {
1693 /* Run in generator mode */
1694 log_setup_generator();
1695
1696 if (!IN_SET(strv_length(argv), 2, 4))
1697 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1698 "This program takes one or three arguments.");
1699
1700 arg_dest = ASSERT_PTR(argv[1]);
1701 arg_dest_late = ASSERT_PTR(argv[argc > 3 ? 3 : 1]);
1702 }
1703
1704 return run_generator();
1705}
1706
1707DEFINE_MAIN_FUNCTION(run);