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