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