]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/gpt-auto-generator/gpt-auto-generator.c
Move missing_xyz.h for glibc headers to src/basic/include/ (#37960)
[thirdparty/systemd.git] / src / gpt-auto-generator / gpt-auto-generator.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <sys/file.h>
4
5#include "sd-id128.h"
6
7#include "alloc-util.h"
8#include "blockdev-util.h"
9#include "device-util.h"
10#include "devnum-util.h"
11#include "dissect-image.h"
12#include "dropin.h"
13#include "efi-loader.h"
14#include "efivars.h"
15#include "errno-util.h"
16#include "factory-reset.h"
17#include "fd-util.h"
18#include "fileio.h"
19#include "fstab-util.h"
20#include "generator.h"
21#include "gpt.h"
22#include "image-policy.h"
23#include "initrd-util.h"
24#include "loop-util.h"
25#include "mountpoint-util.h"
26#include "parse-util.h"
27#include "path-util.h"
28#include "proc-cmdline.h"
29#include "special.h"
30#include "stat-util.h"
31#include "string-util.h"
32#include "strv.h"
33#include "time-util.h"
34#include "unit-name.h"
35#include "virt.h"
36
37typedef enum MountPointFlags {
38 MOUNT_RW = 1 << 0,
39 MOUNT_GROWFS = 1 << 1,
40 MOUNT_MEASURE = 1 << 2,
41 MOUNT_VALIDATEFS = 1 << 3,
42} MountPointFlags;
43
44static const char *arg_dest = NULL;
45static const char *arg_dest_late = NULL;
46static bool arg_enabled = true;
47static GptAutoRoot arg_auto_root = _GPT_AUTO_ROOT_INVALID;
48static GptAutoRoot arg_auto_usr = _GPT_AUTO_ROOT_INVALID;
49static bool arg_swap_enabled = true;
50static char *arg_root_fstype = NULL;
51static char *arg_root_options = NULL;
52static int arg_root_rw = -1;
53static char *arg_usr_fstype = NULL;
54static char *arg_usr_options = NULL;
55static ImagePolicy *arg_image_policy = NULL;
56static ImageFilter *arg_image_filter = NULL;
57
58STATIC_DESTRUCTOR_REGISTER(arg_root_fstype, freep);
59STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep);
60STATIC_DESTRUCTOR_REGISTER(arg_usr_fstype, freep);
61STATIC_DESTRUCTOR_REGISTER(arg_usr_options, freep);
62STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
63STATIC_DESTRUCTOR_REGISTER(arg_image_filter, image_filter_freep);
64
65#define LOADER_PARTITION_IDLE_USEC (120 * USEC_PER_SEC)
66
67static int add_cryptsetup(
68 const char *id,
69 const char *what,
70 const char *mount_opts,
71 MountPointFlags flags,
72 bool require,
73 char **ret_device) {
74
75#if HAVE_LIBCRYPTSETUP
76 _cleanup_free_ char *e = NULL, *n = NULL, *d = NULL, *options = NULL;
77 _cleanup_fclose_ FILE *f = NULL;
78 int r;
79
80 assert(id);
81 assert(what);
82
83 r = unit_name_from_path(what, ".device", &d);
84 if (r < 0)
85 return log_error_errno(r, "Failed to generate unit name: %m");
86
87 e = unit_name_escape(id);
88 if (!e)
89 return log_oom();
90
91 r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
92 if (r < 0)
93 return log_error_errno(r, "Failed to generate unit name: %m");
94
95 r = generator_open_unit_file(arg_dest_late, /* source = */ NULL, n, &f);
96 if (r < 0)
97 return r;
98
99 r = generator_write_cryptsetup_unit_section(f, NULL);
100 if (r < 0)
101 return r;
102
103 fprintf(f,
104 "Before=umount.target cryptsetup.target\n"
105 "Conflicts=umount.target\n"
106 "BindsTo=%s\n"
107 "After=%s\n",
108 d, d);
109
110 if (!FLAGS_SET(flags, MOUNT_RW)) {
111 options = strdup("read-only");
112 if (!options)
113 return log_oom();
114 }
115
116 r = efi_measured_uki(LOG_WARNING);
117 if (r > 0)
118 /* Enable TPM2 based unlocking automatically, if we have a TPM. See #30176. */
119 if (!strextend_with_separator(&options, ",", "tpm2-device=auto"))
120 return log_oom();
121
122 if (FLAGS_SET(flags, MOUNT_MEASURE)) {
123 /* We only measure the root volume key into PCR 15 if we are booted with sd-stub (i.e. in a
124 * UKI), and sd-stub measured the UKI. We do this in order not to step into people's own PCR
125 * assignment, under the assumption that people who are fine to use sd-stub with its PCR
126 * assignments are also OK with our PCR 15 use here. */
127 if (r > 0)
128 if (!strextend_with_separator(&options, ",", "tpm2-measure-pcr=yes"))
129 return log_oom();
130 if (r == 0)
131 log_debug("Will not measure volume key of volume '%s', not booted via systemd-stub with measurements enabled.", id);
132 }
133
134 r = generator_write_cryptsetup_service_section(f, id, what, NULL, options);
135 if (r < 0)
136 return r;
137
138 r = fflush_and_check(f);
139 if (r < 0)
140 return log_error_errno(r, "Failed to write file %s: %m", n);
141
142 r = generator_write_device_timeout(arg_dest_late, what, mount_opts, /* filtered = */ NULL);
143 if (r < 0)
144 return r;
145
146 r = generator_add_symlink(arg_dest_late, d, "wants", n);
147 if (r < 0)
148 return r;
149
150 const char *dmname = strjoina("dev-mapper-", e, ".device");
151
152 if (require) {
153 r = generator_add_symlink(arg_dest_late, "cryptsetup.target", "requires", n);
154 if (r < 0)
155 return r;
156
157 r = generator_add_symlink(arg_dest_late, dmname, "requires", n);
158 if (r < 0)
159 return r;
160 }
161
162 r = write_drop_in_format(arg_dest_late, dmname, 50, "job-timeout",
163 "# Automatically generated by systemd-gpt-auto-generator\n\n"
164 "[Unit]\n"
165 "JobTimeoutSec=infinity"); /* the binary handles timeouts anyway */
166 if (r < 0)
167 log_warning_errno(r, "Failed to write device timeout drop-in, ignoring: %m");
168
169 if (ret_device) {
170 char *s;
171
172 s = path_join("/dev/mapper", id);
173 if (!s)
174 return log_oom();
175
176 *ret_device = s;
177 }
178
179 return 0;
180#else
181 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
182 "Partition is encrypted, but systemd-gpt-auto-generator was compiled without libcryptsetup support.");
183#endif
184}
185
186#if ENABLE_EFI
187static int add_veritysetup(
188 const char *id,
189 const char *data_what,
190 const char *hash_what,
191 const char *mount_opts) {
192
193#if HAVE_LIBCRYPTSETUP
194 int r;
195
196 assert(id);
197 assert(data_what);
198 assert(hash_what);
199
200 _cleanup_free_ char *dd = NULL;
201 r = unit_name_from_path(data_what, ".device", &dd);
202 if (r < 0)
203 return log_error_errno(r, "Failed to generate data device unit name: %m");
204
205 _cleanup_free_ char *dh = NULL;
206 r = unit_name_from_path(hash_what, ".device", &dh);
207 if (r < 0)
208 return log_error_errno(r, "Failed to generate hash device unit name: %m");
209
210 _cleanup_free_ char *e = unit_name_escape(id);
211 if (!e)
212 return log_oom();
213
214 _cleanup_free_ char *n = NULL;
215 r = unit_name_build("systemd-veritysetup", e, ".service", &n);
216 if (r < 0)
217 return log_error_errno(r, "Failed to generate unit name: %m");
218
219 _cleanup_fclose_ FILE *f = NULL;
220 r = generator_open_unit_file(arg_dest_late, /* source= */ NULL, n, &f);
221 if (r < 0)
222 return r;
223
224 r = generator_write_veritysetup_unit_section(f, /* source= */ NULL);
225 if (r < 0)
226 return r;
227
228 fprintf(f,
229 "Before=veritysetup.target\n"
230 "BindsTo=%1$s %2$s\n"
231 "After=%1$s %2$s\n",
232 dd, dh);
233
234 r = generator_write_veritysetup_service_section(
235 f,
236 id,
237 data_what,
238 hash_what,
239 /* roothash= */ NULL, /* NULL means: derive root hash from udev property ID_DISSECT_PART_ROOTHASH */
240 "root-hash-signature=auto"); /* auto means: derive signature from udev property ID_DISSECT_PART_ROOTHASH_SIG */
241 if (r < 0)
242 return r;
243
244 r = fflush_and_check(f);
245 if (r < 0)
246 return log_error_errno(r, "Failed to write file %s: %m", n);
247
248 r = generator_write_device_timeout(arg_dest_late, data_what, mount_opts, /* filtered= */ NULL);
249 if (r < 0)
250 return r;
251
252 r = generator_write_device_timeout(arg_dest_late, hash_what, mount_opts, /* filtered= */ NULL);
253 if (r < 0)
254 return r;
255
256 r = generator_add_symlink(arg_dest_late, dd, "wants", n);
257 if (r < 0)
258 return r;
259
260 r = generator_add_symlink(arg_dest_late, dh, "wants", n);
261 if (r < 0)
262 return r;
263
264 _cleanup_free_ char *dmname = NULL;
265 dmname = strjoin("dev-mapper-", e, ".device");
266 if (!dmname)
267 return log_oom();
268
269 r = write_drop_in_format(
270 arg_dest_late,
271 dmname, 50, "job-timeout",
272 "# Automatically generated by systemd-gpt-auto-generator\n\n"
273 "[Unit]\n"
274 "JobTimeoutSec=infinity"); /* the binary handles timeouts anyway */
275 if (r < 0)
276 return log_error_errno(r, "Failed to write device timeout drop-in: %m");
277
278 return 0;
279#else
280 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
281 "Partition is Verity protected, but systemd-gpt-auto-generator was compiled without libcryptsetup support.");
282#endif
283}
284#endif
285
286static int add_mount(
287 const char *id,
288 const char *what,
289 const char *where,
290 const char *fstype,
291 MountPointFlags flags,
292 const char *options,
293 const char *description,
294 const char *post) {
295
296 _cleanup_free_ char *unit = NULL, *crypto_what = NULL, *opts_filtered = NULL;
297 _cleanup_fclose_ FILE *f = NULL;
298 int r;
299
300 /* Note that we don't apply specifier escaping on the input strings here, since we know they are not configured
301 * externally, but all originate from our own sources here, and hence we know they contain no % characters that
302 * could potentially be understood as specifiers. */
303
304 assert(id);
305 assert(what);
306 assert(where);
307 assert(description);
308
309 log_debug("Adding %s: %s fstype=%s", where, what, fstype ?: "(any)");
310
311 if (streq_ptr(fstype, "crypto_LUKS")) {
312 /* Mount options passed are determined by partition_pick_mount_options(), whose result
313 * is known to not contain timeout options. */
314 r = add_cryptsetup(id, what, /* mount_opts = */ NULL, flags, /* require= */ true, &crypto_what);
315 if (r < 0)
316 return r;
317
318 what = crypto_what;
319 fstype = NULL;
320 } else if (fstype) {
321 r = dissect_fstype_ok(fstype);
322 if (r < 0)
323 return log_error_errno(r, "Unable to determine of dissected file system type '%s' is permitted: %m", fstype);
324 if (!r)
325 return log_error_errno(
326 SYNTHETIC_ERRNO(EIDRM),
327 "Refusing to automatically mount uncommon file system '%s' to '%s'.",
328 fstype, where);
329 }
330
331 r = generator_write_device_timeout(arg_dest_late, what, options, &opts_filtered);
332 if (r < 0)
333 return r;
334
335 r = unit_name_from_path(where, ".mount", &unit);
336 if (r < 0)
337 return log_error_errno(r, "Failed to generate unit name: %m");
338
339 r = generator_open_unit_file(arg_dest_late, /* source = */ NULL, unit, &f);
340 if (r < 0)
341 return r;
342
343 fprintf(f,
344 "[Unit]\n"
345 "Description=%s\n"
346 "Documentation=man:systemd-gpt-auto-generator(8)\n",
347 description);
348
349 if (post)
350 fprintf(f, "Before=%s\n", post);
351
352 /* NB: here we do not write to arg_dest_late, but to arg_dest! We typically leave the normal
353 * generator drop-in dir for explicit configuration via systemd-fstab-generator or similar, and put
354 * out automatic configuration in the arg_dest_late directory. But this one is an exception, since we
355 * need to override the static version of the fsck root service file. */
356 r = generator_write_fsck_deps(f, arg_dest, what, where, fstype, opts_filtered);
357 if (r < 0)
358 return r;
359
360 r = generator_write_blockdev_dependency(f, what);
361 if (r < 0)
362 return r;
363
364 fprintf(f,
365 "\n"
366 "[Mount]\n"
367 "What=%s\n"
368 "Where=%s\n",
369 what, where);
370
371 if (fstype)
372 fprintf(f, "Type=%s\n", fstype);
373
374 if (opts_filtered)
375 fprintf(f, "Options=%s\n", opts_filtered);
376
377 r = generator_write_mount_timeout(f, where, opts_filtered);
378 if (r < 0)
379 return r;
380
381 r = fflush_and_check(f);
382 if (r < 0)
383 return log_error_errno(r, "Failed to write unit %s: %m", unit);
384
385 if (FLAGS_SET(flags, MOUNT_VALIDATEFS)) {
386 r = generator_hook_up_validatefs(arg_dest_late, where, post);
387 if (r < 0)
388 return r;
389 }
390
391 if (FLAGS_SET(flags, MOUNT_GROWFS)) {
392 r = generator_hook_up_growfs(arg_dest_late, where, post);
393 if (r < 0)
394 return r;
395 }
396
397 if (FLAGS_SET(flags, MOUNT_MEASURE)) {
398 r = generator_hook_up_pcrfs(arg_dest_late, where, post);
399 if (r < 0)
400 return r;
401 }
402
403 if (post) {
404 r = generator_add_symlink(arg_dest_late, post, "requires", unit);
405 if (r < 0)
406 return r;
407 }
408
409 return 0;
410}
411
412static int path_is_busy(const char *where) {
413 int r;
414
415 assert(where);
416
417 /* already a mountpoint; generators run during reload */
418 r = path_is_mount_point_full(where, /* root = */ NULL, AT_SYMLINK_FOLLOW);
419 if (r > 0)
420 return false;
421 /* The directory will be created by the mount or automount unit when it is started. */
422 if (r == -ENOENT)
423 return false;
424
425 if (r < 0)
426 return log_warning_errno(r, "Cannot check if \"%s\" is a mount point: %m", where);
427
428 /* not a mountpoint but it contains files */
429 r = dir_is_empty(where, /* ignore_hidden_or_backup= */ false);
430 if (r == -ENOTDIR) {
431 log_debug("\"%s\" is not a directory, ignoring.", where);
432 return true;
433 } else if (r < 0)
434 return log_warning_errno(r, "Cannot check if \"%s\" is empty: %m", where);
435 else if (r == 0) {
436 log_debug("\"%s\" already populated, ignoring.", where);
437 return true;
438 }
439
440 return false;
441}
442
443static int add_partition_mount(
444 PartitionDesignator d,
445 DissectedPartition *p,
446 const char *id,
447 const char *where,
448 const char *description) {
449
450 _cleanup_free_ char *options = NULL;
451 int r;
452
453 assert(p);
454
455 r = path_is_busy(where);
456 if (r != 0)
457 return r < 0 ? r : 0;
458
459 r = partition_pick_mount_options(
460 d,
461 dissected_partition_fstype(p),
462 p->rw,
463 /* discard= */ true,
464 &options,
465 /* ret_ms_flags= */ NULL);
466 if (r < 0)
467 return r;
468
469 return add_mount(
470 id,
471 p->node,
472 where,
473 p->fstype,
474 (p->rw ? MOUNT_RW : 0) |
475 MOUNT_VALIDATEFS |
476 (p->growfs ? MOUNT_GROWFS : 0) |
477 (STR_IN_SET(id, "root", "var") ? MOUNT_MEASURE : 0), /* by default measure rootfs and /var, since they contain the "identity" of the system */
478 options,
479 description,
480 SPECIAL_LOCAL_FS_TARGET);
481}
482
483static int add_partition_swap(DissectedPartition *p) {
484 const char *what;
485 _cleanup_free_ char *name = NULL, *crypto_what = NULL;
486 _cleanup_fclose_ FILE *f = NULL;
487 int r;
488
489 assert(p);
490 assert(p->node);
491
492 if (!arg_swap_enabled)
493 return 0;
494
495 /* Disable the swap auto logic if at least one swap is defined in /etc/fstab, see #6192. */
496 r = fstab_has_fstype("swap");
497 if (r < 0)
498 return log_error_errno(r, "Failed to parse fstab: %m");
499 if (r > 0) {
500 log_debug("swap specified in fstab, ignoring.");
501 return 0;
502 }
503
504 if (streq_ptr(p->fstype, "crypto_LUKS")) {
505 r = add_cryptsetup("swap", p->node, /* mount_opts = */ NULL, MOUNT_RW, /* require= */ true, &crypto_what);
506 if (r < 0)
507 return r;
508 what = crypto_what;
509 } else
510 what = p->node;
511
512 log_debug("Adding swap: %s", what);
513
514 r = unit_name_from_path(what, ".swap", &name);
515 if (r < 0)
516 return log_error_errno(r, "Failed to generate unit name: %m");
517
518 r = generator_open_unit_file(arg_dest_late, /* source = */ NULL, name, &f);
519 if (r < 0)
520 return r;
521
522 fprintf(f,
523 "[Unit]\n"
524 "Description=Swap Partition\n"
525 "Documentation=man:systemd-gpt-auto-generator(8)\n");
526
527 r = generator_write_blockdev_dependency(f, what);
528 if (r < 0)
529 return r;
530
531 fprintf(f,
532 "\n"
533 "[Swap]\n"
534 "What=%s\n",
535 what);
536
537 r = fflush_and_check(f);
538 if (r < 0)
539 return log_error_errno(r, "Failed to write unit %s: %m", name);
540
541 return generator_add_symlink(arg_dest_late, SPECIAL_SWAP_TARGET, "wants", name);
542}
543
544static int add_automount(
545 const char *id,
546 const char *what,
547 const char *where,
548 const char *fstype,
549 MountPointFlags flags,
550 const char *options,
551 const char *description,
552 usec_t timeout) {
553
554 _cleanup_free_ char *unit = NULL;
555 _cleanup_fclose_ FILE *f = NULL;
556 int r;
557
558 assert(id);
559 assert(where);
560 assert(description);
561
562 r = add_mount(id,
563 what,
564 where,
565 fstype,
566 flags,
567 options,
568 description,
569 /* post= */ NULL);
570 if (r < 0)
571 return r;
572
573 r = unit_name_from_path(where, ".automount", &unit);
574 if (r < 0)
575 return log_error_errno(r, "Failed to generate unit name: %m");
576
577 r = generator_open_unit_file(arg_dest_late, /* source = */ NULL, unit, &f);
578 if (r < 0)
579 return r;
580
581 fprintf(f,
582 "[Unit]\n"
583 "Description=%s\n"
584 "Documentation=man:systemd-gpt-auto-generator(8)\n"
585 "[Automount]\n"
586 "Where=%s\n"
587 "TimeoutIdleSec="USEC_FMT"\n",
588 description,
589 where,
590 timeout / USEC_PER_SEC);
591
592 r = fflush_and_check(f);
593 if (r < 0)
594 return log_error_errno(r, "Failed to write unit %s: %m", unit);
595
596 return generator_add_symlink(arg_dest_late, SPECIAL_LOCAL_FS_TARGET, "wants", unit);
597}
598
599static int add_partition_xbootldr(DissectedPartition *p) {
600 _cleanup_free_ char *options = NULL;
601 int r;
602
603 assert(p);
604 assert(!in_initrd());
605
606 r = path_is_busy("/boot");
607 if (r < 0)
608 return r;
609 if (r > 0)
610 return 0;
611
612 r = partition_pick_mount_options(
613 PARTITION_XBOOTLDR,
614 dissected_partition_fstype(p),
615 /* rw= */ true,
616 /* discard= */ false,
617 &options,
618 /* ret_ms_flags= */ NULL);
619 if (r < 0)
620 return log_error_errno(r, "Failed to determine default mount options for /boot/: %m");
621
622 return add_automount(
623 "boot",
624 p->node,
625 "/boot",
626 p->fstype,
627 MOUNT_RW,
628 options,
629 "Boot Loader Partition",
630 LOADER_PARTITION_IDLE_USEC);
631}
632
633#if ENABLE_EFI
634static int add_partition_esp(DissectedPartition *p, bool has_xbootldr) {
635 const char *esp_path = NULL, *id = NULL;
636 _cleanup_free_ char *options = NULL;
637 int r;
638
639 assert(p);
640 assert(!in_initrd());
641
642 /* Check if there's an existing fstab entry for ESP. If so, we just skip the gpt-auto logic. */
643 r = fstab_has_node(p->node);
644 if (r < 0)
645 log_warning_errno(r, "Failed to check if fstab entry for device '%s' exists, ignoring: %m",
646 p->node);
647 if (r > 0)
648 return 0;
649
650 /* If XBOOTLDR partition is not present and /boot/ is unused and empty, we'll take that.
651 * Otherwise, if /efi/ is unused and empty (or missing), we'll take that.
652 * Otherwise, we do nothing. */
653 if (!has_xbootldr) {
654 r = path_is_busy("/boot");
655 if (r < 0)
656 return r;
657 if (r == 0) {
658 esp_path = "/boot";
659 id = "boot";
660 }
661 }
662
663 if (!esp_path) {
664 r = path_is_busy("/efi");
665 if (r < 0)
666 return r;
667 if (r > 0)
668 return 0;
669
670 esp_path = "/efi";
671 id = "efi";
672 }
673
674 r = partition_pick_mount_options(
675 PARTITION_ESP,
676 dissected_partition_fstype(p),
677 /* rw= */ true,
678 /* discard= */ false,
679 &options,
680 /* ret_ms_flags= */ NULL);
681 if (r < 0)
682 return log_error_errno(r, "Failed to determine default mount options for %s: %m", esp_path);
683
684 return add_automount(
685 id,
686 p->node,
687 esp_path,
688 p->fstype,
689 MOUNT_RW,
690 options,
691 "EFI System Partition Automount",
692 LOADER_PARTITION_IDLE_USEC);
693}
694#else
695static int add_partition_esp(DissectedPartition *p, bool has_xbootldr) {
696 return 0;
697}
698#endif
699
700static int add_partition_root_rw(DissectedPartition *p) {
701 const char *path;
702 int r;
703
704 assert(p);
705 assert(!in_initrd());
706
707 /* Invoked on the main system (not initrd), to honour GPT flag 60 on the root fs (ro) */
708
709 if (arg_root_rw >= 0) {
710 log_debug("Parameter ro/rw specified on kernel command line, not generating drop-in for systemd-remount-fs.service.");
711 return 0;
712 }
713
714 if (!p->rw) {
715 log_debug("Root partition marked read-only in GPT partition table, not generating drop-in for systemd-remount-fs.service.");
716 return 0;
717 }
718
719 r = generator_enable_remount_fs_service(arg_dest_late);
720 if (r < 0)
721 return r;
722
723 path = strjoina(arg_dest_late, "/systemd-remount-fs.service.d/50-remount-rw.conf");
724
725 r = write_string_file(path,
726 "# Automatically generated by systemd-gpt-auto-generator\n\n"
727 "[Service]\n"
728 "Environment=SYSTEMD_REMOUNT_ROOT_RW=1\n",
729 WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_NOFOLLOW|WRITE_STRING_FILE_MKDIR_0755);
730 if (r < 0)
731 return log_error_errno(r, "Failed to write drop-in file %s: %m", path);
732
733 return 0;
734}
735
736static int add_partition_root_growfs(DissectedPartition *p) {
737
738 assert(p);
739 assert(!in_initrd());
740
741 /* Invoked on the main system (not initrd), to honour GPT flag 59 on the root fs (growfs) */
742
743 if (!p->growfs) {
744 log_debug("Root partition not marked for growing the file system in the GPT partition table, not generating drop-in for systemd-growfs-root.service.");
745 return 0;
746 }
747
748 return generator_hook_up_growfs(arg_dest_late, "/", SPECIAL_LOCAL_FS_TARGET);
749}
750
751static int add_partition_root_flags(DissectedPartition *p) {
752 int r = 0;
753
754 assert(p);
755 assert(!in_initrd());
756
757 RET_GATHER(r, add_partition_root_growfs(p));
758 RET_GATHER(r, add_partition_root_rw(p));
759
760 return r;
761}
762
763#if ENABLE_EFI
764static int add_root_cryptsetup(void) {
765#if HAVE_LIBCRYPTSETUP
766
767 assert(arg_auto_root != GPT_AUTO_ROOT_OFF);
768
769 /* If a device /dev/gpt-auto-root-luks or /dev/disk/by-designator/root-luks appears, then make it
770 * pull in systemd-cryptsetup@root.service, which sets it up, and causes /dev/gpt-auto-root or
771 * /dev/disk/by-designator/root to appear which is all we are looking for. */
772
773 const char *bdev =
774 IN_SET(arg_auto_root, GPT_AUTO_ROOT_DISSECT, GPT_AUTO_ROOT_DISSECT_FORCE) ?
775 "/dev/disk/by-designator/root-luks" : "/dev/gpt-auto-root-luks";
776
777 if (IN_SET(arg_auto_root, GPT_AUTO_ROOT_FORCE, GPT_AUTO_ROOT_DISSECT_FORCE)) {
778 /* Similar logic as in add_root_mount(), see below */
779 FactoryResetMode f = factory_reset_mode();
780 if (f < 0)
781 log_warning_errno(f, "Failed to determine whether we are in factory reset mode, assuming not: %m");
782
783 if (IN_SET(f, FACTORY_RESET_ON, FACTORY_RESET_COMPLETE))
784 bdev =
785 IN_SET(arg_auto_root, GPT_AUTO_ROOT_DISSECT, GPT_AUTO_ROOT_DISSECT_FORCE) ?
786 "/dev/disk/by-designator/root-luks-ignore-factory-reset" :
787 "/dev/gpt-auto-root-luks-ignore-factory-reset";
788 }
789
790 return add_cryptsetup(
791 "root",
792 bdev,
793 arg_root_options,
794 MOUNT_RW|MOUNT_MEASURE,
795 /* require= */ false,
796 /* ret_device= */ NULL);
797#else
798 return 0;
799#endif
800}
801#endif
802
803static int add_root_mount(void) {
804#if ENABLE_EFI
805 _cleanup_free_ char *options = NULL;
806 int r;
807
808 /* Explicitly disabled? Then exit immediately */
809 if (arg_auto_root == GPT_AUTO_ROOT_OFF)
810 return 0;
811
812 /* Neither explicitly enabled nor disabled? Then decide based on the EFI partition variables to be set */
813 if (arg_auto_root < 0) {
814 if (!is_efi_boot()) {
815 log_debug("Not an EFI boot, not creating root mount.");
816 return 0;
817 }
818
819 r = efi_loader_get_device_part_uuid(/* ret= */ NULL);
820 if (r == -ENOENT) {
821 log_notice("EFI loader partition unknown, not processing %s.\n"
822 "(The boot loader did not set EFI variable LoaderDevicePartUUID.)",
823 in_initrd() ? "/sysroot" : "/");
824 return 0;
825 }
826 if (r < 0)
827 return log_error_errno(r, "Failed to read loader partition UUID: %m");
828 }
829
830 /* OK, we shall look for a root device, so let's wait for a root device to show up. A udev rule will
831 * create the link for us under the right name.
832 *
833 * There are two distinct names: the /dev/gpt-auto-root-ignore-factory-reset symlink is created for
834 * the root partition if factory reset mode is enabled or complete, and the /dev/gpt-auto-root
835 * symlink is only created if factory reset mode is off or already complete (thus taking factory
836 * reset state into account). In scenarios where the root disk is partially reformatted during
837 * factory reset the latter is the link to use, otherwise the former (so that we don't accidentally
838 * mount a root partition too early that is about to be wiped and replaced by another one). */
839
840 const char *bdev =
841 IN_SET(arg_auto_root, GPT_AUTO_ROOT_DISSECT, GPT_AUTO_ROOT_DISSECT_FORCE) ?
842 "/dev/disk/by-designator/root" : "/dev/gpt-auto-root";
843 if (IN_SET(arg_auto_root, GPT_AUTO_ROOT_FORCE, GPT_AUTO_ROOT_DISSECT_FORCE)) {
844 FactoryResetMode f = factory_reset_mode();
845 if (f < 0)
846 log_warning_errno(f, "Failed to determine whether we are in factory reset mode, assuming not: %m");
847
848 if (IN_SET(f, FACTORY_RESET_ON, FACTORY_RESET_COMPLETE))
849 bdev =
850 IN_SET(arg_auto_root, GPT_AUTO_ROOT_DISSECT, GPT_AUTO_ROOT_DISSECT_FORCE) ?
851 "/dev/disk/by-designator/root-ignore-factory-reset" :
852 "/dev/gpt-auto-root-ignore-factory-reset";
853 }
854
855 if (in_initrd()) {
856 r = generator_write_initrd_root_device_deps(arg_dest_late, bdev);
857 if (r < 0)
858 return 0;
859
860 r = add_root_cryptsetup();
861 if (r < 0)
862 return r;
863
864 /* If a device /dev/disk/by-designator/root-verity or
865 * /dev/disk/by-designator/root-verity-data appears, then make it pull in
866 * systemd-cryptsetup@root.service, which sets it up, and causes /dev/disk/by-designator/root
867 * to appear. */
868 r = add_veritysetup(
869 "root",
870 "/dev/disk/by-designator/root-verity-data",
871 "/dev/disk/by-designator/root-verity",
872 arg_root_options);
873 if (r < 0)
874 return r;
875 }
876
877 /* Note that we do not need to enable systemd-remount-fs.service here. If /etc/fstab exists,
878 * systemd-fstab-generator will pull it in for us, and otherwise add_partition_root_flags() will do
879 * it, after the initrd transition. */
880
881 r = partition_pick_mount_options(
882 PARTITION_ROOT,
883 arg_root_fstype,
884 arg_root_rw > 0,
885 /* discard= */ true,
886 &options,
887 /* ret_ms_flags= */ NULL);
888 if (r < 0)
889 return log_error_errno(r, "Failed to pick root mount options: %m");
890
891 if (arg_root_options)
892 if (!strextend_with_separator(&options, ",", arg_root_options))
893 return log_oom();
894
895 return add_mount(
896 "root",
897 bdev,
898 in_initrd() ? "/sysroot" : "/",
899 arg_root_fstype,
900 (arg_root_rw > 0 ? MOUNT_RW : 0) |
901 (in_initrd() ? MOUNT_VALIDATEFS : 0) |
902 MOUNT_MEASURE,
903 options,
904 "Root Partition",
905 in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
906#else
907 return 0;
908#endif
909}
910
911static int add_usr_mount(void) {
912#if ENABLE_EFI
913 int r;
914
915 /* /usr/ discovery must be enabled explicitly. */
916 if (arg_auto_usr <= 0)
917 return 0;
918
919 /* We do not support the other gpt-auto modes for /usr/, but the parser should already have checked that. */
920 assert(arg_auto_usr == GPT_AUTO_ROOT_DISSECT);
921
922 if (arg_root_fstype && !arg_usr_fstype) {
923 arg_usr_fstype = strdup(arg_root_fstype);
924 if (!arg_usr_fstype)
925 return log_oom();
926 }
927
928 if (arg_root_options && !arg_usr_options) {
929 arg_usr_options = strdup(arg_root_options);
930 if (!arg_usr_options)
931 return log_oom();
932 }
933
934 if (in_initrd()) {
935 r = add_cryptsetup(
936 "usr",
937 "/dev/disk/by-designator/usr-luks",
938 arg_usr_options,
939 MOUNT_RW|MOUNT_MEASURE,
940 /* require= */ false,
941 /* ret_device= */ NULL);
942 if (r < 0)
943 return r;
944
945 /* If a device /dev/disk/by-designator/usr-verity or
946 * /dev/disk/by-designator/usr-verity-data appears, then make it pull in
947 * systemd-cryptsetup@usr.service, which sets it up, and causes /dev/disk/by-designator/usr
948 * to appear. */
949 r = add_veritysetup(
950 "usr",
951 "/dev/disk/by-designator/usr-verity-data",
952 "/dev/disk/by-designator/usr-verity",
953 arg_usr_options);
954 if (r < 0)
955 return r;
956 }
957
958 _cleanup_free_ char *options = NULL;
959 r = partition_pick_mount_options(
960 PARTITION_USR,
961 arg_usr_fstype,
962 /* rw= */ false,
963 /* discard= */ true,
964 &options,
965 /* ret_ms_flags= */ NULL);
966 if (r < 0)
967 return log_error_errno(r, "Failed to pick /usr/ mount options: %m");
968
969 if (arg_usr_options)
970 if (!strextend_with_separator(&options, ",", arg_usr_options))
971 return log_oom();
972
973 r = add_mount("usr",
974 "/dev/disk/by-designator/usr",
975 in_initrd() ? "/sysusr/usr" : "/usr",
976 arg_usr_fstype,
977 /* flags = */ 0,
978 options,
979 "/usr/ Partition",
980 in_initrd() ? SPECIAL_INITRD_USR_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
981 if (r < 0)
982 return r;
983
984 if (in_initrd()) {
985 log_debug("Synthesizing entry what=/sysusr/usr where=/sysroot/usr opts=bind");
986
987 r = add_mount("usr-bind",
988 "/sysusr/usr",
989 "/sysroot/usr",
990 /* fstype= */ NULL,
991 MOUNT_VALIDATEFS,
992 "bind",
993 "/usr/ Partition (Final)",
994 SPECIAL_INITRD_FS_TARGET);
995 if (r < 0)
996 return r;
997 }
998#endif
999 return 0;
1000}
1001
1002static int process_loader_partitions(DissectedPartition *esp, DissectedPartition *xbootldr) {
1003 sd_id128_t loader_uuid;
1004 int r;
1005
1006 assert(esp);
1007 assert(xbootldr);
1008
1009 /* If any paths in fstab look similar to our favorite paths for ESP or XBOOTLDR, we just exit
1010 * early. We also don't bother with cases where one is configured explicitly and the other shall be
1011 * mounted automatically. */
1012
1013 r = fstab_has_mount_point_prefix_strv(STRV_MAKE("/boot", "/efi"));
1014 if (r > 0) {
1015 log_debug("Found mount entries in the /boot/ or /efi/ hierarchies in fstab, not generating ESP or XBOOTLDR mounts.");
1016 return 0;
1017 }
1018 if (r < 0)
1019 log_debug_errno(r, "Failed to check fstab existing paths, ignoring: %m");
1020
1021 if (!is_efi_boot()) {
1022 log_debug("Not an EFI boot, skipping loader partition UUID check.");
1023 goto mount;
1024 }
1025
1026 /* Let's check if LoaderDevicePartUUID points to either ESP or XBOOTLDR. We prefer it pointing
1027 * to the ESP, but we accept XBOOTLDR too. If it points to neither of them, don't mount any
1028 * loader partitions, since they are not the ones used for booting. */
1029
1030 r = efi_loader_get_device_part_uuid(&loader_uuid);
1031 if (r == -ENOENT) {
1032 log_debug_errno(r, "EFI loader partition unknown, skipping ESP and XBOOTLDR mounts.");
1033 return 0;
1034 }
1035 if (r < 0)
1036 return log_debug_errno(r, "Failed to read loader partition UUID, ignoring: %m");
1037
1038 if (esp->found && sd_id128_equal(esp->uuid, loader_uuid))
1039 goto mount;
1040
1041 if (xbootldr->found && sd_id128_equal(xbootldr->uuid, loader_uuid)) {
1042 log_debug("LoaderDevicePartUUID points to XBOOTLDR partition.");
1043 goto mount;
1044 }
1045
1046 log_debug("LoaderDevicePartUUID points to neither ESP nor XBOOTLDR, ignoring.");
1047 return 0;
1048
1049mount:
1050 r = 0;
1051
1052 if (xbootldr->found)
1053 RET_GATHER(r, add_partition_xbootldr(xbootldr));
1054 if (esp->found)
1055 RET_GATHER(r, add_partition_esp(esp, xbootldr->found));
1056
1057 return r;
1058}
1059
1060static int enumerate_partitions(dev_t devnum) {
1061 _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
1062 _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
1063 _cleanup_free_ char *devname = NULL;
1064 int r;
1065
1066 static const PartitionDesignator ignore_designators[] = {
1067 PARTITION_ROOT,
1068 PARTITION_ROOT_VERITY,
1069 PARTITION_ROOT_VERITY_SIG,
1070 PARTITION_USR,
1071 PARTITION_USR_VERITY,
1072 PARTITION_USR_VERITY_SIG,
1073 };
1074
1075 assert(!in_initrd());
1076
1077 /* Run on the final root fs (not in the initrd), to mount auxiliary partitions, and hook in rw
1078 * remount and growfs of the root partition */
1079
1080 r = block_get_whole_disk(devnum, &devnum);
1081 if (r < 0)
1082 return log_debug_errno(r, "Failed to get whole block device for " DEVNUM_FORMAT_STR ": %m",
1083 DEVNUM_FORMAT_VAL(devnum));
1084
1085 r = devname_from_devnum(S_IFBLK, devnum, &devname);
1086 if (r < 0)
1087 return log_debug_errno(r, "Failed to get device node of " DEVNUM_FORMAT_STR ": %m",
1088 DEVNUM_FORMAT_VAL(devnum));
1089
1090 _cleanup_(image_policy_freep) ImagePolicy *image_policy = NULL;
1091 r = image_policy_ignore_designators(
1092 arg_image_policy ?: &image_policy_host,
1093 ignore_designators, ELEMENTSOF(ignore_designators),
1094 &image_policy);
1095 if (r < 0)
1096 return log_debug_errno(r, "Failed to mark root/usr designators as ignore in image policy: %m");
1097
1098 /* Let's take a LOCK_SH lock on the block device, in case udevd is already running. If we don't take
1099 * the lock, udevd might end up issuing BLKRRPART in the middle, and we don't want that, since that
1100 * might remove all partitions while we are operating on them. */
1101 r = loop_device_open_from_path(devname, O_RDONLY, LOCK_SH, &loop);
1102 if (r < 0)
1103 return log_debug_errno(r, "Failed to open %s: %m", devname);
1104
1105 r = dissect_loop_device(
1106 loop,
1107 /* verity= */ NULL,
1108 /* mount_options= */ NULL,
1109 image_policy,
1110 arg_image_filter,
1111 DISSECT_IMAGE_GPT_ONLY|
1112 DISSECT_IMAGE_USR_NO_ROOT|
1113 DISSECT_IMAGE_DISKSEQ_DEVNODE|
1114 DISSECT_IMAGE_ALLOW_EMPTY,
1115 /* NB! Unlike most other places where we dissect block devices we do not use
1116 * DISSECT_IMAGE_ADD_PARTITION_DEVICES here: we want that the kernel finds the
1117 * devices, and udev probes them before we mount them via .mount units much later
1118 * on. And thus we also don't set DISSECT_IMAGE_PIN_PARTITION_DEVICES here, because
1119 * we don't actually mount anything immediately. */
1120 &m);
1121 if (r < 0) {
1122 bool ok = r == -ENOPKG;
1123 dissect_log_error(ok ? LOG_DEBUG : LOG_ERR, r, devname, NULL);
1124 return ok ? 0 : r;
1125 }
1126
1127 if (m->partitions[PARTITION_SWAP].found)
1128 RET_GATHER(r, add_partition_swap(m->partitions + PARTITION_SWAP));
1129
1130 RET_GATHER(r, process_loader_partitions(m->partitions + PARTITION_ESP, m->partitions + PARTITION_XBOOTLDR));
1131
1132 if (m->partitions[PARTITION_HOME].found)
1133 RET_GATHER(r, add_partition_mount(PARTITION_HOME, m->partitions + PARTITION_HOME,
1134 "home", "/home", "Home Partition"));
1135
1136 if (m->partitions[PARTITION_SRV].found)
1137 RET_GATHER(r, add_partition_mount(PARTITION_SRV, m->partitions + PARTITION_SRV,
1138 "srv", "/srv", "Server Data Partition"));
1139
1140 if (m->partitions[PARTITION_VAR].found)
1141 RET_GATHER(r, add_partition_mount(PARTITION_VAR, m->partitions + PARTITION_VAR,
1142 "var", "/var", "Variable Data Partition"));
1143
1144 if (m->partitions[PARTITION_TMP].found)
1145 RET_GATHER(r, add_partition_mount(PARTITION_TMP, m->partitions + PARTITION_TMP,
1146 "var-tmp", "/var/tmp", "Temporary Data Partition"));
1147
1148 if (m->partitions[PARTITION_ROOT].found)
1149 RET_GATHER(r, add_partition_root_flags(m->partitions + PARTITION_ROOT));
1150
1151 return r;
1152}
1153
1154static int add_mounts(void) {
1155 dev_t devno;
1156 int r;
1157
1158 if (in_initrd())
1159 return 0;
1160
1161 r = blockdev_get_root(LOG_ERR, &devno);
1162 if (r < 0)
1163 return r;
1164 if (r == 0) {
1165 log_debug("Skipping automatic GPT dissection logic, root file system not backed by a (single) whole block device.");
1166 return 0;
1167 }
1168
1169 return enumerate_partitions(devno);
1170}
1171
1172static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
1173 int r;
1174
1175 assert(key);
1176
1177 if (proc_cmdline_key_streq(key, "systemd.gpt_auto") ||
1178 proc_cmdline_key_streq(key, "rd.systemd.gpt_auto")) {
1179
1180 r = value ? parse_boolean(value) : 1;
1181 if (r < 0)
1182 log_warning_errno(r, "Failed to parse gpt-auto switch \"%s\", ignoring: %m", value);
1183 else
1184 arg_enabled = r;
1185
1186 } else if (streq(key, "root")) {
1187
1188 if (proc_cmdline_value_missing(key, value))
1189 return 0;
1190
1191 /* Disable root disk logic if there's a root= value specified (unless it happens to be
1192 * "gpt-auto" or "gpt-auto-force") */
1193
1194 arg_auto_root = parse_gpt_auto_root("root=", value);
1195 assert(arg_auto_root >= 0);
1196
1197 } else if (streq(key, "roothash")) {
1198
1199 if (proc_cmdline_value_missing(key, value))
1200 return 0;
1201
1202 /* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */
1203
1204 arg_auto_root = GPT_AUTO_ROOT_OFF;
1205 log_debug("Disabling root partition auto-detection, roothash= is set.");
1206
1207 } else if (streq(key, "rootfstype")) {
1208
1209 if (proc_cmdline_value_missing(key, value))
1210 return 0;
1211
1212 return free_and_strdup_warn(&arg_root_fstype, value);
1213
1214 } else if (streq(key, "rootflags")) {
1215
1216 if (proc_cmdline_value_missing(key, value))
1217 return 0;
1218
1219 if (!strextend_with_separator(&arg_root_options, ",", value))
1220 return log_oom();
1221
1222 } else if (streq(key, "mount.usr")) {
1223
1224 if (proc_cmdline_value_missing(key, value))
1225 return 0;
1226
1227 /* Disable root disk logic if there's a root= value specified (unless it happens to be
1228 * "gpt-auto" or "gpt-auto-force") */
1229
1230 arg_auto_usr = parse_gpt_auto_root("mount.usr=", value);
1231 assert(arg_auto_usr >= 0);
1232
1233 if (IN_SET(arg_auto_usr, GPT_AUTO_ROOT_ON, GPT_AUTO_ROOT_FORCE, GPT_AUTO_ROOT_DISSECT_FORCE)) {
1234 log_warning("'gpt-auto', 'gpt-auto-force' and 'dissect-force' are not supported for mount.usr=. Automatically resorting to mount.usr=dissect mode instead.");
1235 arg_auto_usr = GPT_AUTO_ROOT_DISSECT;
1236 }
1237
1238 } else if (streq(key, "mount.usrfstype")) {
1239
1240 if (proc_cmdline_value_missing(key, value))
1241 return 0;
1242
1243 return free_and_strdup_warn(&arg_usr_fstype, empty_to_null(value));
1244
1245 } else if (streq(key, "mount.usrflags")) {
1246
1247 if (proc_cmdline_value_missing(key, value))
1248 return 0;
1249
1250 if (!strextend_with_separator(&arg_usr_options, ",", value))
1251 return log_oom();
1252
1253 } else if (streq(key, "rw") && !value)
1254 arg_root_rw = true;
1255 else if (streq(key, "ro") && !value)
1256 arg_root_rw = false;
1257 else if (proc_cmdline_key_streq(key, "systemd.image_policy"))
1258 return parse_image_policy_argument(value, &arg_image_policy);
1259 else if (proc_cmdline_key_streq(key, "systemd.image_filter")) {
1260 _cleanup_(image_filter_freep) ImageFilter *f = NULL;
1261
1262 r = image_filter_parse(value, &f);
1263 if (r < 0)
1264 return log_error_errno(r, "Failed to parse image filter: %s", value);
1265
1266 image_filter_free(arg_image_filter);
1267 arg_image_filter = TAKE_PTR(f);
1268
1269 } else if (streq(key, "systemd.swap")) {
1270
1271 r = value ? parse_boolean(value) : 1;
1272 if (r < 0)
1273 log_warning_errno(r, "Failed to parse swap switch \"%s\", ignoring: %m", value);
1274 else
1275 arg_swap_enabled = r;
1276
1277 if (!arg_swap_enabled)
1278 log_debug("Disabling swap partitions auto-detection, systemd.swap=no is defined.");
1279
1280 }
1281
1282 return 0;
1283}
1284
1285static int run(const char *dest, const char *dest_early, const char *dest_late) {
1286 int r;
1287
1288 assert_se(arg_dest = dest);
1289 assert_se(arg_dest_late = dest_late);
1290
1291 if (detect_container() > 0) {
1292 log_debug("In a container, exiting.");
1293 return 0;
1294 }
1295
1296 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
1297 if (r < 0)
1298 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
1299
1300 if (!arg_enabled) {
1301 log_debug("Disabled, exiting.");
1302 return 0;
1303 }
1304
1305 r = 0;
1306 RET_GATHER(r, add_root_mount());
1307 RET_GATHER(r, add_usr_mount());
1308 RET_GATHER(r, add_mounts());
1309
1310 return r;
1311}
1312
1313DEFINE_MAIN_GENERATOR_FUNCTION(run);