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