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