chiark / gitweb /
gpt-auto-generator: print debug messages when we ignore a block device
[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, 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 (!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' '' '%s'\n"
153                 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
154                 d, d,
155                 id, what, rw ? "" : "read-only",
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                 bool rw,
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, rw, &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         fprintf(f, "Options=%s\n", rw ? "rw" : "ro");
290
291         fflush(f);
292         if (ferror(f)) {
293                 log_error("Failed to write unit file %s: %m", p);
294                 return -errno;
295         }
296
297         if (post) {
298                 lnk = strjoin(arg_dest, "/", post, ".requires/", unit, NULL);
299                 if (!lnk)
300                         return log_oom();
301
302                 mkdir_parents_label(lnk, 0755);
303                 if (symlink(p, lnk) < 0) {
304                         log_error("Failed to create symlink %s: %m", lnk);
305                         return -errno;
306                 }
307         }
308
309         return 0;
310 }
311
312 static int probe_and_add_mount(
313                 const char *id,
314                 const char *what,
315                 const char *where,
316                 bool rw,
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                         rw,
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         bool home_rw = true, srv_rw = true;
385         blkid_partlist pl;
386         int r, k;
387         dev_t pn;
388
389         udev = udev_new();
390         if (!udev)
391                 return log_oom();
392
393         d = udev_device_new_from_devnum(udev, 'b', devnum);
394         if (!d)
395                 return log_oom();
396
397         parent = udev_device_get_parent(d);
398         if (!parent) {
399                 log_debug("Not a partitioned device, ignoring.");
400                 return 0;
401         }
402
403         /* Does it have a devtype? */
404         devtype = udev_device_get_devtype(parent);
405         if (!devtype) {
406                 log_debug("Parent doesn't have a device type, ignoring.");
407                 return 0;
408         }
409
410         /* Is this a disk or a partition? We only care for disks... */
411         if (!streq(devtype, "disk")) {
412                 log_debug("Parent isn't a raw disk, ignoring.");
413                 return 0;
414         }
415
416         /* Does it have a device node? */
417         node = udev_device_get_devnode(parent);
418         if (!node) {
419                 log_debug("Parent device does not have device node, ignoring.");
420                 return 0;
421         }
422
423         log_debug("Root device %s.", node);
424
425         pn = udev_device_get_devnum(parent);
426         if (major(pn) == 0)
427                 return 0;
428
429         errno = 0;
430         b = blkid_new_probe_from_filename(node);
431         if (!b) {
432                 if (errno == 0)
433                         return log_oom();
434
435                 log_error("Failed allocate prober: %m");
436                 return -errno;
437         }
438
439         blkid_probe_enable_superblocks(b, 1);
440         blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
441         blkid_probe_enable_partitions(b, 1);
442         blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
443
444         errno = 0;
445         r = blkid_do_safeprobe(b);
446         if (r == -2 || r == 1) /* no result or uncertain */
447                 return 0;
448         else if (r != 0) {
449                 if (errno == 0)
450                         errno = EIO;
451                 log_error("Failed to probe %s: %m", node);
452                 return -errno;
453         }
454
455         errno = 0;
456         r = blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
457         if (r != 0) {
458                 if (errno == 0)
459                         errno = EIO;
460                 log_error("Failed to determine partition table type of %s: %m", node);
461                 return -errno;
462         }
463
464         /* We only do this all for GPT... */
465         if (!streq_ptr(pttype, "gpt")) {
466                 log_debug("Not a GPT partition table, ignoring.");
467                 return 0;
468         }
469
470         errno = 0;
471         pl = blkid_probe_get_partitions(b);
472         if (!pl) {
473                 if (errno == 0)
474                         return log_oom();
475
476                 log_error("Failed to list partitions of %s: %m", node);
477                 return -errno;
478         }
479
480         e = udev_enumerate_new(udev);
481         if (!e)
482                 return log_oom();
483
484         r = udev_enumerate_add_match_parent(e, parent);
485         if (r < 0)
486                 return log_oom();
487
488         r = udev_enumerate_add_match_subsystem(e, "block");
489         if (r < 0)
490                 return log_oom();
491
492         r = udev_enumerate_scan_devices(e);
493         if (r < 0) {
494                 log_error("Failed to enumerate partitions on %s: %s", node, strerror(-r));
495                 return r;
496         }
497
498         first = udev_enumerate_get_list_entry(e);
499         udev_list_entry_foreach(item, first) {
500                 _cleanup_udev_device_unref_ struct udev_device *q;
501                 const char *stype, *subnode;
502                 sd_id128_t type_id;
503                 blkid_partition pp;
504                 dev_t qn;
505                 int nr;
506                 unsigned long long flags;
507
508                 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
509                 if (!q)
510                         continue;
511
512                 qn = udev_device_get_devnum(q);
513                 if (major(qn) == 0)
514                         continue;
515
516                 if (qn == devnum)
517                         continue;
518
519                 if (qn == pn)
520                         continue;
521
522                 subnode = udev_device_get_devnode(q);
523                 if (!subnode)
524                         continue;
525
526                 pp = blkid_partlist_devno_to_partition(pl, qn);
527                 if (!pp)
528                         continue;
529
530                 flags = blkid_partition_get_flags(pp);
531
532                 /* Ignore partitions that are not marked for automatic
533                  * mounting on discovery */
534                 if (flags & GPT_FLAG_NO_AUTO)
535                         continue;
536
537                 nr = blkid_partition_get_partno(pp);
538                 if (nr < 0)
539                         continue;
540
541                 stype = blkid_partition_get_type_string(pp);
542                 if (!stype)
543                         continue;
544
545                 if (sd_id128_from_string(stype, &type_id) < 0)
546                         continue;
547
548                 if (sd_id128_equal(type_id, GPT_SWAP)) {
549
550                         if (flags & GPT_FLAG_READ_ONLY) {
551                                 log_debug("%s marked as read-only swap partition, which is bogus, ignoring.", subnode);
552                                 continue;
553                         }
554
555                         k = add_swap(subnode);
556                         if (k < 0)
557                                 r = k;
558
559                 } else if (sd_id128_equal(type_id, GPT_HOME)) {
560
561                         /* We only care for the first /home partition */
562                         if (home && nr >= home_nr)
563                                 continue;
564
565                         home_nr = nr;
566                         home_rw = !(flags & GPT_FLAG_READ_ONLY),
567
568                         free(home);
569                         home = strdup(subnode);
570                         if (!home)
571                                 return log_oom();
572
573                 } else if (sd_id128_equal(type_id, GPT_SRV)) {
574
575                         /* We only care for the first /srv partition */
576                         if (srv && nr >= srv_nr)
577                                 continue;
578
579                         srv_nr = nr;
580                         srv_rw = !(flags & GPT_FLAG_READ_ONLY),
581
582                         free(srv);
583                         srv = strdup(node);
584                         if (!srv)
585                                 return log_oom();
586                 }
587         }
588
589         if (home) {
590                 k = probe_and_add_mount("home", home, "/home", home_rw, "Home Partition", SPECIAL_LOCAL_FS_TARGET);
591                 if (k < 0)
592                         r = k;
593         }
594
595         if (srv) {
596                 k = probe_and_add_mount("srv", srv, "/srv", srv_rw, "Server Data Partition", SPECIAL_LOCAL_FS_TARGET);
597                 if (k < 0)
598                         r = k;
599         }
600
601         return r;
602 }
603
604 static int get_btrfs_block_device(const char *path, dev_t *dev) {
605         struct btrfs_ioctl_fs_info_args fsi = {};
606         _cleanup_close_ int fd = -1;
607         uint64_t id;
608
609         assert(path);
610         assert(dev);
611
612         fd = open(path, O_DIRECTORY|O_CLOEXEC);
613         if (fd < 0)
614                 return -errno;
615
616         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
617                 return -errno;
618
619         /* We won't do this for btrfs RAID */
620         if (fsi.num_devices != 1)
621                 return 0;
622
623         for (id = 1; id <= fsi.max_id; id++) {
624                 struct btrfs_ioctl_dev_info_args di = {
625                         .devid = id,
626                 };
627                 struct stat st;
628
629                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
630                         if (errno == ENODEV)
631                                 continue;
632
633                         return -errno;
634                 }
635
636                 if (stat((char*) di.path, &st) < 0)
637                         return -errno;
638
639                 if (!S_ISBLK(st.st_mode))
640                         return -ENODEV;
641
642                 if (major(st.st_rdev) == 0)
643                         return -ENODEV;
644
645                 *dev = st.st_rdev;
646                 return 1;
647         }
648
649         return -ENODEV;
650 }
651
652 static int get_block_device(const char *path, dev_t *dev) {
653         struct stat st;
654         struct statfs sfs;
655
656         assert(path);
657         assert(dev);
658
659         if (lstat(path, &st))
660                 return -errno;
661
662         if (major(st.st_dev) != 0) {
663                 *dev = st.st_dev;
664                 return 1;
665         }
666
667         if (statfs(path, &sfs) < 0)
668                 return -errno;
669
670         if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
671                 return get_btrfs_block_device(path, dev);
672
673         return 0;
674 }
675
676 static int parse_proc_cmdline_item(const char *key, const char *value) {
677         int r;
678
679         assert(key);
680
681         if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto") && value) {
682
683                 r = parse_boolean(value);
684                 if (r < 0)
685                         log_warning("Failed to parse gpt-auto switch %s. Ignoring.", value);
686
687                 arg_enabled = r;
688
689         } else if (streq(key, "root") && value) {
690
691                 /* Disable root disk logic if there's a root= value
692                  * specified (unless it happens to be "gpt-auto") */
693
694                 arg_root_enabled = streq(value, "gpt-auto");
695
696         } else if (streq(key, "rw") && !value)
697                 arg_root_rw = true;
698         else if (streq(key, "ro") && !value)
699                 arg_root_rw = false;
700         else if (startswith(key, "systemd.gpt-auto.") || startswith(key, "rd.systemd.gpt-auto."))
701                 log_warning("Unknown kernel switch %s. Ignoring.", key);
702
703         return 0;
704 }
705
706 static int add_root_mount(void) {
707
708 #ifdef ENABLE_EFI
709         int r;
710
711         if (!is_efi_boot()) {
712                 log_debug("Not a EFI boot, not creating root mount.");
713                 return 0;
714         }
715
716         r = efi_loader_get_device_part_uuid(NULL);
717         if (r == -ENOENT) {
718                 log_debug("EFI loader partition unknown, exiting.");
719                 return 0;
720         } else if (r < 0) {
721                 log_error("Failed to read ESP partition UUID: %s", strerror(-r));
722                 return r;
723         }
724
725         /* OK, we have an ESP partition, this is fantastic, so let's
726          * wait for a root device to show up. A udev rule will create
727          * the link for us under the right name. */
728
729         return add_mount(
730                         "root",
731                         "/dev/gpt-auto-root",
732                         in_initrd() ? "/sysroot" : "/",
733                         NULL,
734                         arg_root_rw,
735                         "Root Partition",
736                         in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
737 #else
738         return 0;
739 #endif
740 }
741
742 static int add_mounts(void) {
743         dev_t devno;
744         int r;
745
746         r = get_block_device("/", &devno);
747         if (r < 0) {
748                 log_error("Failed to determine block device of root file system: %s", strerror(-r));
749                 return r;
750         } else if (r == 0) {
751                 log_debug("Root file system not on a (single) block device.");
752                 return 0;
753         }
754
755         return enumerate_partitions(devno);
756 }
757
758 int main(int argc, char *argv[]) {
759         int r = 0;
760
761         if (argc > 1 && argc != 4) {
762                 log_error("This program takes three or no arguments.");
763                 return EXIT_FAILURE;
764         }
765
766         if (argc > 1)
767                 arg_dest = argv[3];
768
769         log_set_target(LOG_TARGET_SAFE);
770         log_parse_environment();
771         log_open();
772
773         umask(0022);
774
775         if (detect_container(NULL) > 0) {
776                 log_debug("In a container, exiting.");
777                 return EXIT_SUCCESS;
778         }
779
780         if (parse_proc_cmdline(parse_proc_cmdline_item) < 0)
781                 return EXIT_FAILURE;
782
783         if (!arg_enabled) {
784                 log_debug("Disabled, exiting.");
785                 return EXIT_SUCCESS;
786         }
787
788         if (arg_root_enabled)
789                 r = add_root_mount();
790
791         if (!in_initrd()) {
792                 int k;
793
794                 k = add_mounts();
795                 if (k < 0)
796                         r = k;
797         }
798
799         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
800 }