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