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