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