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