]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/gpt-auto-generator/gpt-auto-generator.c
7d014db69daf87a3931f665609c38274ce450abb
[thirdparty/systemd.git] / src / gpt-auto-generator / gpt-auto-generator.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2013 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <blkid.h>
22 #include <stdlib.h>
23 #include <sys/statfs.h>
24 #include <unistd.h>
25
26 #include "libudev.h"
27 #include "sd-id128.h"
28
29 #include "alloc-util.h"
30 #include "blkid-util.h"
31 #include "btrfs-util.h"
32 #include "dirent-util.h"
33 #include "dissect-image.h"
34 #include "efivars.h"
35 #include "fd-util.h"
36 #include "fileio.h"
37 #include "fstab-util.h"
38 #include "generator.h"
39 #include "gpt.h"
40 #include "missing.h"
41 #include "mkdir.h"
42 #include "mount-util.h"
43 #include "parse-util.h"
44 #include "path-util.h"
45 #include "proc-cmdline.h"
46 #include "special.h"
47 #include "stat-util.h"
48 #include "string-util.h"
49 #include "udev-util.h"
50 #include "unit-name.h"
51 #include "util.h"
52 #include "virt.h"
53
54 static const char *arg_dest = "/tmp";
55 static bool arg_enabled = true;
56 static bool arg_root_enabled = true;
57 static bool arg_root_rw = false;
58
59 static int add_cryptsetup(const char *id, const char *what, bool rw, bool require, char **device) {
60 _cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL;
61 _cleanup_fclose_ FILE *f = NULL;
62 char *ret;
63 int r;
64
65 assert(id);
66 assert(what);
67
68 r = unit_name_from_path(what, ".device", &d);
69 if (r < 0)
70 return log_error_errno(r, "Failed to generate unit name: %m");
71
72 e = unit_name_escape(id);
73 if (!e)
74 return log_oom();
75
76 r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
77 if (r < 0)
78 return log_error_errno(r, "Failed to generate unit name: %m");
79
80 p = strjoin(arg_dest, "/", n);
81 if (!p)
82 return log_oom();
83
84 f = fopen(p, "wxe");
85 if (!f)
86 return log_error_errno(errno, "Failed to create unit file %s: %m", p);
87
88 fprintf(f,
89 "# Automatically generated by systemd-gpt-auto-generator\n\n"
90 "[Unit]\n"
91 "Description=Cryptography Setup for %%I\n"
92 "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n"
93 "DefaultDependencies=no\n"
94 "Conflicts=umount.target\n"
95 "BindsTo=dev-mapper-%%i.device %s\n"
96 "Before=umount.target cryptsetup.target\n"
97 "After=%s\n"
98 "IgnoreOnIsolate=true\n"
99 "[Service]\n"
100 "Type=oneshot\n"
101 "RemainAfterExit=yes\n"
102 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
103 "KeyringMode=shared\n" /* make sure we can share cached keys among instances */
104 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n"
105 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
106 d, d,
107 id, what, rw ? "" : "read-only",
108 id);
109
110 r = fflush_and_check(f);
111 if (r < 0)
112 return log_error_errno(r, "Failed to write file %s: %m", p);
113
114 r = generator_add_symlink(arg_dest, d, "wants", n);
115 if (r < 0)
116 return r;
117
118 if (require) {
119 const char *dmname;
120
121 r = generator_add_symlink(arg_dest, "cryptsetup.target", "requires", n);
122 if (r < 0)
123 return r;
124
125 dmname = strjoina("dev-mapper-", e, ".device");
126 r = generator_add_symlink(arg_dest, dmname, "requires", n);
127 if (r < 0)
128 return r;
129 }
130
131 free(p);
132 p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf");
133 if (!p)
134 return log_oom();
135
136 mkdir_parents_label(p, 0755);
137 r = write_string_file(p,
138 "# Automatically generated by systemd-gpt-auto-generator\n\n"
139 "[Unit]\n"
140 "JobTimeoutSec=0\n",
141 WRITE_STRING_FILE_CREATE); /* the binary handles timeouts anyway */
142 if (r < 0)
143 return log_error_errno(r, "Failed to write device drop-in: %m");
144
145 ret = strappend("/dev/mapper/", id);
146 if (!ret)
147 return log_oom();
148
149 if (device)
150 *device = ret;
151 return 0;
152 }
153
154 static int add_mount(
155 const char *id,
156 const char *what,
157 const char *where,
158 const char *fstype,
159 bool rw,
160 const char *options,
161 const char *description,
162 const char *post) {
163
164 _cleanup_free_ char *unit = NULL, *crypto_what = NULL, *p = NULL;
165 _cleanup_fclose_ FILE *f = NULL;
166 int r;
167
168 assert(id);
169 assert(what);
170 assert(where);
171 assert(description);
172
173 log_debug("Adding %s: %s %s", where, what, strna(fstype));
174
175 if (streq_ptr(fstype, "crypto_LUKS")) {
176
177 r = add_cryptsetup(id, what, rw, true, &crypto_what);
178 if (r < 0)
179 return r;
180
181 what = crypto_what;
182 fstype = NULL;
183 }
184
185 r = unit_name_from_path(where, ".mount", &unit);
186 if (r < 0)
187 return log_error_errno(r, "Failed to generate unit name: %m");
188
189 p = strjoin(arg_dest, "/", unit);
190 if (!p)
191 return log_oom();
192
193 f = fopen(p, "wxe");
194 if (!f)
195 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
196
197 fprintf(f,
198 "# Automatically generated by systemd-gpt-auto-generator\n\n"
199 "[Unit]\n"
200 "Description=%s\n"
201 "Documentation=man:systemd-gpt-auto-generator(8)\n",
202 description);
203
204 if (post)
205 fprintf(f, "Before=%s\n", post);
206
207 r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
208 if (r < 0)
209 return r;
210
211 fprintf(f,
212 "\n"
213 "[Mount]\n"
214 "What=%s\n"
215 "Where=%s\n",
216 what, where);
217
218 if (fstype)
219 fprintf(f, "Type=%s\n", fstype);
220
221 if (options)
222 fprintf(f, "Options=%s,%s\n", options, rw ? "rw" : "ro");
223 else
224 fprintf(f, "Options=%s\n", rw ? "rw" : "ro");
225
226 r = fflush_and_check(f);
227 if (r < 0)
228 return log_error_errno(r, "Failed to write unit file %s: %m", p);
229
230 if (post)
231 return generator_add_symlink(arg_dest, post, "requires", unit);
232 return 0;
233 }
234
235 static bool path_is_busy(const char *where) {
236 int r;
237
238 /* already a mountpoint; generators run during reload */
239 r = path_is_mount_point(where, NULL, AT_SYMLINK_FOLLOW);
240 if (r > 0)
241 return false;
242
243 /* the directory might not exist on a stateless system */
244 if (r == -ENOENT)
245 return false;
246
247 if (r < 0)
248 return true;
249
250 /* not a mountpoint but it contains files */
251 if (dir_is_empty(where) <= 0)
252 return true;
253
254 return false;
255 }
256
257 static int add_partition_mount(
258 DissectedPartition *p,
259 const char *id,
260 const char *where,
261 const char *description) {
262
263 assert(p);
264
265 if (path_is_busy(where)) {
266 log_debug("%s already populated, ignoring.", where);
267 return 0;
268 }
269
270 return add_mount(
271 id,
272 p->node,
273 where,
274 p->fstype,
275 p->rw,
276 NULL,
277 description,
278 SPECIAL_LOCAL_FS_TARGET);
279 }
280
281 static int add_swap(const char *path) {
282 _cleanup_free_ char *name = NULL, *unit = NULL;
283 _cleanup_fclose_ FILE *f = NULL;
284 int r;
285
286 assert(path);
287
288 /* Disable the swap auto logic if at least one swap is defined in /etc/fstab, see #6192. */
289 r = fstab_has_fstype("swap");
290 if (r < 0)
291 return log_error_errno(r, "Failed to parse fstab: %m");
292 if (r > 0) {
293 log_debug("swap specified in fstab, ignoring.");
294 return 0;
295 }
296
297 log_debug("Adding swap: %s", path);
298
299 r = unit_name_from_path(path, ".swap", &name);
300 if (r < 0)
301 return log_error_errno(r, "Failed to generate unit name: %m");
302
303 unit = strjoin(arg_dest, "/", name);
304 if (!unit)
305 return log_oom();
306
307 f = fopen(unit, "wxe");
308 if (!f)
309 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
310
311 fprintf(f,
312 "# Automatically generated by systemd-gpt-auto-generator\n\n"
313 "[Unit]\n"
314 "Description=Swap Partition\n"
315 "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
316 "[Swap]\n"
317 "What=%s\n",
318 path);
319
320 r = fflush_and_check(f);
321 if (r < 0)
322 return log_error_errno(r, "Failed to write unit file %s: %m", unit);
323
324 return generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET, "wants", name);
325 }
326
327 #if ENABLE_EFI
328 static int add_automount(
329 const char *id,
330 const char *what,
331 const char *where,
332 const char *fstype,
333 bool rw,
334 const char *options,
335 const char *description,
336 usec_t timeout) {
337
338 _cleanup_free_ char *unit = NULL;
339 _cleanup_free_ char *opt, *p = NULL;
340 _cleanup_fclose_ FILE *f = NULL;
341 int r;
342
343 assert(id);
344 assert(where);
345 assert(description);
346
347 if (options)
348 opt = strjoin(options, ",noauto");
349 else
350 opt = strdup("noauto");
351 if (!opt)
352 return log_oom();
353
354 r = add_mount(id,
355 what,
356 where,
357 fstype,
358 rw,
359 opt,
360 description,
361 NULL);
362 if (r < 0)
363 return r;
364
365 r = unit_name_from_path(where, ".automount", &unit);
366 if (r < 0)
367 return log_error_errno(r, "Failed to generate unit name: %m");
368
369 p = strjoin(arg_dest, "/", unit);
370 if (!p)
371 return log_oom();
372
373 f = fopen(p, "wxe");
374 if (!f)
375 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
376
377 fprintf(f,
378 "# Automatically generated by systemd-gpt-auto-generator\n\n"
379 "[Unit]\n"
380 "Description=%s\n"
381 "Documentation=man:systemd-gpt-auto-generator(8)\n"
382 "[Automount]\n"
383 "Where=%s\n"
384 "TimeoutIdleSec="USEC_FMT"\n",
385 description,
386 where,
387 timeout / USEC_PER_SEC);
388
389 r = fflush_and_check(f);
390 if (r < 0)
391 return log_error_errno(r, "Failed to write unit file %s: %m", p);
392
393 return generator_add_symlink(arg_dest, SPECIAL_LOCAL_FS_TARGET, "wants", unit);
394 }
395
396 static int add_esp(DissectedPartition *p) {
397 const char *esp;
398 int r;
399
400 assert(p);
401
402 if (in_initrd()) {
403 log_debug("In initrd, ignoring the ESP.");
404 return 0;
405 }
406
407 /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice */
408 esp = access("/efi/", F_OK) >= 0 ? "/efi" : "/boot";
409
410 /* We create an .automount which is not overridden by the .mount from the fstab generator. */
411 r = fstab_is_mount_point(esp);
412 if (r < 0)
413 return log_error_errno(r, "Failed to parse fstab: %m");
414 if (r > 0) {
415 log_debug("%s specified in fstab, ignoring.", esp);
416 return 0;
417 }
418
419 if (path_is_busy(esp)) {
420 log_debug("%s already populated, ignoring.", esp);
421 return 0;
422 }
423
424 if (is_efi_boot()) {
425 sd_id128_t loader_uuid;
426
427 /* If this is an EFI boot, be extra careful, and only mount the ESP if it was the ESP used for booting. */
428
429 r = efi_loader_get_device_part_uuid(&loader_uuid);
430 if (r == -ENOENT) {
431 log_debug("EFI loader partition unknown.");
432 return 0;
433 }
434 if (r < 0)
435 return log_error_errno(r, "Failed to read ESP partition UUID: %m");
436
437 if (!sd_id128_equal(p->uuid, loader_uuid)) {
438 log_debug("Partition for %s does not appear to be the partition we are booted from.", esp);
439 return 0;
440 }
441 } else
442 log_debug("Not an EFI boot, skipping ESP check.");
443
444 return add_automount("boot",
445 p->node,
446 esp,
447 p->fstype,
448 true,
449 "umask=0077",
450 "EFI System Partition Automount",
451 120 * USEC_PER_SEC);
452 }
453 #else
454 static int add_esp(DissectedPartition *p) {
455 return 0;
456 }
457 #endif
458
459 static int open_parent(dev_t devnum, int *ret) {
460 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
461 _cleanup_udev_unref_ struct udev *udev = NULL;
462 const char *name, *devtype, *node;
463 struct udev_device *parent;
464 dev_t pn;
465 int fd;
466
467 assert(ret);
468
469 udev = udev_new();
470 if (!udev)
471 return log_oom();
472
473 d = udev_device_new_from_devnum(udev, 'b', devnum);
474 if (!d)
475 return log_oom();
476
477 name = udev_device_get_devnode(d);
478 if (!name)
479 name = udev_device_get_syspath(d);
480 if (!name) {
481 log_debug("Device %u:%u does not have a name, ignoring.", major(devnum), minor(devnum));
482 goto not_found;
483 }
484
485 parent = udev_device_get_parent(d);
486 if (!parent) {
487 log_debug("%s: not a partitioned device, ignoring.", name);
488 goto not_found;
489 }
490
491 /* Does it have a devtype? */
492 devtype = udev_device_get_devtype(parent);
493 if (!devtype) {
494 log_debug("%s: parent doesn't have a device type, ignoring.", name);
495 goto not_found;
496 }
497
498 /* Is this a disk or a partition? We only care for disks... */
499 if (!streq(devtype, "disk")) {
500 log_debug("%s: parent isn't a raw disk, ignoring.", name);
501 goto not_found;
502 }
503
504 /* Does it have a device node? */
505 node = udev_device_get_devnode(parent);
506 if (!node) {
507 log_debug("%s: parent device does not have device node, ignoring.", name);
508 goto not_found;
509 }
510
511 log_debug("%s: root device %s.", name, node);
512
513 pn = udev_device_get_devnum(parent);
514 if (major(pn) == 0) {
515 log_debug("%s: parent device is not a proper block device, ignoring.", name);
516 goto not_found;
517 }
518
519 fd = open(node, O_RDONLY|O_CLOEXEC|O_NOCTTY);
520 if (fd < 0)
521 return log_error_errno(errno, "Failed to open %s: %m", node);
522
523 *ret = fd;
524 return 1;
525
526 not_found:
527 *ret = -1;
528 return 0;
529 }
530
531 static int enumerate_partitions(dev_t devnum) {
532
533 _cleanup_close_ int fd = -1;
534 _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
535 int r, k;
536
537 r = open_parent(devnum, &fd);
538 if (r <= 0)
539 return r;
540
541 r = dissect_image(fd, NULL, 0, DISSECT_IMAGE_GPT_ONLY, &m);
542 if (r == -ENOPKG) {
543 log_debug_errno(r, "No suitable partition table found, ignoring.");
544 return 0;
545 }
546 if (r < 0)
547 return log_error_errno(r, "Failed to dissect: %m");
548
549 if (m->partitions[PARTITION_SWAP].found) {
550 k = add_swap(m->partitions[PARTITION_SWAP].node);
551 if (k < 0)
552 r = k;
553 }
554
555 if (m->partitions[PARTITION_ESP].found) {
556 k = add_esp(m->partitions + PARTITION_ESP);
557 if (k < 0)
558 r = k;
559 }
560
561 if (m->partitions[PARTITION_HOME].found) {
562 k = add_partition_mount(m->partitions + PARTITION_HOME, "home", "/home", "Home Partition");
563 if (k < 0)
564 r = k;
565 }
566
567 if (m->partitions[PARTITION_SRV].found) {
568 k = add_partition_mount(m->partitions + PARTITION_SRV, "srv", "/srv", "Server Data Partition");
569 if (k < 0)
570 r = k;
571 }
572
573 return r;
574 }
575
576 static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
577 int r;
578
579 assert(key);
580
581 if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto")) {
582
583 r = value ? parse_boolean(value) : 1;
584 if (r < 0)
585 log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value);
586 else
587 arg_enabled = r;
588
589 } else if (streq(key, "root")) {
590
591 if (proc_cmdline_value_missing(key, value))
592 return 0;
593
594 /* Disable root disk logic if there's a root= value
595 * specified (unless it happens to be "gpt-auto") */
596
597 arg_root_enabled = streq(value, "gpt-auto");
598
599 } else if (streq(key, "roothash")) {
600
601 if (proc_cmdline_value_missing(key, value))
602 return 0;
603
604 /* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */
605
606 arg_root_enabled = false;
607
608 } else if (streq(key, "rw") && !value)
609 arg_root_rw = true;
610 else if (streq(key, "ro") && !value)
611 arg_root_rw = false;
612
613 return 0;
614 }
615
616 #if ENABLE_EFI
617 static int add_root_cryptsetup(void) {
618
619 /* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
620 * sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
621
622 return add_cryptsetup("root", "/dev/gpt-auto-root-luks", true, false, NULL);
623 }
624 #endif
625
626 static int add_root_mount(void) {
627
628 #if ENABLE_EFI
629 int r;
630
631 if (!is_efi_boot()) {
632 log_debug("Not a EFI boot, not creating root mount.");
633 return 0;
634 }
635
636 r = efi_loader_get_device_part_uuid(NULL);
637 if (r == -ENOENT) {
638 log_debug("EFI loader partition unknown, exiting.");
639 return 0;
640 } else if (r < 0)
641 return log_error_errno(r, "Failed to read ESP partition UUID: %m");
642
643 /* OK, we have an ESP partition, this is fantastic, so let's
644 * wait for a root device to show up. A udev rule will create
645 * the link for us under the right name. */
646
647 if (in_initrd()) {
648 r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root");
649 if (r < 0)
650 return 0;
651
652 r = add_root_cryptsetup();
653 if (r < 0)
654 return r;
655 }
656
657 return add_mount(
658 "root",
659 "/dev/gpt-auto-root",
660 in_initrd() ? "/sysroot" : "/",
661 NULL,
662 arg_root_rw,
663 NULL,
664 "Root Partition",
665 in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
666 #else
667 return 0;
668 #endif
669 }
670
671 static int add_mounts(void) {
672 dev_t devno;
673 int r;
674
675 r = get_block_device_harder("/", &devno);
676 if (r < 0)
677 return log_error_errno(r, "Failed to determine block device of root file system: %m");
678 if (r == 0) {
679 r = get_block_device_harder("/usr", &devno);
680 if (r < 0)
681 return log_error_errno(r, "Failed to determine block device of /usr file system: %m");
682 if (r == 0) {
683 log_debug("Neither root nor /usr file system are on a (single) block device.");
684 return 0;
685 }
686 }
687
688 return enumerate_partitions(devno);
689 }
690
691 int main(int argc, char *argv[]) {
692 int r, k;
693
694 if (argc > 1 && argc != 4) {
695 log_error("This program takes three or no arguments.");
696 return EXIT_FAILURE;
697 }
698
699 if (argc > 1)
700 arg_dest = argv[3];
701
702 log_set_target(LOG_TARGET_SAFE);
703 log_parse_environment();
704 log_open();
705
706 umask(0022);
707
708 if (detect_container() > 0) {
709 log_debug("In a container, exiting.");
710 return EXIT_SUCCESS;
711 }
712
713 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
714 if (r < 0)
715 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
716
717 if (!arg_enabled) {
718 log_debug("Disabled, exiting.");
719 return EXIT_SUCCESS;
720 }
721
722 if (arg_root_enabled)
723 r = add_root_mount();
724 else
725 r = 0;
726
727 if (!in_initrd()) {
728 k = add_mounts();
729 if (k < 0)
730 r = k;
731 }
732
733 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
734 }