]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/gpt-auto-generator/gpt-auto-generator.c
readahead: wipe out readahead
[thirdparty/systemd.git] / src / gpt-auto-generator / gpt-auto-generator.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <fcntl.h>
25 #include <sys/ioctl.h>
26 #include <sys/statfs.h>
27 #include <blkid/blkid.h>
28
29 #ifdef HAVE_LINUX_BTRFS_H
30 #include <linux/btrfs.h>
31 #endif
32
33 #include "sd-id128.h"
34 #include "libudev.h"
35 #include "path-util.h"
36 #include "util.h"
37 #include "mkdir.h"
38 #include "missing.h"
39 #include "udev-util.h"
40 #include "special.h"
41 #include "unit-name.h"
42 #include "virt.h"
43 #include "generator.h"
44 #include "gpt.h"
45 #include "fileio.h"
46 #include "efivars.h"
47 #include "blkid-util.h"
48
49 static const char *arg_dest = "/tmp";
50 static bool arg_enabled = true;
51 static bool arg_root_enabled = true;
52 static bool arg_root_rw = false;
53
54 static int add_swap(const char *path) {
55 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
56 _cleanup_fclose_ FILE *f = NULL;
57
58 assert(path);
59
60 log_debug("Adding swap: %s", path);
61
62 name = unit_name_from_path(path, ".swap");
63 if (!name)
64 return log_oom();
65
66 unit = strjoin(arg_dest, "/", name, NULL);
67 if (!unit)
68 return log_oom();
69
70 f = fopen(unit, "wxe");
71 if (!f) {
72 log_error("Failed to create unit file %s: %m", unit);
73 return -errno;
74 }
75
76 fprintf(f,
77 "# Automatically generated by systemd-gpt-auto-generator\n\n"
78 "[Unit]\n"
79 "Description=Swap Partition\n"
80 "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
81 "[Swap]\n"
82 "What=%s\n",
83 path);
84
85 fflush(f);
86 if (ferror(f)) {
87 log_error("Failed to write unit file %s: %m", unit);
88 return -errno;
89 }
90
91 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
92 if (!lnk)
93 return log_oom();
94
95 mkdir_parents_label(lnk, 0755);
96 if (symlink(unit, lnk) < 0) {
97 log_error("Failed to create symlink %s: %m", lnk);
98 return -errno;
99 }
100
101 return 0;
102 }
103
104 static int add_cryptsetup(const char *id, const char *what, bool rw, char **device) {
105 _cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL;
106 _cleanup_fclose_ FILE *f = NULL;
107 char *from, *ret;
108 int r;
109
110 assert(id);
111 assert(what);
112 assert(device);
113
114 d = unit_name_from_path(what, ".device");
115 if (!d)
116 return log_oom();
117
118 e = unit_name_escape(id);
119 if (!e)
120 return log_oom();
121
122 n = unit_name_build("systemd-cryptsetup", e, ".service");
123 if (!n)
124 return log_oom();
125
126 p = strjoin(arg_dest, "/", n, NULL);
127 if (!p)
128 return log_oom();
129
130 f = fopen(p, "wxe");
131 if (!f) {
132 log_error("Failed to create unit file %s: %m", p);
133 return -errno;
134 }
135
136 fprintf(f,
137 "# Automatically generated by systemd-gpt-auto-generator\n\n"
138 "[Unit]\n"
139 "Description=Cryptography Setup for %%I\n"
140 "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n"
141 "DefaultDependencies=no\n"
142 "Conflicts=umount.target\n"
143 "BindsTo=dev-mapper-%%i.device %s\n"
144 "Before=umount.target cryptsetup.target\n"
145 "After=%s\n"
146 "IgnoreOnIsolate=true\n"
147 "[Service]\n"
148 "Type=oneshot\n"
149 "RemainAfterExit=yes\n"
150 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
151 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n"
152 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
153 d, d,
154 id, what, rw ? "" : "read-only",
155 id);
156
157 fflush(f);
158 if (ferror(f)) {
159 log_error("Failed to write file %s: %m", p);
160 return -errno;
161 }
162
163 from = strappenda("../", n);
164
165 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
166 if (!to)
167 return log_oom();
168
169 mkdir_parents_label(to, 0755);
170 if (symlink(from, to) < 0) {
171 log_error("Failed to create symlink %s: %m", to);
172 return -errno;
173 }
174
175 free(to);
176 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
177 if (!to)
178 return log_oom();
179
180 mkdir_parents_label(to, 0755);
181 if (symlink(from, to) < 0) {
182 log_error("Failed to create symlink %s: %m", to);
183 return -errno;
184 }
185
186 free(to);
187 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
188 if (!to)
189 return log_oom();
190
191 mkdir_parents_label(to, 0755);
192 if (symlink(from, to) < 0) {
193 log_error("Failed to create symlink %s: %m", to);
194 return -errno;
195 }
196
197 free(p);
198 p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
199 if (!p)
200 return log_oom();
201
202 mkdir_parents_label(p, 0755);
203 r = write_string_file(p,
204 "# Automatically generated by systemd-gpt-auto-generator\n\n"
205 "[Unit]\n"
206 "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
207 if (r < 0) {
208 log_error("Failed to write device drop-in: %s", strerror(-r));
209 return r;
210 }
211
212 ret = strappend("/dev/mapper/", id);
213 if (!ret)
214 return log_oom();
215
216 *device = ret;
217 return 0;
218 }
219
220 static int add_mount(
221 const char *id,
222 const char *what,
223 const char *where,
224 const char *fstype,
225 bool rw,
226 const char *description,
227 const char *post) {
228
229 _cleanup_free_ char *unit = NULL, *lnk = NULL, *crypto_what = NULL, *p = NULL;
230 _cleanup_fclose_ FILE *f = NULL;
231 int r;
232
233 assert(id);
234 assert(what);
235 assert(where);
236 assert(description);
237
238 log_debug("Adding %s: %s %s", where, what, strna(fstype));
239
240 if (streq_ptr(fstype, "crypto_LUKS")) {
241
242 r = add_cryptsetup(id, what, rw, &crypto_what);
243 if (r < 0)
244 return r;
245
246 what = crypto_what;
247 fstype = NULL;
248 }
249
250 unit = unit_name_from_path(where, ".mount");
251 if (!unit)
252 return log_oom();
253
254 p = strjoin(arg_dest, "/", unit, NULL);
255 if (!p)
256 return log_oom();
257
258 f = fopen(p, "wxe");
259 if (!f) {
260 log_error("Failed to create unit file %s: %m", unit);
261 return -errno;
262 }
263
264 fprintf(f,
265 "# Automatically generated by systemd-gpt-auto-generator\n\n"
266 "[Unit]\n"
267 "Description=%s\n"
268 "Documentation=man:systemd-gpt-auto-generator(8)\n",
269 description);
270
271 if (post)
272 fprintf(f, "Before=%s\n", post);
273
274 r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
275 if (r < 0)
276 return r;
277
278 fprintf(f,
279 "\n"
280 "[Mount]\n"
281 "What=%s\n"
282 "Where=%s\n",
283 what, where);
284
285 if (fstype)
286 fprintf(f, "Type=%s\n", fstype);
287
288 fprintf(f, "Options=%s\n", rw ? "rw" : "ro");
289
290 fflush(f);
291 if (ferror(f)) {
292 log_error("Failed to write unit file %s: %m", p);
293 return -errno;
294 }
295
296 if (post) {
297 lnk = strjoin(arg_dest, "/", post, ".requires/", unit, NULL);
298 if (!lnk)
299 return log_oom();
300
301 mkdir_parents_label(lnk, 0755);
302 if (symlink(p, lnk) < 0) {
303 log_error("Failed to create symlink %s: %m", lnk);
304 return -errno;
305 }
306 }
307
308 return 0;
309 }
310
311 static int probe_and_add_mount(
312 const char *id,
313 const char *what,
314 const char *where,
315 bool rw,
316 const char *description,
317 const char *post) {
318
319 _cleanup_blkid_free_probe_ blkid_probe b = NULL;
320 const char *fstype;
321 int r;
322
323 assert(id);
324 assert(what);
325 assert(where);
326 assert(description);
327
328 if (path_is_mount_point(where, true) <= 0 &&
329 dir_is_empty(where) <= 0) {
330 log_debug("%s already populated, ignoring.", where);
331 return 0;
332 }
333
334 /* Let's check the partition type here, so that we know
335 * whether to do LUKS magic. */
336
337 errno = 0;
338 b = blkid_new_probe_from_filename(what);
339 if (!b) {
340 if (errno == 0)
341 return log_oom();
342 log_error("Failed to allocate prober: %m");
343 return -errno;
344 }
345
346 blkid_probe_enable_superblocks(b, 1);
347 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
348
349 errno = 0;
350 r = blkid_do_safeprobe(b);
351 if (r == -2 || r == 1) /* no result or uncertain */
352 return 0;
353 else if (r != 0) {
354 if (errno == 0)
355 errno = EIO;
356 log_error("Failed to probe %s: %m", what);
357 return -errno;
358 }
359
360 blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
361
362 return add_mount(
363 id,
364 what,
365 where,
366 fstype,
367 rw,
368 description,
369 post);
370 }
371
372 static int enumerate_partitions(dev_t devnum) {
373
374 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
375 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
376 _cleanup_blkid_free_probe_ blkid_probe b = NULL;
377 _cleanup_udev_unref_ struct udev *udev = NULL;
378 _cleanup_free_ char *home = NULL, *srv = NULL;
379 struct udev_list_entry *first, *item;
380 struct udev_device *parent = NULL;
381 const char *node, *pttype, *devtype;
382 int home_nr = -1, srv_nr = -1;
383 bool home_rw = true, srv_rw = true;
384 blkid_partlist pl;
385 int r, k;
386 dev_t pn;
387
388 udev = udev_new();
389 if (!udev)
390 return log_oom();
391
392 d = udev_device_new_from_devnum(udev, 'b', devnum);
393 if (!d)
394 return log_oom();
395
396 parent = udev_device_get_parent(d);
397 if (!parent) {
398 log_debug("Not a partitioned device, ignoring.");
399 return 0;
400 }
401
402 /* Does it have a devtype? */
403 devtype = udev_device_get_devtype(parent);
404 if (!devtype) {
405 log_debug("Parent doesn't have a device type, ignoring.");
406 return 0;
407 }
408
409 /* Is this a disk or a partition? We only care for disks... */
410 if (!streq(devtype, "disk")) {
411 log_debug("Parent isn't a raw disk, ignoring.");
412 return 0;
413 }
414
415 /* Does it have a device node? */
416 node = udev_device_get_devnode(parent);
417 if (!node) {
418 log_debug("Parent device does not have device node, ignoring.");
419 return 0;
420 }
421
422 log_debug("Root device %s.", node);
423
424 pn = udev_device_get_devnum(parent);
425 if (major(pn) == 0)
426 return 0;
427
428 errno = 0;
429 b = blkid_new_probe_from_filename(node);
430 if (!b) {
431 if (errno == 0)
432 return log_oom();
433
434 log_error("Failed allocate prober: %m");
435 return -errno;
436 }
437
438 blkid_probe_enable_partitions(b, 1);
439 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
440
441 errno = 0;
442 r = blkid_do_safeprobe(b);
443 if (r == -2 || r == 1) /* no result or uncertain */
444 return 0;
445 else if (r != 0) {
446 if (errno == 0)
447 errno = EIO;
448 log_error("Failed to probe %s: %m", node);
449 return -errno;
450 }
451
452 errno = 0;
453 r = blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
454 if (r != 0) {
455 if (errno == 0)
456 errno = EIO;
457 log_error("Failed to determine partition table type of %s: %m", node);
458 return -errno;
459 }
460
461 /* We only do this all for GPT... */
462 if (!streq_ptr(pttype, "gpt")) {
463 log_debug("Not a GPT partition table, ignoring.");
464 return 0;
465 }
466
467 errno = 0;
468 pl = blkid_probe_get_partitions(b);
469 if (!pl) {
470 if (errno == 0)
471 return log_oom();
472
473 log_error("Failed to list partitions of %s: %m", node);
474 return -errno;
475 }
476
477 e = udev_enumerate_new(udev);
478 if (!e)
479 return log_oom();
480
481 r = udev_enumerate_add_match_parent(e, parent);
482 if (r < 0)
483 return log_oom();
484
485 r = udev_enumerate_add_match_subsystem(e, "block");
486 if (r < 0)
487 return log_oom();
488
489 r = udev_enumerate_scan_devices(e);
490 if (r < 0) {
491 log_error("Failed to enumerate partitions on %s: %s", node, strerror(-r));
492 return r;
493 }
494
495 first = udev_enumerate_get_list_entry(e);
496 udev_list_entry_foreach(item, first) {
497 _cleanup_udev_device_unref_ struct udev_device *q;
498 const char *stype, *subnode;
499 sd_id128_t type_id;
500 blkid_partition pp;
501 dev_t qn;
502 int nr;
503 unsigned long long flags;
504
505 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
506 if (!q)
507 continue;
508
509 qn = udev_device_get_devnum(q);
510 if (major(qn) == 0)
511 continue;
512
513 if (qn == devnum)
514 continue;
515
516 if (qn == pn)
517 continue;
518
519 subnode = udev_device_get_devnode(q);
520 if (!subnode)
521 continue;
522
523 pp = blkid_partlist_devno_to_partition(pl, qn);
524 if (!pp)
525 continue;
526
527 flags = blkid_partition_get_flags(pp);
528
529 /* Ignore partitions that are not marked for automatic
530 * mounting on discovery */
531 if (flags & GPT_FLAG_NO_AUTO)
532 continue;
533
534 nr = blkid_partition_get_partno(pp);
535 if (nr < 0)
536 continue;
537
538 stype = blkid_partition_get_type_string(pp);
539 if (!stype)
540 continue;
541
542 if (sd_id128_from_string(stype, &type_id) < 0)
543 continue;
544
545 if (sd_id128_equal(type_id, GPT_SWAP)) {
546
547 if (flags & GPT_FLAG_READ_ONLY) {
548 log_debug("%s marked as read-only swap partition, which is bogus, ignoring.", subnode);
549 continue;
550 }
551
552 k = add_swap(subnode);
553 if (k < 0)
554 r = k;
555
556 } else if (sd_id128_equal(type_id, GPT_HOME)) {
557
558 /* We only care for the first /home partition */
559 if (home && nr >= home_nr)
560 continue;
561
562 home_nr = nr;
563 home_rw = !(flags & GPT_FLAG_READ_ONLY),
564
565 free(home);
566 home = strdup(subnode);
567 if (!home)
568 return log_oom();
569
570 } else if (sd_id128_equal(type_id, GPT_SRV)) {
571
572 /* We only care for the first /srv partition */
573 if (srv && nr >= srv_nr)
574 continue;
575
576 srv_nr = nr;
577 srv_rw = !(flags & GPT_FLAG_READ_ONLY),
578
579 free(srv);
580 srv = strdup(node);
581 if (!srv)
582 return log_oom();
583 }
584 }
585
586 if (home) {
587 k = probe_and_add_mount("home", home, "/home", home_rw, "Home Partition", SPECIAL_LOCAL_FS_TARGET);
588 if (k < 0)
589 r = k;
590 }
591
592 if (srv) {
593 k = probe_and_add_mount("srv", srv, "/srv", srv_rw, "Server Data Partition", SPECIAL_LOCAL_FS_TARGET);
594 if (k < 0)
595 r = k;
596 }
597
598 return r;
599 }
600
601 static int get_btrfs_block_device(const char *path, dev_t *dev) {
602 struct btrfs_ioctl_fs_info_args fsi = {};
603 _cleanup_close_ int fd = -1;
604 uint64_t id;
605
606 assert(path);
607 assert(dev);
608
609 fd = open(path, O_DIRECTORY|O_CLOEXEC);
610 if (fd < 0)
611 return -errno;
612
613 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
614 return -errno;
615
616 /* We won't do this for btrfs RAID */
617 if (fsi.num_devices != 1)
618 return 0;
619
620 for (id = 1; id <= fsi.max_id; id++) {
621 struct btrfs_ioctl_dev_info_args di = {
622 .devid = id,
623 };
624 struct stat st;
625
626 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
627 if (errno == ENODEV)
628 continue;
629
630 return -errno;
631 }
632
633 if (stat((char*) di.path, &st) < 0)
634 return -errno;
635
636 if (!S_ISBLK(st.st_mode))
637 return -ENODEV;
638
639 if (major(st.st_rdev) == 0)
640 return -ENODEV;
641
642 *dev = st.st_rdev;
643 return 1;
644 }
645
646 return -ENODEV;
647 }
648
649 static int get_block_device(const char *path, dev_t *dev) {
650 struct stat st;
651 struct statfs sfs;
652
653 assert(path);
654 assert(dev);
655
656 if (lstat(path, &st))
657 return -errno;
658
659 if (major(st.st_dev) != 0) {
660 *dev = st.st_dev;
661 return 1;
662 }
663
664 if (statfs(path, &sfs) < 0)
665 return -errno;
666
667 if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
668 return get_btrfs_block_device(path, dev);
669
670 return 0;
671 }
672
673 static int parse_proc_cmdline_item(const char *key, const char *value) {
674 int r;
675
676 assert(key);
677
678 if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto") && value) {
679
680 r = parse_boolean(value);
681 if (r < 0)
682 log_warning("Failed to parse gpt-auto switch %s. Ignoring.", value);
683
684 arg_enabled = r;
685
686 } else if (streq(key, "root") && value) {
687
688 /* Disable root disk logic if there's a root= value
689 * specified (unless it happens to be "gpt-auto") */
690
691 arg_root_enabled = streq(value, "gpt-auto");
692
693 } else if (streq(key, "rw") && !value)
694 arg_root_rw = true;
695 else if (streq(key, "ro") && !value)
696 arg_root_rw = false;
697
698 return 0;
699 }
700
701 static int add_root_mount(void) {
702
703 #ifdef ENABLE_EFI
704 int r;
705
706 if (!is_efi_boot()) {
707 log_debug("Not a EFI boot, not creating root mount.");
708 return 0;
709 }
710
711 r = efi_loader_get_device_part_uuid(NULL);
712 if (r == -ENOENT) {
713 log_debug("EFI loader partition unknown, exiting.");
714 return 0;
715 } else if (r < 0) {
716 log_error("Failed to read ESP partition UUID: %s", strerror(-r));
717 return r;
718 }
719
720 /* OK, we have an ESP partition, this is fantastic, so let's
721 * wait for a root device to show up. A udev rule will create
722 * the link for us under the right name. */
723
724 return add_mount(
725 "root",
726 "/dev/gpt-auto-root",
727 in_initrd() ? "/sysroot" : "/",
728 NULL,
729 arg_root_rw,
730 "Root Partition",
731 in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
732 #else
733 return 0;
734 #endif
735 }
736
737 static int add_mounts(void) {
738 dev_t devno;
739 int r;
740
741 r = get_block_device("/", &devno);
742 if (r < 0) {
743 log_error("Failed to determine block device of root file system: %s", strerror(-r));
744 return r;
745 } else if (r == 0) {
746 log_debug("Root file system not on a (single) block device.");
747 return 0;
748 }
749
750 return enumerate_partitions(devno);
751 }
752
753 int main(int argc, char *argv[]) {
754 int r = 0;
755
756 if (argc > 1 && argc != 4) {
757 log_error("This program takes three or no arguments.");
758 return EXIT_FAILURE;
759 }
760
761 if (argc > 1)
762 arg_dest = argv[3];
763
764 log_set_target(LOG_TARGET_SAFE);
765 log_parse_environment();
766 log_open();
767
768 umask(0022);
769
770 if (detect_container(NULL) > 0) {
771 log_debug("In a container, exiting.");
772 return EXIT_SUCCESS;
773 }
774
775 if (parse_proc_cmdline(parse_proc_cmdline_item) < 0)
776 return EXIT_FAILURE;
777
778 if (!arg_enabled) {
779 log_debug("Disabled, exiting.");
780 return EXIT_SUCCESS;
781 }
782
783 if (arg_root_enabled)
784 r = add_root_mount();
785
786 if (!in_initrd()) {
787 int k;
788
789 k = add_mounts();
790 if (k < 0)
791 r = k;
792 }
793
794 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
795 }