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