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/>.
25 #include <sys/ioctl.h>
26 #include <sys/statfs.h>
27 #include <blkid/blkid.h>
29 #ifdef HAVE_LINUX_BTRFS_H
30 #include <linux/btrfs.h>
35 #include "path-util.h"
39 #include "udev-util.h"
41 #include "unit-name.h"
43 #include "generator.h"
47 #include "blkid-util.h"
49 static const char *arg_dest = "/tmp";
50 static bool arg_enabled = true;
51 static bool arg_root_enabled = true;
52 static bool arg_root_rw = false;
54 static int add_swap(const char *path) {
55 _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
56 _cleanup_fclose_ FILE *f = NULL;
60 log_debug("Adding swap: %s", path);
62 name = unit_name_from_path(path, ".swap");
66 unit = strjoin(arg_dest, "/", name, NULL);
70 f = fopen(unit, "wxe");
72 log_error("Failed to create unit file %s: %m", unit);
77 "# Automatically generated by systemd-gpt-auto-generator\n\n"
79 "Description=Swap Partition\n"
80 "Documentation=man:systemd-gpt-auto-generator(8)\n\n"
87 log_error("Failed to write unit file %s: %m", unit);
91 lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL);
95 mkdir_parents_label(lnk, 0755);
96 if (symlink(unit, lnk) < 0) {
97 log_error("Failed to create symlink %s: %m", lnk);
104 static int add_cryptsetup(const char *id, const char *what, bool rw, char **device) {
105 _cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL;
106 _cleanup_fclose_ FILE *f = NULL;
114 d = unit_name_from_path(what, ".device");
118 e = unit_name_escape(id);
122 n = unit_name_build("systemd-cryptsetup", e, ".service");
126 p = strjoin(arg_dest, "/", n, NULL);
132 log_error("Failed to create unit file %s: %m", p);
137 "# Automatically generated by systemd-gpt-auto-generator\n\n"
139 "Description=Cryptography Setup for %%I\n"
140 "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n"
141 "DefaultDependencies=no\n"
142 "Conflicts=umount.target\n"
143 "BindsTo=dev-mapper-%%i.device %s\n"
144 "Before=umount.target cryptsetup.target\n"
146 "IgnoreOnIsolate=true\n"
147 "After=systemd-readahead-collect.service systemd-readahead-replay.service\n\n"
150 "RemainAfterExit=yes\n"
151 "TimeoutSec=0\n" /* the binary handles timeouts anyway */
152 "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n"
153 "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
155 id, what, rw ? "" : "read-only",
160 log_error("Failed to write file %s: %m", p);
164 from = strappenda("../", n);
166 to = strjoin(arg_dest, "/", d, ".wants/", n, NULL);
170 mkdir_parents_label(to, 0755);
171 if (symlink(from, to) < 0) {
172 log_error("Failed to create symlink %s: %m", to);
177 to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL);
181 mkdir_parents_label(to, 0755);
182 if (symlink(from, to) < 0) {
183 log_error("Failed to create symlink %s: %m", to);
188 to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL);
192 mkdir_parents_label(to, 0755);
193 if (symlink(from, to) < 0) {
194 log_error("Failed to create symlink %s: %m", to);
199 p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL);
203 mkdir_parents_label(p, 0755);
204 r = write_string_file(p,
205 "# Automatically generated by systemd-gpt-auto-generator\n\n"
207 "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */
209 log_error("Failed to write device drop-in: %s", strerror(-r));
213 ret = strappend("/dev/mapper/", id);
221 static int add_mount(
227 const char *description,
230 _cleanup_free_ char *unit = NULL, *lnk = NULL, *crypto_what = NULL, *p = NULL;
231 _cleanup_fclose_ FILE *f = NULL;
239 log_debug("Adding %s: %s %s", where, what, strna(fstype));
241 if (streq_ptr(fstype, "crypto_LUKS")) {
243 r = add_cryptsetup(id, what, rw, &crypto_what);
251 unit = unit_name_from_path(where, ".mount");
255 p = strjoin(arg_dest, "/", unit, NULL);
261 log_error("Failed to create unit file %s: %m", unit);
266 "# Automatically generated by systemd-gpt-auto-generator\n\n"
269 "Documentation=man:systemd-gpt-auto-generator(8)\n",
273 fprintf(f, "Before=%s\n", post);
275 r = generator_write_fsck_deps(f, arg_dest, what, where, fstype);
287 fprintf(f, "Type=%s\n", fstype);
289 fprintf(f, "Options=%s\n", rw ? "rw" : "ro");
293 log_error("Failed to write unit file %s: %m", p);
298 lnk = strjoin(arg_dest, "/", post, ".requires/", unit, NULL);
302 mkdir_parents_label(lnk, 0755);
303 if (symlink(p, lnk) < 0) {
304 log_error("Failed to create symlink %s: %m", lnk);
312 static int probe_and_add_mount(
317 const char *description,
320 _cleanup_blkid_free_probe_ blkid_probe b = NULL;
329 if (path_is_mount_point(where, true) <= 0 &&
330 dir_is_empty(where) <= 0) {
331 log_debug("%s already populated, ignoring.", where);
335 /* Let's check the partition type here, so that we know
336 * whether to do LUKS magic. */
339 b = blkid_new_probe_from_filename(what);
343 log_error("Failed to allocate prober: %m");
347 blkid_probe_enable_superblocks(b, 1);
348 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
351 r = blkid_do_safeprobe(b);
352 if (r == -2 || r == 1) /* no result or uncertain */
357 log_error("Failed to probe %s: %m", what);
361 blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
373 static int enumerate_partitions(dev_t devnum) {
375 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
376 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
377 _cleanup_blkid_free_probe_ blkid_probe b = NULL;
378 _cleanup_udev_unref_ struct udev *udev = NULL;
379 _cleanup_free_ char *home = NULL, *srv = NULL;
380 struct udev_list_entry *first, *item;
381 struct udev_device *parent = NULL;
382 const char *node, *pttype, *devtype;
383 int home_nr = -1, srv_nr = -1;
384 bool home_rw = true, srv_rw = true;
393 d = udev_device_new_from_devnum(udev, 'b', devnum);
397 parent = udev_device_get_parent(d);
401 /* Does it have a devtype? */
402 devtype = udev_device_get_devtype(parent);
406 /* Is this a disk or a partition? We only care for disks... */
407 if (!streq(devtype, "disk"))
410 /* Does it have a device node? */
411 node = udev_device_get_devnode(parent);
415 log_debug("Root device %s.", node);
417 pn = udev_device_get_devnum(parent);
422 b = blkid_new_probe_from_filename(node);
427 log_error("Failed allocate prober: %m");
431 blkid_probe_enable_superblocks(b, 1);
432 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
433 blkid_probe_enable_partitions(b, 1);
434 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
437 r = blkid_do_safeprobe(b);
438 if (r == -2 || r == 1) /* no result or uncertain */
443 log_error("Failed to probe %s: %m", node);
448 r = blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
452 log_error("Failed to determine partition table type of %s: %m", node);
456 /* We only do this all for GPT... */
457 if (!streq_ptr(pttype, "gpt"))
461 pl = blkid_probe_get_partitions(b);
466 log_error("Failed to list partitions of %s: %m", node);
470 e = udev_enumerate_new(udev);
474 r = udev_enumerate_add_match_parent(e, parent);
478 r = udev_enumerate_add_match_subsystem(e, "block");
482 r = udev_enumerate_scan_devices(e);
484 log_error("Failed to enumerate partitions on %s: %s", node, strerror(-r));
488 first = udev_enumerate_get_list_entry(e);
489 udev_list_entry_foreach(item, first) {
490 _cleanup_udev_device_unref_ struct udev_device *q;
491 const char *stype, *subnode;
496 unsigned long long flags;
498 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
502 qn = udev_device_get_devnum(q);
512 subnode = udev_device_get_devnode(q);
516 pp = blkid_partlist_devno_to_partition(pl, qn);
520 flags = blkid_partition_get_flags(pp);
522 /* Ignore partitions that are not marked for automatic
523 * mounting on discovery */
524 if (flags & GPT_FLAG_NO_AUTO)
527 nr = blkid_partition_get_partno(pp);
531 stype = blkid_partition_get_type_string(pp);
535 if (sd_id128_from_string(stype, &type_id) < 0)
538 if (sd_id128_equal(type_id, GPT_SWAP)) {
540 if (flags & GPT_FLAG_READ_ONLY) {
541 log_debug("%s marked as read-only swap partition, which is bogus, ignoring.", subnode);
545 k = add_swap(subnode);
549 } else if (sd_id128_equal(type_id, GPT_HOME)) {
551 /* We only care for the first /home partition */
552 if (home && nr >= home_nr)
556 home_rw = !(flags & GPT_FLAG_READ_ONLY),
559 home = strdup(subnode);
563 } else if (sd_id128_equal(type_id, GPT_SRV)) {
565 /* We only care for the first /srv partition */
566 if (srv && nr >= srv_nr)
570 srv_rw = !(flags & GPT_FLAG_READ_ONLY),
580 k = probe_and_add_mount("home", home, "/home", home_rw, "Home Partition", SPECIAL_LOCAL_FS_TARGET);
586 k = probe_and_add_mount("srv", srv, "/srv", srv_rw, "Server Data Partition", SPECIAL_LOCAL_FS_TARGET);
594 static int get_btrfs_block_device(const char *path, dev_t *dev) {
595 struct btrfs_ioctl_fs_info_args fsi = {};
596 _cleanup_close_ int fd = -1;
602 fd = open(path, O_DIRECTORY|O_CLOEXEC);
606 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0)
609 /* We won't do this for btrfs RAID */
610 if (fsi.num_devices != 1)
613 for (id = 1; id <= fsi.max_id; id++) {
614 struct btrfs_ioctl_dev_info_args di = {
619 if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) {
626 if (stat((char*) di.path, &st) < 0)
629 if (!S_ISBLK(st.st_mode))
632 if (major(st.st_rdev) == 0)
642 static int get_block_device(const char *path, dev_t *dev) {
649 if (lstat(path, &st))
652 if (major(st.st_dev) != 0) {
657 if (statfs(path, &sfs) < 0)
660 if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
661 return get_btrfs_block_device(path, dev);
666 static int parse_proc_cmdline_item(const char *key, const char *value) {
671 if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto") && value) {
673 r = parse_boolean(value);
675 log_warning("Failed to parse gpt-auto switch %s. Ignoring.", value);
679 } else if (streq(key, "root") && value) {
681 /* Disable root disk logic if there's a root= value
682 * specified (unless it happens to be "gpt-auto") */
684 arg_root_enabled = streq(value, "gpt-auto");
686 } else if (streq(key, "rw") && !value)
688 else if (streq(key, "ro") && !value)
690 else if (startswith(key, "systemd.gpt-auto.") || startswith(key, "rd.systemd.gpt-auto."))
691 log_warning("Unknown kernel switch %s. Ignoring.", key);
696 static int add_root_mount(void) {
701 if (!is_efi_boot()) {
702 log_debug("Not a EFI boot, not creating root mount.");
706 r = efi_loader_get_device_part_uuid(NULL);
708 log_debug("EFI loader partition unknown, exiting.");
711 log_error("Failed to read ESP partition UUID: %s", strerror(-r));
715 /* OK, we have an ESP partition, this is fantastic, so let's
716 * wait for a root device to show up. A udev rule will create
717 * the link for us under the right name. */
721 "/dev/gpt-auto-root",
722 in_initrd() ? "/sysroot" : "/",
726 in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
732 static int add_mounts(void) {
736 r = get_block_device("/", &devno);
738 log_error("Failed to determine block device of root file system: %s", strerror(-r));
741 log_debug("Root file system not on a (single) block device.");
745 return enumerate_partitions(devno);
748 int main(int argc, char *argv[]) {
751 if (argc > 1 && argc != 4) {
752 log_error("This program takes three or no arguments.");
759 log_set_target(LOG_TARGET_SAFE);
760 log_parse_environment();
765 if (detect_container(NULL) > 0) {
766 log_debug("In a container, exiting.");
770 if (parse_proc_cmdline(parse_proc_cmdline_item) < 0)
774 log_debug("Disabled, exiting.");
778 if (arg_root_enabled)
779 r = add_root_mount();
789 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;