1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
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.
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.
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/>.
24 #include <sys/statfs.h>
25 #include <blkid/blkid.h>
29 #include "path-util.h"
33 #include "udev-util.h"
35 #include "unit-name.h"
37 #include "generator.h"
41 #include "blkid-util.h"
42 #include "btrfs-util.h"
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;
49 static int add_swap(const char *path) {
50 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
51 _cleanup_fclose_ FILE *f = NULL;
55 log_debug("Adding swap: %s", path);
57 name = unit_name_from_path(path, ".swap");
61 unit = strjoin(arg_dest, "/", name, NULL);
65 f = fopen(unit, "wxe");
67 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
70 "# Automatically generated by systemd-gpt-auto-generator\n\n"
72 "Description=Swap Partition\n"
73 "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
80 return log_error_errno(errno, "Failed to write unit file %s: %m", unit);
82 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
86 mkdir_parents_label(lnk, 0755);
87 if (symlink(unit, lnk) < 0)
88 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
93 static int add_cryptsetup(const char *id, const char *what, bool rw, char **device) {
94 _cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL;
95 _cleanup_fclose_ FILE *f = NULL;
103 d = unit_name_from_path(what, ".device");
107 e = unit_name_escape(id);
111 n = unit_name_build("systemd-cryptsetup", e, ".service");
115 p = strjoin(arg_dest, "/", n, NULL);
121 return log_error_errno(errno, "Failed to create unit file %s: %m", p);
124 "# Automatically generated by systemd-gpt-auto-generator\n\n"
126 "Description=Cryptography Setup for %%I\n"
127 "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n"
128 "DefaultDependencies=no\n"
129 "Conflicts=umount.target\n"
130 "BindsTo=dev-mapper-%%i.device %s\n"
131 "Before=umount.target cryptsetup.target\n"
133 "IgnoreOnIsolate=true\n"
136 "RemainAfterExit=yes\n"
137 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
138 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n"
139 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
141 id, what, rw ? "" : "read-only",
146 return log_error_errno(errno, "Failed to write file %s: %m", p);
148 from = strjoina("../", n);
150 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
154 mkdir_parents_label(to, 0755);
155 if (symlink(from, to) < 0)
156 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
159 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
163 mkdir_parents_label(to, 0755);
164 if (symlink(from, to) < 0)
165 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
168 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
172 mkdir_parents_label(to, 0755);
173 if (symlink(from, to) < 0)
174 return log_error_errno(errno, "Failed to create symlink %s: %m", to);
177 p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
181 mkdir_parents_label(p, 0755);
182 r = write_string_file(p,
183 "# Automatically generated by systemd-gpt-auto-generator\n\n"
185 "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
187 return log_error_errno(r, "Failed to write device drop-in: %m");
189 ret = strappend("/dev/mapper/", id);
197 static int add_mount(
203 const char *description,
206 _cleanup_free_ char *unit = NULL, *lnk = NULL, *crypto_what = NULL, *p = NULL;
207 _cleanup_fclose_ FILE *f = NULL;
215 log_debug("Adding %s: %s %s", where, what, strna(fstype));
217 if (streq_ptr(fstype, "crypto_LUKS")) {
219 r = add_cryptsetup(id, what, rw, &crypto_what);
227 unit = unit_name_from_path(where, ".mount");
231 p = strjoin(arg_dest, "/", unit, NULL);
237 return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
240 "# Automatically generated by systemd-gpt-auto-generator\n\n"
243 "Documentation=man:systemd-gpt-auto-generator(8)\n",
247 fprintf(f, "Before=%s\n", post);
249 r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
261 fprintf(f, "Type=%s\n", fstype);
263 fprintf(f, "Options=%s\n", rw ? "rw" : "ro");
267 return log_error_errno(errno, "Failed to write unit file %s: %m", p);
270 lnk = strjoin(arg_dest, "/", post, ".requires/", unit, NULL);
274 mkdir_parents_label(lnk, 0755);
275 if (symlink(p, lnk) < 0)
276 return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
282 static int probe_and_add_mount(
287 const char *description,
290 _cleanup_blkid_free_probe_ blkid_probe b = NULL;
291 const char *fstype = NULL;
299 if (path_is_mount_point(where, true) <= 0 &&
300 dir_is_empty(where) <= 0) {
301 log_debug("%s already populated, ignoring.", where);
305 /* Let's check the partition type here, so that we know
306 * whether to do LUKS magic. */
309 b = blkid_new_probe_from_filename(what);
313 log_error_errno(errno, "Failed to allocate prober: %m");
317 blkid_probe_enable_superblocks(b, 1);
318 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
321 r = blkid_do_safeprobe(b);
322 if (r == -2 || r == 1) /* no result or uncertain */
325 return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what);
327 /* add_mount is OK with fstype being NULL. */
328 (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
340 static int enumerate_partitions(dev_t devnum) {
342 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
343 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
344 _cleanup_blkid_free_probe_ blkid_probe b = NULL;
345 _cleanup_udev_unref_ struct udev *udev = NULL;
346 _cleanup_free_ char *home = NULL, *srv = NULL;
347 struct udev_list_entry *first, *item;
348 struct udev_device *parent = NULL;
349 const char *name, *node, *pttype, *devtype;
350 int home_nr = -1, srv_nr = -1;
351 bool home_rw = true, srv_rw = true;
360 d = udev_device_new_from_devnum(udev, 'b', devnum);
364 name = udev_device_get_devnode(d);
366 name = udev_device_get_syspath(d);
368 log_debug("Device %u:%u does not have a name, ignoring.",
369 major(devnum), minor(devnum));
373 parent = udev_device_get_parent(d);
375 log_debug("%s: not a partitioned device, ignoring.", name);
379 /* Does it have a devtype? */
380 devtype = udev_device_get_devtype(parent);
382 log_debug("%s: parent doesn't have a device type, ignoring.", name);
386 /* Is this a disk or a partition? We only care for disks... */
387 if (!streq(devtype, "disk")) {
388 log_debug("%s: parent isn't a raw disk, ignoring.", name);
392 /* Does it have a device node? */
393 node = udev_device_get_devnode(parent);
395 log_debug("%s: parent device does not have device node, ignoring.", name);
399 log_debug("%s: root device %s.", name, node);
401 pn = udev_device_get_devnum(parent);
406 b = blkid_new_probe_from_filename(node);
411 return log_error_errno(errno, "%s: failed to allocate prober: %m", node);
414 blkid_probe_enable_partitions(b, 1);
415 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
418 r = blkid_do_safeprobe(b);
419 if (r == -2 || r == 1) /* no result or uncertain */
422 return log_error_errno(errno ?: EIO, "%s: failed to probe: %m", node);
425 r = blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
427 return log_error_errno(errno ?: EIO,
428 "%s: failed to determine partition table type: %m", node);
430 /* We only do this all for GPT... */
431 if (!streq_ptr(pttype, "gpt")) {
432 log_debug("%s: not a GPT partition table, ignoring.", node);
437 pl = blkid_probe_get_partitions(b);
442 return log_error_errno(errno, "%s: failed to list partitions: %m", node);
445 e = udev_enumerate_new(udev);
449 r = udev_enumerate_add_match_parent(e, parent);
453 r = udev_enumerate_add_match_subsystem(e, "block");
457 r = udev_enumerate_scan_devices(e);
459 return log_error_errno(r, "%s: failed to enumerate partitions: %m", node);
461 first = udev_enumerate_get_list_entry(e);
462 udev_list_entry_foreach(item, first) {
463 _cleanup_udev_device_unref_ struct udev_device *q;
464 const char *stype, *subnode;
469 unsigned long long flags;
471 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
475 qn = udev_device_get_devnum(q);
485 subnode = udev_device_get_devnode(q);
489 pp = blkid_partlist_devno_to_partition(pl, qn);
493 flags = blkid_partition_get_flags(pp);
495 /* Ignore partitions that are not marked for automatic
496 * mounting on discovery */
497 if (flags & GPT_FLAG_NO_AUTO)
500 nr = blkid_partition_get_partno(pp);
504 stype = blkid_partition_get_type_string(pp);
508 if (sd_id128_from_string(stype, &type_id) < 0)
511 if (sd_id128_equal(type_id, GPT_SWAP)) {
513 if (flags & GPT_FLAG_READ_ONLY) {
514 log_debug("%s marked as read-only swap partition, which is bogus. Ignoring.", subnode);
518 k = add_swap(subnode);
522 } else if (sd_id128_equal(type_id, GPT_HOME)) {
524 /* We only care for the first /home partition */
525 if (home && nr >= home_nr)
529 home_rw = !(flags & GPT_FLAG_READ_ONLY),
532 home = strdup(subnode);
536 } else if (sd_id128_equal(type_id, GPT_SRV)) {
538 /* We only care for the first /srv partition */
539 if (srv && nr >= srv_nr)
543 srv_rw = !(flags & GPT_FLAG_READ_ONLY),
546 srv = strdup(subnode);
553 k = probe_and_add_mount("home", home, "/home", home_rw, "Home Partition", SPECIAL_LOCAL_FS_TARGET);
559 k = probe_and_add_mount("srv", srv, "/srv", srv_rw, "Server Data Partition", SPECIAL_LOCAL_FS_TARGET);
567 static int get_block_device(const char *path, dev_t *dev) {
574 if (lstat(path, &st))
577 if (major(st.st_dev) != 0) {
582 if (statfs(path, &sfs) < 0)
585 if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
586 return btrfs_get_block_device(path, dev);
591 static int parse_proc_cmdline_item(const char *key, const char *value) {
596 if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto") && value) {
598 r = parse_boolean(value);
600 log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value);
604 } else if (streq(key, "root") && value) {
606 /* Disable root disk logic if there's a root= value
607 * specified (unless it happens to be "gpt-auto") */
609 arg_root_enabled = streq(value, "gpt-auto");
611 } else if (streq(key, "rw") && !value)
613 else if (streq(key, "ro") && !value)
619 static int add_root_mount(void) {
624 if (!is_efi_boot()) {
625 log_debug("Not a EFI boot, not creating root mount.");
629 r = efi_loader_get_device_part_uuid(NULL);
631 log_debug("EFI loader partition unknown, exiting.");
634 return log_error_errno(r, "Failed to read ESP partition UUID: %m");
636 /* OK, we have an ESP partition, this is fantastic, so let's
637 * wait for a root device to show up. A udev rule will create
638 * the link for us under the right name. */
642 "/dev/gpt-auto-root",
643 in_initrd() ? "/sysroot" : "/",
647 in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
653 static int add_mounts(void) {
657 r = get_block_device("/", &devno);
659 return log_error_errno(r, "Failed to determine block device of root file system: %m");
661 log_debug("Root file system not on a (single) block device.");
665 return enumerate_partitions(devno);
668 int main(int argc, char *argv[]) {
671 if (argc > 1 && argc != 4) {
672 log_error("This program takes three or no arguments.");
679 log_set_target(LOG_TARGET_SAFE);
680 log_parse_environment();
685 if (detect_container(NULL) > 0) {
686 log_debug("In a container, exiting.");
690 r = parse_proc_cmdline(parse_proc_cmdline_item);
692 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
695 log_debug("Disabled, exiting.");
699 if (arg_root_enabled)
700 r = add_root_mount();
710 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;