]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fstab-generator/fstab-generator.c
Merge pull request #7610 from poettering/stdio-nolocking
[thirdparty/systemd.git] / src / fstab-generator / fstab-generator.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2012 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <mntent.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <stdio_ext.h>
27
28 #include "alloc-util.h"
29 #include "fd-util.h"
30 #include "fileio.h"
31 #include "fs-util.h"
32 #include "fstab-util.h"
33 #include "generator.h"
34 #include "log.h"
35 #include "mkdir.h"
36 #include "mount-setup.h"
37 #include "mount-util.h"
38 #include "parse-util.h"
39 #include "path-util.h"
40 #include "proc-cmdline.h"
41 #include "special.h"
42 #include "specifier.h"
43 #include "stat-util.h"
44 #include "string-util.h"
45 #include "strv.h"
46 #include "unit-name.h"
47 #include "util.h"
48 #include "virt.h"
49 #include "volatile-util.h"
50
51 typedef enum MountpointFlags {
52 NOAUTO = 1 << 0,
53 NOFAIL = 1 << 1,
54 AUTOMOUNT = 1 << 2,
55 MAKEFS = 1 << 3,
56 GROWFS = 1 << 4,
57 } MountpointFlags;
58
59 static const char *arg_dest = "/tmp";
60 static const char *arg_dest_late = "/tmp";
61 static bool arg_fstab_enabled = true;
62 static char *arg_root_what = NULL;
63 static char *arg_root_fstype = NULL;
64 static char *arg_root_options = NULL;
65 static char *arg_root_hash = NULL;
66 static int arg_root_rw = -1;
67 static char *arg_usr_what = NULL;
68 static char *arg_usr_fstype = NULL;
69 static char *arg_usr_options = NULL;
70 static VolatileMode arg_volatile_mode = _VOLATILE_MODE_INVALID;
71
72 static int write_options(FILE *f, const char *options) {
73 _cleanup_free_ char *o = NULL;
74
75 if (isempty(options))
76 return 0;
77
78 if (streq(options, "defaults"))
79 return 0;
80
81 o = specifier_escape(options);
82 if (!o)
83 return log_oom();
84
85 fprintf(f, "Options=%s\n", o);
86 return 1;
87 }
88
89 static int write_what(FILE *f, const char *what) {
90 _cleanup_free_ char *w = NULL;
91
92 w = specifier_escape(what);
93 if (!w)
94 return log_oom();
95
96 fprintf(f, "What=%s\n", w);
97 return 1;
98 }
99
100 static int add_swap(
101 const char *what,
102 struct mntent *me,
103 MountpointFlags flags) {
104
105 _cleanup_free_ char *name = NULL, *unit = NULL;
106 _cleanup_fclose_ FILE *f = NULL;
107 int r;
108
109 assert(what);
110 assert(me);
111
112 if (access("/proc/swaps", F_OK) < 0) {
113 log_info("Swap not supported, ignoring fstab swap entry for %s.", what);
114 return 0;
115 }
116
117 if (detect_container() > 0) {
118 log_info("Running in a container, ignoring fstab swap entry for %s.", what);
119 return 0;
120 }
121
122 r = unit_name_from_path(what, ".swap", &name);
123 if (r < 0)
124 return log_error_errno(r, "Failed to generate unit name: %m");
125
126 unit = strjoin(arg_dest, "/", name);
127 if (!unit)
128 return log_oom();
129
130 f = fopen(unit, "wxe");
131 if (!f)
132 return log_error_errno(errno,
133 errno == EEXIST ?
134 "Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?" :
135 "Failed to create unit file %s: %m",
136 unit);
137
138 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
139
140 fputs("# Automatically generated by systemd-fstab-generator\n\n"
141 "[Unit]\n"
142 "SourcePath=/etc/fstab\n"
143 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n"
144 "[Swap]\n", f);
145
146 r = write_what(f, what);
147 if (r < 0)
148 return r;
149
150 r = write_options(f, me->mnt_opts);
151 if (r < 0)
152 return r;
153
154 r = fflush_and_check(f);
155 if (r < 0)
156 return log_error_errno(r, "Failed to write unit file %s: %m", unit);
157
158 /* use what as where, to have a nicer error message */
159 r = generator_write_timeouts(arg_dest, what, what, me->mnt_opts, NULL);
160 if (r < 0)
161 return r;
162
163 if (flags & MAKEFS) {
164 r = generator_hook_up_mkswap(arg_dest, what);
165 if (r < 0)
166 return r;
167 }
168
169 if (flags & GROWFS)
170 /* TODO: swap devices must be wiped and recreated */
171 log_warning("%s: growing swap devices is currently unsupported.", what);
172
173 if (!(flags & NOAUTO)) {
174 r = generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET,
175 (flags & NOFAIL) ? "wants" : "requires", name);
176 if (r < 0)
177 return r;
178 }
179
180 return 0;
181 }
182
183 static bool mount_is_network(struct mntent *me) {
184 assert(me);
185
186 return fstab_test_option(me->mnt_opts, "_netdev\0") ||
187 fstype_is_network(me->mnt_type);
188 }
189
190 static bool mount_in_initrd(struct mntent *me) {
191 assert(me);
192
193 return fstab_test_option(me->mnt_opts, "x-initrd.mount\0") ||
194 streq(me->mnt_dir, "/usr");
195 }
196
197 static int write_timeout(FILE *f, const char *where, const char *opts,
198 const char *filter, const char *variable) {
199 _cleanup_free_ char *timeout = NULL;
200 char timespan[FORMAT_TIMESPAN_MAX];
201 usec_t u;
202 int r;
203
204 r = fstab_filter_options(opts, filter, NULL, &timeout, NULL);
205 if (r < 0)
206 return log_warning_errno(r, "Failed to parse options: %m");
207 if (r == 0)
208 return 0;
209
210 r = parse_sec_fix_0(timeout, &u);
211 if (r < 0) {
212 log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout);
213 return 0;
214 }
215
216 fprintf(f, "%s=%s\n", variable, format_timespan(timespan, sizeof(timespan), u, 0));
217
218 return 0;
219 }
220
221 static int write_idle_timeout(FILE *f, const char *where, const char *opts) {
222 return write_timeout(f, where, opts,
223 "x-systemd.idle-timeout\0", "TimeoutIdleSec");
224 }
225
226 static int write_mount_timeout(FILE *f, const char *where, const char *opts) {
227 return write_timeout(f, where, opts,
228 "x-systemd.mount-timeout\0", "TimeoutSec");
229 }
230
231 static int write_dependency(FILE *f, const char *opts,
232 const char *filter, const char *format) {
233 _cleanup_strv_free_ char **names = NULL, **units = NULL;
234 _cleanup_free_ char *res = NULL;
235 char **s;
236 int r;
237
238 assert(f);
239 assert(opts);
240
241 r = fstab_extract_values(opts, filter, &names);
242 if (r < 0)
243 return log_warning_errno(r, "Failed to parse options: %m");
244 if (r == 0)
245 return 0;
246
247 STRV_FOREACH(s, names) {
248 char *x;
249
250 r = unit_name_mangle_with_suffix(*s, UNIT_NAME_NOGLOB, ".mount", &x);
251 if (r < 0)
252 return log_error_errno(r, "Failed to generate unit name: %m");
253 r = strv_consume(&units, x);
254 if (r < 0)
255 return log_oom();
256 }
257
258 if (units) {
259 res = strv_join(units, " ");
260 if (!res)
261 return log_oom();
262 #pragma GCC diagnostic push
263 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
264 fprintf(f, format, res);
265 #pragma GCC diagnostic pop
266 }
267
268 return 0;
269 }
270
271 static int write_after(FILE *f, const char *opts) {
272 return write_dependency(f, opts, "x-systemd.after", "After=%1$s\n");
273 }
274
275 static int write_requires_after(FILE *f, const char *opts) {
276 return write_dependency(f, opts,
277 "x-systemd.requires", "After=%1$s\nRequires=%1$s\n");
278 }
279
280 static int write_before(FILE *f, const char *opts) {
281 return write_dependency(f, opts,
282 "x-systemd.before", "Before=%1$s\n");
283 }
284
285 static int write_requires_mounts_for(FILE *f, const char *opts) {
286 _cleanup_strv_free_ char **paths = NULL, **paths_escaped = NULL;
287 _cleanup_free_ char *res = NULL;
288 int r;
289
290 assert(f);
291 assert(opts);
292
293 r = fstab_extract_values(opts, "x-systemd.requires-mounts-for", &paths);
294 if (r < 0)
295 return log_warning_errno(r, "Failed to parse options: %m");
296 if (r == 0)
297 return 0;
298
299 r = specifier_escape_strv(paths, &paths_escaped);
300 if (r < 0)
301 return log_error_errno(r, "Failed to escape paths: %m");
302
303 res = strv_join(paths_escaped, " ");
304 if (!res)
305 return log_oom();
306
307 fprintf(f, "RequiresMountsFor=%s\n", res);
308
309 return 0;
310 }
311
312 static int add_mount(
313 const char *dest,
314 const char *what,
315 const char *where,
316 const char *original_where,
317 const char *fstype,
318 const char *opts,
319 int passno,
320 MountpointFlags flags,
321 const char *post,
322 const char *source) {
323
324 _cleanup_free_ char
325 *name = NULL,
326 *automount_name = NULL, *automount_unit = NULL,
327 *filtered = NULL,
328 *where_escaped = NULL;
329 const char *unit;
330 _cleanup_fclose_ FILE *f = NULL;
331 int r;
332
333 assert(what);
334 assert(where);
335 assert(opts);
336 assert(post);
337 assert(source);
338
339 if (streq_ptr(fstype, "autofs"))
340 return 0;
341
342 if (!is_path(where)) {
343 log_warning("Mount point %s is not a valid path, ignoring.", where);
344 return 0;
345 }
346
347 if (mount_point_is_api(where) ||
348 mount_point_ignore(where))
349 return 0;
350
351 if (path_equal(where, "/")) {
352 if (flags & NOAUTO)
353 log_warning("Ignoring \"noauto\" for root device");
354 if (flags & NOFAIL)
355 log_warning("Ignoring \"nofail\" for root device");
356 if (flags & AUTOMOUNT)
357 log_warning("Ignoring automount option for root device");
358
359 SET_FLAG(flags, NOAUTO | NOFAIL | AUTOMOUNT, false);
360 }
361
362 r = unit_name_from_path(where, ".mount", &name);
363 if (r < 0)
364 return log_error_errno(r, "Failed to generate unit name: %m");
365
366 unit = strjoina(dest, "/", name);
367
368 f = fopen(unit, "wxe");
369 if (!f)
370 return log_error_errno(errno,
371 errno == EEXIST ?
372 "Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?" :
373 "Failed to create unit file %s: %m",
374 unit);
375
376 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
377
378 fprintf(f,
379 "# Automatically generated by systemd-fstab-generator\n\n"
380 "[Unit]\n"
381 "SourcePath=%s\n"
382 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
383 source);
384
385 if (STRPTR_IN_SET(fstype, "nfs", "nfs4") && !(flags & AUTOMOUNT) &&
386 fstab_test_yes_no_option(opts, "bg\0" "fg\0")) {
387 /* The default retry timeout that mount.nfs uses for 'bg' mounts
388 * is 10000 minutes, where as it uses 2 minutes for 'fg' mounts.
389 * As we are making 'bg' mounts look like an 'fg' mount to
390 * mount.nfs (so systemd can manage the job-control aspects of 'bg'),
391 * we need to explicitly preserve that default, and also ensure
392 * the systemd mount-timeout doesn't interfere.
393 * By placing these options first, they can be over-ridden by
394 * settings in /etc/fstab. */
395 opts = strjoina("x-systemd.mount-timeout=infinity,retry=10000,", opts, ",fg");
396 SET_FLAG(flags, NOFAIL, true);
397 }
398
399 if (!(flags & NOFAIL) && !(flags & AUTOMOUNT))
400 fprintf(f, "Before=%s\n", post);
401
402 if (!(flags & AUTOMOUNT) && opts) {
403 r = write_after(f, opts);
404 if (r < 0)
405 return r;
406 r = write_requires_after(f, opts);
407 if (r < 0)
408 return r;
409 r = write_before(f, opts);
410 if (r < 0)
411 return r;
412 r = write_requires_mounts_for(f, opts);
413 if (r < 0)
414 return r;
415 }
416
417 if (passno != 0) {
418 r = generator_write_fsck_deps(f, dest, what, where, fstype);
419 if (r < 0)
420 return r;
421 }
422
423 fprintf(f, "\n[Mount]\n");
424 if (original_where)
425 fprintf(f, "# Canonicalized from %s\n", original_where);
426
427 where_escaped = specifier_escape(where);
428 if (!where_escaped)
429 return log_oom();
430 fprintf(f, "Where=%s\n", where_escaped);
431
432 r = write_what(f, what);
433 if (r < 0)
434 return r;
435
436 if (!isempty(fstype) && !streq(fstype, "auto")) {
437 _cleanup_free_ char *t;
438
439 t = specifier_escape(fstype);
440 if (!t)
441 return -ENOMEM;
442
443 fprintf(f, "Type=%s\n", t);
444 }
445
446 r = generator_write_timeouts(dest, what, where, opts, &filtered);
447 if (r < 0)
448 return r;
449
450 r = generator_write_device_deps(dest, what, where, opts);
451 if (r < 0)
452 return r;
453
454 r = write_mount_timeout(f, where, opts);
455 if (r < 0)
456 return r;
457
458 r = write_options(f, filtered);
459 if (r < 0)
460 return r;
461
462 r = fflush_and_check(f);
463 if (r < 0)
464 return log_error_errno(r, "Failed to write unit file %s: %m", unit);
465
466 if (flags & MAKEFS) {
467 r = generator_hook_up_mkfs(dest, what, where, fstype);
468 if (r < 0)
469 return r;
470 }
471
472 if (flags & GROWFS) {
473 r = generator_hook_up_growfs(dest, where, post);
474 if (r < 0)
475 return r;
476 }
477
478 if (!(flags & NOAUTO) && !(flags & AUTOMOUNT)) {
479 r = generator_add_symlink(dest, post,
480 (flags & NOFAIL) ? "wants" : "requires", name);
481 if (r < 0)
482 return r;
483 }
484
485 if (flags & AUTOMOUNT) {
486 r = unit_name_from_path(where, ".automount", &automount_name);
487 if (r < 0)
488 return log_error_errno(r, "Failed to generate unit name: %m");
489
490 automount_unit = strjoin(dest, "/", automount_name);
491 if (!automount_unit)
492 return log_oom();
493
494 fclose(f);
495 f = fopen(automount_unit, "wxe");
496 if (!f)
497 return log_error_errno(errno, "Failed to create unit file %s: %m", automount_unit);
498
499 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
500
501 fprintf(f,
502 "# Automatically generated by systemd-fstab-generator\n\n"
503 "[Unit]\n"
504 "SourcePath=%s\n"
505 "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n",
506 source);
507
508 fprintf(f, "Before=%s\n", post);
509
510 if (opts) {
511 r = write_after(f, opts);
512 if (r < 0)
513 return r;
514 r = write_requires_after(f, opts);
515 if (r < 0)
516 return r;
517 r = write_before(f, opts);
518 if (r < 0)
519 return r;
520 r = write_requires_mounts_for(f, opts);
521 if (r < 0)
522 return r;
523 }
524
525 fprintf(f,
526 "\n"
527 "[Automount]\n"
528 "Where=%s\n",
529 where_escaped);
530
531 r = write_idle_timeout(f, where, opts);
532 if (r < 0)
533 return r;
534
535 r = fflush_and_check(f);
536 if (r < 0)
537 return log_error_errno(r, "Failed to write unit file %s: %m", automount_unit);
538
539 r = generator_add_symlink(dest, post,
540 (flags & NOFAIL) ? "wants" : "requires", automount_name);
541 if (r < 0)
542 return r;
543 }
544
545 return 0;
546 }
547
548 static int parse_fstab(bool initrd) {
549 _cleanup_endmntent_ FILE *f = NULL;
550 const char *fstab_path;
551 struct mntent *me;
552 int r = 0;
553
554 fstab_path = initrd ? "/sysroot/etc/fstab" : "/etc/fstab";
555 f = setmntent(fstab_path, "re");
556 if (!f) {
557 if (errno == ENOENT)
558 return 0;
559
560 return log_error_errno(errno, "Failed to open %s: %m", fstab_path);
561 }
562
563 while ((me = getmntent(f))) {
564 _cleanup_free_ char *where = NULL, *what = NULL, *canonical_where = NULL;
565 bool makefs, growfs, noauto, nofail;
566 int k;
567
568 if (initrd && !mount_in_initrd(me))
569 continue;
570
571 what = fstab_node_to_udev_node(me->mnt_fsname);
572 if (!what)
573 return log_oom();
574
575 if (is_device_path(what) && path_is_read_only_fs("sys") > 0) {
576 log_info("Running in a container, ignoring fstab device entry for %s.", what);
577 continue;
578 }
579
580 where = strdup(me->mnt_dir);
581 if (!where)
582 return log_oom();
583
584 if (is_path(where)) {
585 path_kill_slashes(where);
586 /* Follow symlinks here; see 5261ba901845c084de5a8fd06500ed09bfb0bd80 which makes sense for
587 * mount units, but causes problems since it historically worked to have symlinks in e.g.
588 * /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case
589 * where a symlink refers to another mount target; this works assuming the sub-mountpoint
590 * target is the final directory.
591 */
592 r = chase_symlinks(where, initrd ? "/sysroot" : NULL,
593 CHASE_PREFIX_ROOT | CHASE_NONEXISTENT,
594 &canonical_where);
595 if (r < 0)
596 /* In this case for now we continue on as if it wasn't a symlink */
597 log_warning_errno(r, "Failed to read symlink target for %s: %m", where);
598 else {
599 if (streq(canonical_where, where))
600 canonical_where = mfree(canonical_where);
601 else
602 log_debug("Canonicalized what=%s where=%s to %s",
603 what, where, canonical_where);
604 }
605 }
606
607 makefs = fstab_test_option(me->mnt_opts, "x-systemd.makefs\0");
608 growfs = fstab_test_option(me->mnt_opts, "x-systemd.growfs\0");
609 noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0");
610 nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0");
611 log_debug("Found entry what=%s where=%s type=%s makefs=%s nofail=%s noauto=%s",
612 what, where, me->mnt_type,
613 yes_no(makefs),
614 yes_no(noauto), yes_no(nofail));
615
616 if (streq(me->mnt_type, "swap"))
617 k = add_swap(what, me,
618 makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL);
619 else {
620 bool automount;
621 const char *post;
622
623 automount = fstab_test_option(me->mnt_opts,
624 "comment=systemd.automount\0"
625 "x-systemd.automount\0");
626 if (initrd)
627 post = SPECIAL_INITRD_FS_TARGET;
628 else if (mount_is_network(me))
629 post = SPECIAL_REMOTE_FS_TARGET;
630 else
631 post = SPECIAL_LOCAL_FS_TARGET;
632
633 k = add_mount(arg_dest,
634 what,
635 canonical_where ?: where,
636 canonical_where ? where: NULL,
637 me->mnt_type,
638 me->mnt_opts,
639 me->mnt_passno,
640 makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT,
641 post,
642 fstab_path);
643 }
644
645 if (r >= 0 && k < 0)
646 r = k;
647 }
648
649 return r;
650 }
651
652 static int add_sysroot_mount(void) {
653 _cleanup_free_ char *what = NULL;
654 const char *opts;
655 int r;
656
657 if (isempty(arg_root_what)) {
658 log_debug("Could not find a root= entry on the kernel command line.");
659 return 0;
660 }
661
662 if (streq(arg_root_what, "gpt-auto")) {
663 /* This is handled by the gpt-auto generator */
664 log_debug("Skipping root directory handling, as gpt-auto was requested.");
665 return 0;
666 }
667
668 if (path_equal(arg_root_what, "/dev/nfs")) {
669 /* This is handled by the kernel or the initrd */
670 log_debug("Skipping root directory handling, as /dev/nfs was requested.");
671 return 0;
672 }
673
674 what = fstab_node_to_udev_node(arg_root_what);
675 if (!what)
676 return log_oom();
677
678 if (!arg_root_options)
679 opts = arg_root_rw > 0 ? "rw" : "ro";
680 else if (arg_root_rw >= 0 ||
681 !fstab_test_option(arg_root_options, "ro\0" "rw\0"))
682 opts = strjoina(arg_root_options, ",", arg_root_rw > 0 ? "rw" : "ro");
683 else
684 opts = arg_root_options;
685
686 log_debug("Found entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype));
687
688 if (is_device_path(what)) {
689 r = generator_write_initrd_root_device_deps(arg_dest, what);
690 if (r < 0)
691 return r;
692 }
693
694 return add_mount(arg_dest,
695 what,
696 "/sysroot",
697 NULL,
698 arg_root_fstype,
699 opts,
700 is_device_path(what) ? 1 : 0, /* passno */
701 0, /* makefs off, growfs off, noauto off, nofail off, automount off */
702 SPECIAL_INITRD_ROOT_FS_TARGET,
703 "/proc/cmdline");
704 }
705
706 static int add_sysroot_usr_mount(void) {
707 _cleanup_free_ char *what = NULL;
708 const char *opts;
709
710 if (!arg_usr_what && !arg_usr_fstype && !arg_usr_options)
711 return 0;
712
713 if (arg_root_what && !arg_usr_what) {
714 /* Copy over the root device, in case the /usr mount just differs in a mount option (consider btrfs subvolumes) */
715 arg_usr_what = strdup(arg_root_what);
716 if (!arg_usr_what)
717 return log_oom();
718 }
719
720 if (arg_root_fstype && !arg_usr_fstype) {
721 arg_usr_fstype = strdup(arg_root_fstype);
722 if (!arg_usr_fstype)
723 return log_oom();
724 }
725
726 if (arg_root_options && !arg_usr_options) {
727 arg_usr_options = strdup(arg_root_options);
728 if (!arg_usr_options)
729 return log_oom();
730 }
731
732 if (!arg_usr_what)
733 return 0;
734
735 what = fstab_node_to_udev_node(arg_usr_what);
736 if (!what)
737 return log_oom();
738
739 if (!arg_usr_options)
740 opts = arg_root_rw > 0 ? "rw" : "ro";
741 else if (!fstab_test_option(arg_usr_options, "ro\0" "rw\0"))
742 opts = strjoina(arg_usr_options, ",", arg_root_rw > 0 ? "rw" : "ro");
743 else
744 opts = arg_usr_options;
745
746 log_debug("Found entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype));
747 return add_mount(arg_dest,
748 what,
749 "/sysroot/usr",
750 NULL,
751 arg_usr_fstype,
752 opts,
753 is_device_path(what) ? 1 : 0, /* passno */
754 0,
755 SPECIAL_INITRD_FS_TARGET,
756 "/proc/cmdline");
757 }
758
759 static int add_volatile_root(void) {
760 const char *from, *to;
761
762 if (arg_volatile_mode != VOLATILE_YES)
763 return 0;
764
765 /* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is
766 * requested, leaving only /usr from the root mount inside. */
767
768 from = strjoina(SYSTEM_DATA_UNIT_PATH "/systemd-volatile-root.service");
769 to = strjoina(arg_dest, "/" SPECIAL_INITRD_ROOT_FS_TARGET, ".requires/systemd-volatile-root.service");
770
771 (void) mkdir_parents(to, 0755);
772
773 if (symlink(from, to) < 0)
774 return log_error_errno(errno, "Failed to hook in volatile remount service: %m");
775
776 return 0;
777 }
778
779 static int add_volatile_var(void) {
780
781 if (arg_volatile_mode != VOLATILE_STATE)
782 return 0;
783
784 /* If requested, mount /var as tmpfs, but do so only if there's nothing else defined for this. */
785
786 return add_mount(arg_dest_late,
787 "tmpfs",
788 "/var",
789 NULL,
790 "tmpfs",
791 "mode=0755",
792 0,
793 0,
794 SPECIAL_LOCAL_FS_TARGET,
795 "/proc/cmdline");
796 }
797
798 static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
799 int r;
800
801 /* root=, usr=, usrfstype= and roofstype= may occur more than once, the last
802 * instance should take precedence. In the case of multiple rootflags=
803 * or usrflags= the arguments should be concatenated */
804
805 if (STR_IN_SET(key, "fstab", "rd.fstab")) {
806
807 r = value ? parse_boolean(value) : 1;
808 if (r < 0)
809 log_warning("Failed to parse fstab switch %s. Ignoring.", value);
810 else
811 arg_fstab_enabled = r;
812
813 } else if (streq(key, "root")) {
814
815 if (proc_cmdline_value_missing(key, value))
816 return 0;
817
818 if (free_and_strdup(&arg_root_what, value) < 0)
819 return log_oom();
820
821 } else if (streq(key, "rootfstype")) {
822
823 if (proc_cmdline_value_missing(key, value))
824 return 0;
825
826 if (free_and_strdup(&arg_root_fstype, value) < 0)
827 return log_oom();
828
829 } else if (streq(key, "rootflags")) {
830
831 if (proc_cmdline_value_missing(key, value))
832 return 0;
833
834 if (!strextend_with_separator(&arg_root_options, ",", value, NULL))
835 return log_oom();
836
837 } else if (streq(key, "roothash")) {
838
839 if (proc_cmdline_value_missing(key, value))
840 return 0;
841
842 if (free_and_strdup(&arg_root_hash, value) < 0)
843 return log_oom();
844
845 } else if (streq(key, "mount.usr")) {
846
847 if (proc_cmdline_value_missing(key, value))
848 return 0;
849
850 if (free_and_strdup(&arg_usr_what, value) < 0)
851 return log_oom();
852
853 } else if (streq(key, "mount.usrfstype")) {
854
855 if (proc_cmdline_value_missing(key, value))
856 return 0;
857
858 if (free_and_strdup(&arg_usr_fstype, value) < 0)
859 return log_oom();
860
861 } else if (streq(key, "mount.usrflags")) {
862
863 if (proc_cmdline_value_missing(key, value))
864 return 0;
865
866 if (!strextend_with_separator(&arg_usr_options, ",", value, NULL))
867 return log_oom();
868
869 } else if (streq(key, "rw") && !value)
870 arg_root_rw = true;
871 else if (streq(key, "ro") && !value)
872 arg_root_rw = false;
873 else if (streq(key, "systemd.volatile")) {
874 VolatileMode m;
875
876 if (value) {
877 m = volatile_mode_from_string(value);
878 if (m < 0)
879 log_warning("Failed to parse systemd.volatile= argument: %s", value);
880 else
881 arg_volatile_mode = m;
882 } else
883 arg_volatile_mode = VOLATILE_YES;
884 }
885
886 return 0;
887 }
888
889 static int determine_root(void) {
890 /* If we have a root hash but no root device then Verity is used, and we use the "root" DM device as root. */
891
892 if (arg_root_what)
893 return 0;
894
895 if (!arg_root_hash)
896 return 0;
897
898 arg_root_what = strdup("/dev/mapper/root");
899 if (!arg_root_what)
900 return log_oom();
901
902 log_info("Using verity root device %s.", arg_root_what);
903
904 return 1;
905 }
906
907 int main(int argc, char *argv[]) {
908 int r = 0;
909
910 if (argc > 1 && argc != 4) {
911 log_error("This program takes three or no arguments.");
912 return EXIT_FAILURE;
913 }
914
915 if (argc > 1)
916 arg_dest = argv[1];
917 if (argc > 3)
918 arg_dest_late = argv[3];
919
920 log_set_target(LOG_TARGET_SAFE);
921 log_parse_environment();
922 log_open();
923
924 umask(0022);
925
926 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
927 if (r < 0)
928 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
929
930 (void) determine_root();
931
932 /* Always honour root= and usr= in the kernel command line if we are in an initrd */
933 if (in_initrd()) {
934 int k;
935
936 r = add_sysroot_mount();
937
938 k = add_sysroot_usr_mount();
939 if (k < 0)
940 r = k;
941
942 k = add_volatile_root();
943 if (k < 0)
944 r = k;
945 } else
946 r = add_volatile_var();
947
948 /* Honour /etc/fstab only when that's enabled */
949 if (arg_fstab_enabled) {
950 int k;
951
952 log_debug("Parsing /etc/fstab");
953
954 /* Parse the local /etc/fstab, possibly from the initrd */
955 k = parse_fstab(false);
956 if (k < 0)
957 r = k;
958
959 /* If running in the initrd also parse the /etc/fstab from the host */
960 if (in_initrd()) {
961 log_debug("Parsing /sysroot/etc/fstab");
962
963 k = parse_fstab(true);
964 if (k < 0)
965 r = k;
966 }
967 }
968
969 free(arg_root_what);
970 free(arg_root_fstype);
971 free(arg_root_options);
972 free(arg_root_hash);
973
974 free(arg_usr_what);
975 free(arg_usr_fstype);
976 free(arg_usr_options);
977
978 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
979 }