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