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