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