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