chiark / gitweb /
gpt-auto-generator: properly handle LUKS partitions
[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
47 static const char *arg_dest = "/tmp";
48
49 DEFINE_TRIVIAL_CLEANUP_FUNC(blkid_probe, blkid_free_probe);
50 #define _cleanup_blkid_freep_probe_ _cleanup_(blkid_free_probep)
51
52 static int verify_gpt_partition(const char *node, sd_id128_t *type, unsigned *nr, char **fstype) {
53         _cleanup_blkid_freep_probe_ blkid_probe b = NULL;
54         const char *v;
55         int r;
56
57         errno = 0;
58         b = blkid_new_probe_from_filename(node);
59         if (!b)
60                 return errno != 0 ? -errno : -ENOMEM;
61
62         blkid_probe_enable_superblocks(b, 1);
63         blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
64         blkid_probe_enable_partitions(b, 1);
65         blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
66
67         errno = 0;
68         r = blkid_do_safeprobe(b);
69         if (r == -2 || r == 1) /* no result or uncertain */
70                 return -EBADSLT;
71         else if (r != 0)
72                 return errno ? -errno : -EIO;
73
74         errno = 0;
75         r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
76         if (r != 0)
77                 /* return 0 if we're not on GPT */
78                 return errno ? -errno : 0;
79
80         if (strcmp(v, "gpt") != 0)
81                 return 0;
82
83         if (type) {
84                 errno = 0;
85                 r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
86                 if (r != 0)
87                         return errno ? -errno : -EIO;
88
89                 r = sd_id128_from_string(v, type);
90                 if (r < 0)
91                         return r;
92         }
93
94         if (nr) {
95                 errno = 0;
96                 r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
97                 if (r != 0)
98                         return errno ? -errno : -EIO;
99
100                 r = safe_atou(v, nr);
101                 if (r < 0)
102                         return r;
103         }
104
105
106         if (fstype) {
107                 errno = 0;
108                 r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
109                 if (r != 0)
110                         *fstype = NULL;
111                 else {
112                         char *fst;
113
114                         fst = strdup(v);
115                         if (!fst)
116                                 return -ENOMEM;
117
118                         *fstype = fst;
119                 }
120         }
121
122         return 1;
123 }
124
125 static int add_swap(const char *path, const char *fstype) {
126         _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
127         _cleanup_fclose_ FILE *f = NULL;
128
129         log_debug("Adding swap: %s %s", path, fstype);
130
131         name = unit_name_from_path(path, ".swap");
132         if (!name)
133                 return log_oom();
134
135         unit = strjoin(arg_dest, "/", name, NULL);
136         if (!unit)
137                 return log_oom();
138
139         f = fopen(unit, "wxe");
140         if (!f) {
141                 log_error("Failed to create unit file %s: %m", unit);
142                 return -errno;
143         }
144
145         fprintf(f,
146                 "# Automatically generated by systemd-gpt-auto-generator\n\n"
147                 "[Swap]\n"
148                 "What=%s\n",
149                 path);
150
151         fflush(f);
152         if (ferror(f)) {
153                 log_error("Failed to write unit file %s: %m", unit);
154                 return -errno;
155         }
156
157         lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
158         if (!lnk)
159                 return log_oom();
160
161         mkdir_parents_label(lnk, 0755);
162         if (symlink(unit, lnk) < 0) {
163                 log_error("Failed to create symlink %s: %m", lnk);
164                 return -errno;
165         }
166
167         return 0;
168 }
169
170 static int add_cryptsetup(const char *id, const char *what, char **device) {
171         _cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL;
172         _cleanup_fclose_ FILE *f = NULL;
173         char *from, *ret;
174         int r;
175
176         assert(id);
177         assert(what);
178         assert(device);
179
180         d = unit_name_from_path(what, ".device");
181         if (!d)
182                 return log_oom();
183
184         e = unit_name_escape(id);
185         if (!e)
186                 return log_oom();
187
188         n = unit_name_build("systemd-cryptsetup", e, ".service");
189         if (!n)
190                 return log_oom();
191
192         p = strjoin(arg_dest, "/", n, NULL);
193         if (!n)
194                 return log_oom();
195
196         f = fopen(p, "wxe");
197         if (!f) {
198                 log_error("Failed to create unit file %s: %m", p);
199                 return -errno;
200         }
201
202         fprintf(f,
203                 "# Automatically generated by systemd-gpt-auto-generator\n\n"
204                 "[Unit]\n"
205                 "Description=Cryptography Setup for %%I\n"
206                 "Documentation=man:systemd-cryptsetup@.service(8)\n"
207                 "DefaultDependencies=no\n"
208                 "Conflicts=umount.target\n"
209                 "BindsTo=dev-mapper-%%i.device %s\n"
210                 "Before=umount.target cryptsetup.target\n"
211                 "After=%s\n"
212                 "IgnoreOnIsolate=true\n"
213                 "After=systemd-readahead-collect.service systemd-readahead-replay.service\n\n"
214                 "[Service]\n"
215                 "Type=oneshot\n"
216                 "RemainAfterExit=yes\n"
217                 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
218                 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s'\n"
219                 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
220                 d, d,
221                 id, what,
222                 id);
223
224         fflush(f);
225         if (ferror(f)) {
226                 log_error("Failed to write file %s: %m", p);
227                 return -errno;
228         }
229
230         from = strappenda("../", n);
231
232         to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
233         if (!to)
234                 return log_oom();
235
236         mkdir_parents_label(to, 0755);
237         if (symlink(from, to) < 0) {
238                 log_error("Failed to create symlink %s: %m", to);
239                 return -errno;
240         }
241
242         free(to);
243         to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
244         if (!to)
245                 return log_oom();
246
247         mkdir_parents_label(to, 0755);
248         if (symlink(from, to) < 0) {
249                 log_error("Failed to create symlink %s: %m", to);
250                 return -errno;
251         }
252
253         free(to);
254         to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
255         if (!to)
256                 return log_oom();
257
258         mkdir_parents_label(to, 0755);
259         if (symlink(from, to) < 0) {
260                 log_error("Failed to create symlink %s: %m", to);
261                 return -errno;
262         }
263
264         free(p);
265         p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
266         if (!p)
267                 return log_oom();
268
269         mkdir_parents_label(p, 0755);
270         r = write_string_file(p,
271                         "# Automatically generated by systemd-gpt-auto-generator\n\n"
272                         "[Unit]\n"
273                         "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
274         if (r < 0) {
275                 log_error("Failed to write device drop-in: %s", strerror(-r));
276                 return r;
277         }
278
279         ret = strappend("/dev/mapper/", id);
280         if (!ret)
281                 return log_oom();
282
283         *device = ret;
284         return 0;
285 }
286
287 static int add_mount(const char *id, const char *what, const char *where, const char *fstype, const char *description) {
288         _cleanup_free_ char *unit = NULL, *lnk = NULL, *crypto_what = NULL, *p = NULL;
289         _cleanup_fclose_ FILE *f = NULL;
290         int r;
291
292         assert(id);
293         assert(what);
294         assert(where);
295         assert(fstype);
296         assert(description);
297
298         if (dir_is_empty(where) <= 0) {
299                 log_debug("%s already populated, ignoring.", where);
300                 return 0;
301         }
302
303         log_debug("Adding %s: %s %s", where, what, fstype);
304
305         if (streq(fstype, "crypto_LUKS")) {
306
307                 r = add_cryptsetup(id, what, &crypto_what);
308                 if (r < 0)
309                         return r;
310
311                 what = crypto_what;
312                 fstype = NULL;
313         }
314
315         unit = unit_name_from_path(where, ".mount");
316         if (!unit)
317                 return log_oom();
318
319         p = strjoin(arg_dest, "/", unit, NULL);
320         if (!p)
321                 return log_oom();
322
323         f = fopen(p, "wxe");
324         if (!f) {
325                 log_error("Failed to create unit file %s: %m", unit);
326                 return -errno;
327         }
328
329         fprintf(f,
330                 "# Automatically generated by systemd-gpt-auto-generator\n\n"
331                 "[Unit]\n"
332                 "Description=%s\n",
333                 description);
334
335         r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
336         if (r < 0)
337                 return r;
338
339         fprintf(f,
340                 "\n"
341                 "[Mount]\n"
342                 "What=%s\n"
343                 "Where=%s\n",
344                 what, where);
345
346         if (fstype) {
347                 fprintf(f,
348                         "Type=%s\n",
349                         fstype);
350         }
351
352         fflush(f);
353         if (ferror(f)) {
354                 log_error("Failed to write unit file %s: %m", unit);
355                 return -errno;
356         }
357
358         lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".requires/", unit, NULL);
359         if (!lnk)
360                 return log_oom();
361
362         mkdir_parents_label(lnk, 0755);
363         if (symlink(unit, lnk) < 0) {
364                 log_error("Failed to create symlink %s: %m", lnk);
365                 return -errno;
366         }
367
368         return 0;
369 }
370
371 static int enumerate_partitions(struct udev *udev, dev_t dev) {
372         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
373         _cleanup_udev_device_unref_ struct udev_device *d = NULL;
374         _cleanup_free_ char *home = NULL, *home_fstype = NULL, *srv = NULL, *srv_fstype = NULL;
375         unsigned home_nr = (unsigned) -1, srv_nr = (unsigned )-1;
376         struct udev_list_entry *first, *item;
377         struct udev_device *parent = NULL;
378         int r;
379
380         e = udev_enumerate_new(udev);
381         if (!e)
382                 return log_oom();
383
384         d = udev_device_new_from_devnum(udev, 'b', dev);
385         if (!d)
386                 return log_oom();
387
388         parent = udev_device_get_parent(d);
389         if (!parent)
390                 return log_oom();
391
392         r = udev_enumerate_add_match_parent(e, parent);
393         if (r < 0)
394                 return log_oom();
395
396         r = udev_enumerate_add_match_subsystem(e, "block");
397         if (r < 0)
398                 return log_oom();
399
400         r = udev_enumerate_scan_devices(e);
401         if (r < 0) {
402                 log_error("Failed to enumerate partitions on /dev/block/%u:%u: %s", major(dev), minor(dev), strerror(-r));
403                 return r;
404         }
405
406         first = udev_enumerate_get_list_entry(e);
407         udev_list_entry_foreach(item, first) {
408                 _cleanup_udev_device_unref_ struct udev_device *q;
409                 _cleanup_free_ char *fstype = NULL;
410                 const char *node = NULL;
411                 sd_id128_t type_id;
412                 unsigned nr;
413
414                 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
415                 if (!q)
416                         return log_oom();
417
418                 if (udev_device_get_devnum(q) == udev_device_get_devnum(d))
419                         continue;
420
421                 if (udev_device_get_devnum(q) == udev_device_get_devnum(parent))
422                         continue;
423
424                 node = udev_device_get_devnode(q);
425                 if (!node)
426                         return log_oom();
427
428                 r = verify_gpt_partition(node, &type_id, &nr, &fstype);
429                 if (r < 0) {
430                         /* skip child devices which are not detected properly */
431                         if (r == -EBADSLT)
432                                 continue;
433                         log_error("Failed to verify GPT partition %s: %s", node, strerror(-r));
434                         return r;
435                 }
436                 if (r == 0)
437                         continue;
438
439                 if (sd_id128_equal(type_id, GPT_SWAP))
440                         add_swap(node, fstype);
441
442                 else if (sd_id128_equal(type_id, GPT_HOME)) {
443
444                         /* We only care for the first /home partition */
445                         if (home && nr >= home_nr)
446                                 continue;
447
448                         home_nr = nr;
449
450                         free(home);
451                         home = strdup(node);
452                         if (!home)
453                                 return log_oom();
454
455                         free(home_fstype);
456                         home_fstype = fstype;
457                         fstype = NULL;
458
459                 } else if (sd_id128_equal(type_id, GPT_SRV)) {
460
461                         /* We only care for the first /srv partition */
462                         if (srv && nr >= srv_nr)
463                                 continue;
464
465                         srv_nr = nr;
466
467                         free(srv);
468                         srv = strdup(node);
469                         if (!srv)
470                                 return log_oom();
471
472                         free(srv_fstype);
473                         srv_fstype = fstype;
474                         fstype = NULL;
475                 }
476         }
477
478         if (home && home_fstype)
479                 add_mount("home", home, "/home", home_fstype, "Home Partition");
480
481         if (srv && srv_fstype)
482                 add_mount("srv", srv, "/srv", srv_fstype, "Server Data Partition");
483
484         return r;
485 }
486
487 static int get_btrfs_block_device(const char *path, dev_t *dev) {
488         struct btrfs_ioctl_fs_info_args fsi = {};
489         _cleanup_close_ int fd = -1;
490         uint64_t id;
491
492         assert(path);
493         assert(dev);
494
495         fd = open(path, O_DIRECTORY|O_CLOEXEC);
496         if (fd < 0)
497                 return -errno;
498
499         if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
500                 return -errno;
501
502         /* We won't do this for btrfs RAID */
503         if (fsi.num_devices != 1)
504                 return 0;
505
506         for (id = 1; id <= fsi.max_id; id++) {
507                 struct btrfs_ioctl_dev_info_args di = {
508                         .devid = id,
509                 };
510                 struct stat st;
511
512                 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
513                         if (errno == ENODEV)
514                                 continue;
515
516                         return -errno;
517                 }
518
519                 if (stat((char*) di.path, &st) < 0)
520                         return -errno;
521
522                 if (!S_ISBLK(st.st_mode))
523                         return -ENODEV;
524
525                 if (major(st.st_rdev) == 0)
526                         return -ENODEV;
527
528                 *dev = st.st_rdev;
529                 return 1;
530         }
531
532         return -ENODEV;
533 }
534
535 static int get_block_device(const char *path, dev_t *dev) {
536         struct stat st;
537         struct statfs sfs;
538
539         assert(path);
540         assert(dev);
541
542         if (lstat(path, &st))
543                 return -errno;
544
545         if (major(st.st_dev) != 0) {
546                 *dev = st.st_dev;
547                 return 1;
548         }
549
550         if (statfs(path, &sfs) < 0)
551                 return -errno;
552
553         if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
554                 return get_btrfs_block_device(path, dev);
555
556         return 0;
557 }
558
559 static int devno_to_devnode(struct udev *udev, dev_t devno, char **ret) {
560         _cleanup_udev_device_unref_ struct udev_device *d;
561         const char *t;
562         char *n;
563
564         d = udev_device_new_from_devnum(udev, 'b', devno);
565         if (!d)
566                 return log_oom();
567
568         t = udev_device_get_devnode(d);
569         if (!t)
570                 return -ENODEV;
571
572         n = strdup(t);
573         if (!n)
574                 return -ENOMEM;
575
576         *ret = n;
577         return 0;
578 }
579
580 int main(int argc, char *argv[]) {
581         _cleanup_udev_unref_ struct udev *udev = NULL;
582         _cleanup_free_ char *node = NULL;
583         dev_t devno;
584         int r = 0;
585
586         if (argc > 1 && argc != 4) {
587                 log_error("This program takes three or no arguments.");
588                 return EXIT_FAILURE;
589         }
590
591         if (argc > 1)
592                 arg_dest = argv[3];
593
594         log_set_target(LOG_TARGET_SAFE);
595         log_parse_environment();
596         log_open();
597
598         umask(0022);
599
600         if (in_initrd()) {
601                 log_debug("In initrd, exiting.");
602                 return EXIT_SUCCESS;
603         }
604
605         if (detect_container(NULL) > 0) {
606                 log_debug("In a container, exiting.");
607                 return EXIT_SUCCESS;
608         }
609
610         r = get_block_device("/", &devno);
611         if (r < 0) {
612                 log_error("Failed to determine block device of root file system: %s", strerror(-r));
613                 return EXIT_FAILURE;
614         } else if (r == 0) {
615                 log_debug("Root file system not on a (single) block device.");
616                 return EXIT_SUCCESS;
617         }
618
619         udev = udev_new();
620         if (!udev) {
621                 log_oom();
622                 return EXIT_FAILURE;
623         }
624
625         r = devno_to_devnode(udev, devno, &node);
626         if (r < 0) {
627                 log_error("Failed to determine block device node from major/minor: %s", strerror(-r));
628                 return EXIT_FAILURE;
629         }
630
631         log_debug("Root device %s.", node);
632
633         r = verify_gpt_partition(node, NULL, NULL, NULL);
634         if (r < 0) {
635                 log_error("Failed to verify GPT partition %s: %s", node, strerror(-r));
636                 return EXIT_FAILURE;
637         }
638         if (r == 0) {
639                 log_debug("Not a GPT disk, exiting.");
640                 return EXIT_SUCCESS;
641         }
642
643         r = enumerate_partitions(udev, devno);
644         if (r < 0)
645                 return EXIT_FAILURE;
646
647         return EXIT_SUCCESS;
648 }