From 5e1f646c67c954fe7d55a747e3ffdd07e20cb46a Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Thu, 15 Sep 2016 01:52:25 -0400 Subject: ./tools/notsd-move --- src/systemd-gpt-auto-generator/Makefile | 42 + .../gpt-auto-generator.c | 1040 ++++++++++++++++++++ .../systemd-gpt-auto-generator.xml | 186 ++++ 3 files changed, 1268 insertions(+) create mode 100644 src/systemd-gpt-auto-generator/Makefile create mode 100644 src/systemd-gpt-auto-generator/gpt-auto-generator.c create mode 100644 src/systemd-gpt-auto-generator/systemd-gpt-auto-generator.xml (limited to 'src/systemd-gpt-auto-generator') diff --git a/src/systemd-gpt-auto-generator/Makefile b/src/systemd-gpt-auto-generator/Makefile new file mode 100644 index 0000000000..ce6b56f998 --- /dev/null +++ b/src/systemd-gpt-auto-generator/Makefile @@ -0,0 +1,42 @@ +# -*- Mode: makefile; indent-tabs-mode: t -*- +# +# This file is part of systemd. +# +# Copyright 2010-2012 Lennart Poettering +# Copyright 2010-2012 Kay Sievers +# Copyright 2013 Zbigniew Jędrzejewski-Szmek +# Copyright 2013 David Strauss +# Copyright 2016 Luke Shumaker +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . +include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk +include $(topsrcdir)/build-aux/Makefile.head.mk + +ifneq ($(HAVE_BLKID),) +systemgenerator_PROGRAMS += \ + systemd-gpt-auto-generator + +systemd_gpt_auto_generator_SOURCES = \ + src/gpt-auto-generator/gpt-auto-generator.c \ + src/basic/blkid-util.h + +systemd_gpt_auto_generator_LDADD = \ + libsystemd-shared.la \ + $(BLKID_LIBS) + +systemd_gpt_auto_generator_CFLAGS = \ + $(BLKID_CFLAGS) +endif # HAVE_BLKID + +include $(topsrcdir)/build-aux/Makefile.tail.mk diff --git a/src/systemd-gpt-auto-generator/gpt-auto-generator.c b/src/systemd-gpt-auto-generator/gpt-auto-generator.c new file mode 100644 index 0000000000..fe598da6e1 --- /dev/null +++ b/src/systemd-gpt-auto-generator/gpt-auto-generator.c @@ -0,0 +1,1040 @@ +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include +#include + +#include "basic/alloc-util.h" +#include "basic/blkid-util.h" +#include "basic/btrfs-util.h" +#include "basic/dirent-util.h" +#include "basic/fd-util.h" +#include "basic/fileio.h" +#include "basic/missing.h" +#include "basic/mkdir.h" +#include "basic/mount-util.h" +#include "basic/parse-util.h" +#include "basic/path-util.h" +#include "basic/proc-cmdline.h" +#include "basic/special.h" +#include "basic/stat-util.h" +#include "basic/string-util.h" +#include "basic/unit-name.h" +#include "basic/util.h" +#include "basic/virt.h" +#include "shared/efivars.h" +#include "shared/fstab-util.h" +#include "shared/generator.h" +#include "shared/gpt.h" +#include "shared/udev-util.h" + +static const char *arg_dest = "/tmp"; +static bool arg_enabled = true; +static bool arg_root_enabled = true; +static bool arg_root_rw = false; + +static int add_cryptsetup(const char *id, const char *what, bool rw, char **device) { + _cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL; + _cleanup_fclose_ FILE *f = NULL; + char *from, *ret; + int r; + + assert(id); + assert(what); + assert(device); + + r = unit_name_from_path(what, ".device", &d); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + e = unit_name_escape(id); + if (!e) + return log_oom(); + + r = unit_name_build("systemd-cryptsetup", e, ".service", &n); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + p = strjoin(arg_dest, "/", n, NULL); + if (!p) + return log_oom(); + + f = fopen(p, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", p); + + fprintf(f, + "# Automatically generated by systemd-gpt-auto-generator\n\n" + "[Unit]\n" + "Description=Cryptography Setup for %%I\n" + "Documentation=man:systemd-gpt-auto-generator(8) man:systemd-cryptsetup@.service(8)\n" + "DefaultDependencies=no\n" + "Conflicts=umount.target\n" + "BindsTo=dev-mapper-%%i.device %s\n" + "Before=umount.target cryptsetup.target\n" + "After=%s\n" + "IgnoreOnIsolate=true\n" + "[Service]\n" + "Type=oneshot\n" + "RemainAfterExit=yes\n" + "TimeoutSec=0\n" /* the binary handles timeouts anyway */ + "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '' '%s'\n" + "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n", + d, d, + id, what, rw ? "" : "read-only", + id); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write file %s: %m", p); + + from = strjoina("../", n); + + to = strjoin(arg_dest, "/", d, ".wants/", n, NULL); + if (!to) + return log_oom(); + + mkdir_parents_label(to, 0755); + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + + free(to); + to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL); + if (!to) + return log_oom(); + + mkdir_parents_label(to, 0755); + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + + free(to); + to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL); + if (!to) + return log_oom(); + + mkdir_parents_label(to, 0755); + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + + free(p); + p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL); + if (!p) + return log_oom(); + + mkdir_parents_label(p, 0755); + r = write_string_file(p, + "# Automatically generated by systemd-gpt-auto-generator\n\n" + "[Unit]\n" + "JobTimeoutSec=0\n", + WRITE_STRING_FILE_CREATE); /* the binary handles timeouts anyway */ + if (r < 0) + return log_error_errno(r, "Failed to write device drop-in: %m"); + + ret = strappend("/dev/mapper/", id); + if (!ret) + return log_oom(); + + *device = ret; + return 0; +} + +static int add_mount( + const char *id, + const char *what, + const char *where, + const char *fstype, + bool rw, + const char *options, + const char *description, + const char *post) { + + _cleanup_free_ char *unit = NULL, *lnk = NULL, *crypto_what = NULL, *p = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(id); + assert(what); + assert(where); + assert(description); + + log_debug("Adding %s: %s %s", where, what, strna(fstype)); + + if (streq_ptr(fstype, "crypto_LUKS")) { + + r = add_cryptsetup(id, what, rw, &crypto_what); + if (r < 0) + return r; + + what = crypto_what; + fstype = NULL; + } + + r = unit_name_from_path(where, ".mount", &unit); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + p = strjoin(arg_dest, "/", unit, NULL); + if (!p) + return log_oom(); + + f = fopen(p, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", unit); + + fprintf(f, + "# Automatically generated by systemd-gpt-auto-generator\n\n" + "[Unit]\n" + "Description=%s\n" + "Documentation=man:systemd-gpt-auto-generator(8)\n", + description); + + if (post) + fprintf(f, "Before=%s\n", post); + + r = generator_write_fsck_deps(f, arg_dest, what, where, fstype); + if (r < 0) + return r; + + fprintf(f, + "\n" + "[Mount]\n" + "What=%s\n" + "Where=%s\n", + what, where); + + if (fstype) + fprintf(f, "Type=%s\n", fstype); + + if (options) + fprintf(f, "Options=%s,%s\n", options, rw ? "rw" : "ro"); + else + fprintf(f, "Options=%s\n", rw ? "rw" : "ro"); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", p); + + if (post) { + lnk = strjoin(arg_dest, "/", post, ".requires/", unit, NULL); + if (!lnk) + return log_oom(); + + mkdir_parents_label(lnk, 0755); + if (symlink(p, lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); + } + + return 0; +} + +static bool path_is_busy(const char *where) { + int r; + + /* already a mountpoint; generators run during reload */ + r = path_is_mount_point(where, AT_SYMLINK_FOLLOW); + if (r > 0) + return false; + + /* the directory might not exist on a stateless system */ + if (r == -ENOENT) + return false; + + if (r < 0) + return true; + + /* not a mountpoint but it contains files */ + if (dir_is_empty(where) <= 0) + return true; + + return false; +} + +static int probe_and_add_mount( + const char *id, + const char *what, + const char *where, + bool rw, + const char *description, + const char *post) { + + _cleanup_blkid_free_probe_ blkid_probe b = NULL; + const char *fstype = NULL; + int r; + + assert(id); + assert(what); + assert(where); + assert(description); + + if (path_is_busy(where)) { + log_debug("%s already populated, ignoring.", where); + return 0; + } + + /* Let's check the partition type here, so that we know + * whether to do LUKS magic. */ + + errno = 0; + b = blkid_new_probe_from_filename(what); + if (!b) { + if (errno == 0) + return log_oom(); + return log_error_errno(errno, "Failed to allocate prober: %m"); + } + + blkid_probe_enable_superblocks(b, 1); + blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2 || r == 1) /* no result or uncertain */ + return 0; + else if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what); + + /* add_mount is OK with fstype being NULL. */ + (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); + + return add_mount( + id, + what, + where, + fstype, + rw, + NULL, + description, + post); +} + +static int add_swap(const char *path) { + _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(path); + + log_debug("Adding swap: %s", path); + + r = unit_name_from_path(path, ".swap", &name); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + unit = strjoin(arg_dest, "/", name, NULL); + if (!unit) + return log_oom(); + + f = fopen(unit, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", unit); + + fprintf(f, + "# Automatically generated by systemd-gpt-auto-generator\n\n" + "[Unit]\n" + "Description=Swap Partition\n" + "Documentation=man:systemd-gpt-auto-generator(8)\n\n" + "[Swap]\n" + "What=%s\n", + path); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", unit); + + lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL); + if (!lnk) + return log_oom(); + + mkdir_parents_label(lnk, 0755); + if (symlink(unit, lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); + + return 0; +} + +#ifdef ENABLE_EFI +static int add_automount( + const char *id, + const char *what, + const char *where, + const char *fstype, + bool rw, + const char *options, + const char *description, + usec_t timeout) { + + _cleanup_free_ char *unit = NULL, *lnk = NULL; + _cleanup_free_ char *opt, *p = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(id); + assert(where); + assert(description); + + if (options) + opt = strjoin(options, ",noauto", NULL); + else + opt = strdup("noauto"); + if (!opt) + return log_oom(); + + r = add_mount(id, + what, + where, + fstype, + rw, + opt, + description, + NULL); + if (r < 0) + return r; + + r = unit_name_from_path(where, ".automount", &unit); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + + p = strjoin(arg_dest, "/", unit, NULL); + if (!p) + return log_oom(); + + f = fopen(p, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", unit); + + fprintf(f, + "# Automatically generated by systemd-gpt-auto-generator\n\n" + "[Unit]\n" + "Description=%s\n" + "Documentation=man:systemd-gpt-auto-generator(8)\n" + "[Automount]\n" + "Where=%s\n" + "TimeoutIdleSec=%lld\n", + description, + where, + (unsigned long long)timeout / USEC_PER_SEC); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", p); + + lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/", unit, NULL); + if (!lnk) + return log_oom(); + mkdir_parents_label(lnk, 0755); + + if (symlink(p, lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); + + return 0; +} + +static int add_boot(const char *what) { + _cleanup_blkid_free_probe_ blkid_probe b = NULL; + const char *fstype = NULL, *uuid = NULL; + sd_id128_t id, type_id; + int r; + + assert(what); + + if (!is_efi_boot()) { + log_debug("Not an EFI boot, ignoring /boot."); + return 0; + } + + if (in_initrd()) { + log_debug("In initrd, ignoring /boot."); + return 0; + } + + if (detect_container() > 0) { + log_debug("In a container, ignoring /boot."); + return 0; + } + + /* We create an .automount which is not overridden by the .mount from the fstab generator. */ + if (fstab_is_mount_point("/boot")) { + log_debug("/boot specified in fstab, ignoring."); + return 0; + } + + if (path_is_busy("/boot")) { + log_debug("/boot already populated, ignoring."); + return 0; + } + + r = efi_loader_get_device_part_uuid(&id); + if (r == -ENOENT) { + log_debug("EFI loader partition unknown."); + return 0; + } + + if (r < 0) + return log_error_errno(r, "Failed to read ESP partition UUID: %m"); + + errno = 0; + b = blkid_new_probe_from_filename(what); + if (!b) { + if (errno == 0) + return log_oom(); + return log_error_errno(errno, "Failed to allocate prober: %m"); + } + + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2 || r == 1) /* no result or uncertain */ + return 0; + else if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what); + + (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); + if (!streq_ptr(fstype, "vfat")) { + log_debug("Partition for /boot is not a FAT filesystem, ignoring."); + return 0; + } + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid, NULL); + if (r != 0) { + log_debug_errno(errno, "Partition for /boot does not have a UUID, ignoring."); + return 0; + } + + if (sd_id128_from_string(uuid, &type_id) < 0) { + log_debug("Partition for /boot does not have a valid UUID, ignoring."); + return 0; + } + + if (!sd_id128_equal(type_id, id)) { + log_debug("Partition for /boot does not appear to be the partition we are booted from."); + return 0; + } + + r = add_automount("boot", + what, + "/boot", + "vfat", + true, + "umask=0077", + "EFI System Partition Automount", + 120 * USEC_PER_SEC); + + return r; +} +#else +static int add_boot(const char *what) { + return 0; +} +#endif + +static int enumerate_partitions(dev_t devnum) { + + _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + _cleanup_blkid_free_probe_ blkid_probe b = NULL; + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_free_ char *boot = NULL, *home = NULL, *srv = NULL; + struct udev_list_entry *first, *item; + struct udev_device *parent = NULL; + const char *name, *node, *pttype, *devtype; + int boot_nr = -1, home_nr = -1, srv_nr = -1; + bool home_rw = true, srv_rw = true; + blkid_partlist pl; + int r, k; + dev_t pn; + + udev = udev_new(); + if (!udev) + return log_oom(); + + d = udev_device_new_from_devnum(udev, 'b', devnum); + if (!d) + return log_oom(); + + name = udev_device_get_devnode(d); + if (!name) + name = udev_device_get_syspath(d); + if (!name) { + log_debug("Device %u:%u does not have a name, ignoring.", + major(devnum), minor(devnum)); + return 0; + } + + parent = udev_device_get_parent(d); + if (!parent) { + log_debug("%s: not a partitioned device, ignoring.", name); + return 0; + } + + /* Does it have a devtype? */ + devtype = udev_device_get_devtype(parent); + if (!devtype) { + log_debug("%s: parent doesn't have a device type, ignoring.", name); + return 0; + } + + /* Is this a disk or a partition? We only care for disks... */ + if (!streq(devtype, "disk")) { + log_debug("%s: parent isn't a raw disk, ignoring.", name); + return 0; + } + + /* Does it have a device node? */ + node = udev_device_get_devnode(parent); + if (!node) { + log_debug("%s: parent device does not have device node, ignoring.", name); + return 0; + } + + log_debug("%s: root device %s.", name, node); + + pn = udev_device_get_devnum(parent); + if (major(pn) == 0) + return 0; + + errno = 0; + b = blkid_new_probe_from_filename(node); + if (!b) { + if (errno == 0) + return log_oom(); + + return log_error_errno(errno, "%s: failed to allocate prober: %m", node); + } + + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == 1) + return 0; /* no results */ + else if (r == -2) { + log_warning("%s: probe gave ambiguous results, ignoring.", node); + return 0; + } else if (r != 0) + return log_error_errno(errno ?: EIO, "%s: failed to probe: %m", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL); + if (r != 0) { + if (errno == 0) + return 0; /* No partition table found. */ + + return log_error_errno(errno, "%s: failed to determine partition table type: %m", node); + } + + /* We only do this all for GPT... */ + if (!streq_ptr(pttype, "gpt")) { + log_debug("%s: not a GPT partition table, ignoring.", node); + return 0; + } + + errno = 0; + pl = blkid_probe_get_partitions(b); + if (!pl) { + if (errno == 0) + return log_oom(); + + return log_error_errno(errno, "%s: failed to list partitions: %m", node); + } + + e = udev_enumerate_new(udev); + if (!e) + return log_oom(); + + r = udev_enumerate_add_match_parent(e, parent); + if (r < 0) + return log_oom(); + + r = udev_enumerate_add_match_subsystem(e, "block"); + if (r < 0) + return log_oom(); + + r = udev_enumerate_scan_devices(e); + if (r < 0) + return log_error_errno(r, "%s: failed to enumerate partitions: %m", node); + + first = udev_enumerate_get_list_entry(e); + udev_list_entry_foreach(item, first) { + _cleanup_udev_device_unref_ struct udev_device *q; + unsigned long long flags; + const char *stype, *subnode; + sd_id128_t type_id; + blkid_partition pp; + dev_t qn; + int nr; + + q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); + if (!q) + continue; + + qn = udev_device_get_devnum(q); + if (major(qn) == 0) + continue; + + if (qn == devnum) + continue; + + if (qn == pn) + continue; + + subnode = udev_device_get_devnode(q); + if (!subnode) + continue; + + pp = blkid_partlist_devno_to_partition(pl, qn); + if (!pp) + continue; + + nr = blkid_partition_get_partno(pp); + if (nr < 0) + continue; + + stype = blkid_partition_get_type_string(pp); + if (!stype) + continue; + + if (sd_id128_from_string(stype, &type_id) < 0) + continue; + + flags = blkid_partition_get_flags(pp); + + if (sd_id128_equal(type_id, GPT_SWAP)) { + + if (flags & GPT_FLAG_NO_AUTO) + continue; + + if (flags & GPT_FLAG_READ_ONLY) { + log_debug("%s marked as read-only swap partition, which is bogus. Ignoring.", subnode); + continue; + } + + k = add_swap(subnode); + if (k < 0) + r = k; + + } else if (sd_id128_equal(type_id, GPT_ESP)) { + + /* We only care for the first /boot partition */ + if (boot && nr >= boot_nr) + continue; + + /* Note that we do not honour the "no-auto" + * flag for the ESP, as it is often unset, to + * hide it from Windows. */ + + boot_nr = nr; + + r = free_and_strdup(&boot, subnode); + if (r < 0) + return log_oom(); + + } else if (sd_id128_equal(type_id, GPT_HOME)) { + + if (flags & GPT_FLAG_NO_AUTO) + continue; + + /* We only care for the first /home partition */ + if (home && nr >= home_nr) + continue; + + home_nr = nr; + home_rw = !(flags & GPT_FLAG_READ_ONLY), + + r = free_and_strdup(&home, subnode); + if (r < 0) + return log_oom(); + + } else if (sd_id128_equal(type_id, GPT_SRV)) { + + if (flags & GPT_FLAG_NO_AUTO) + continue; + + /* We only care for the first /srv partition */ + if (srv && nr >= srv_nr) + continue; + + srv_nr = nr; + srv_rw = !(flags & GPT_FLAG_READ_ONLY), + + r = free_and_strdup(&srv, subnode); + if (r < 0) + return log_oom(); + } + } + + if (boot) { + k = add_boot(boot); + if (k < 0) + r = k; + } + + if (home) { + k = probe_and_add_mount("home", home, "/home", home_rw, "Home Partition", SPECIAL_LOCAL_FS_TARGET); + if (k < 0) + r = k; + } + + if (srv) { + k = probe_and_add_mount("srv", srv, "/srv", srv_rw, "Server Data Partition", SPECIAL_LOCAL_FS_TARGET); + if (k < 0) + r = k; + } + + return r; +} + +static int get_block_device(const char *path, dev_t *dev) { + struct stat st; + struct statfs sfs; + + assert(path); + assert(dev); + + /* Get's the block device directly backing a file system. If + * the block device is encrypted, returns the device mapper + * block device. */ + + if (lstat(path, &st)) + return -errno; + + if (major(st.st_dev) != 0) { + *dev = st.st_dev; + return 1; + } + + if (statfs(path, &sfs) < 0) + return -errno; + + if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) + return btrfs_get_block_device(path, dev); + + return 0; +} + +static int get_block_device_harder(const char *path, dev_t *dev) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_free_ char *p = NULL, *t = NULL; + struct dirent *de, *found = NULL; + const char *q; + unsigned maj, min; + dev_t dt; + int r; + + assert(path); + assert(dev); + + /* Gets the backing block device for a file system, and + * handles LUKS encrypted file systems, looking for its + * immediate parent, if there is one. */ + + r = get_block_device(path, &dt); + if (r <= 0) + return r; + + if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0) + return -ENOMEM; + + d = opendir(p); + if (!d) { + if (errno == ENOENT) + goto fallback; + + return -errno; + } + + FOREACH_DIRENT_ALL(de, d, return -errno) { + + if (STR_IN_SET(de->d_name, ".", "..")) + continue; + + if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) + continue; + + if (found) /* Don't try to support multiple backing block devices */ + goto fallback; + + found = de; + } + + if (!found) + goto fallback; + + q = strjoina(p, "/", found->d_name, "/dev"); + + r = read_one_line_file(q, &t); + if (r == -ENOENT) + goto fallback; + if (r < 0) + return r; + + if (sscanf(t, "%u:%u", &maj, &min) != 2) + return -EINVAL; + + if (maj == 0) + goto fallback; + + *dev = makedev(maj, min); + return 1; + +fallback: + *dev = dt; + return 1; +} + +static int parse_proc_cmdline_item(const char *key, const char *value) { + int r; + + assert(key); + + if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto") && value) { + + r = parse_boolean(value); + if (r < 0) + log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value); + else + arg_enabled = r; + + } else if (streq(key, "root") && value) { + + /* Disable root disk logic if there's a root= value + * specified (unless it happens to be "gpt-auto") */ + + arg_root_enabled = streq(value, "gpt-auto"); + + } else if (streq(key, "rw") && !value) + arg_root_rw = true; + else if (streq(key, "ro") && !value) + arg_root_rw = false; + + return 0; +} + +static int add_root_mount(void) { + +#ifdef ENABLE_EFI + int r; + + if (!is_efi_boot()) { + log_debug("Not a EFI boot, not creating root mount."); + return 0; + } + + r = efi_loader_get_device_part_uuid(NULL); + if (r == -ENOENT) { + log_debug("EFI loader partition unknown, exiting."); + return 0; + } else if (r < 0) + return log_error_errno(r, "Failed to read ESP partition UUID: %m"); + + /* OK, we have an ESP partition, this is fantastic, so let's + * wait for a root device to show up. A udev rule will create + * the link for us under the right name. */ + + if (in_initrd()) { + r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root"); + if (r < 0) + return 0; + } + + return add_mount( + "root", + "/dev/gpt-auto-root", + in_initrd() ? "/sysroot" : "/", + NULL, + arg_root_rw, + NULL, + "Root Partition", + in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET); +#else + return 0; +#endif +} + +static int add_mounts(void) { + dev_t devno; + int r; + + r = get_block_device_harder("/", &devno); + if (r < 0) + return log_error_errno(r, "Failed to determine block device of root file system: %m"); + else if (r == 0) { + r = get_block_device_harder("/usr", &devno); + if (r < 0) + return log_error_errno(r, "Failed to determine block device of /usr file system: %m"); + else if (r == 0) { + log_debug("Neither root nor /usr file system are on a (single) block device."); + return 0; + } + } + + return enumerate_partitions(devno); +} + +int main(int argc, char *argv[]) { + int r = 0; + + if (argc > 1 && argc != 4) { + log_error("This program takes three or no arguments."); + return EXIT_FAILURE; + } + + if (argc > 1) + arg_dest = argv[3]; + + log_set_target(LOG_TARGET_SAFE); + log_parse_environment(); + log_open(); + + umask(0022); + + if (detect_container() > 0) { + log_debug("In a container, exiting."); + return EXIT_SUCCESS; + } + + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + + if (!arg_enabled) { + log_debug("Disabled, exiting."); + return EXIT_SUCCESS; + } + + if (arg_root_enabled) + r = add_root_mount(); + + if (!in_initrd()) { + int k; + + k = add_mounts(); + if (k < 0) + r = k; + } + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/systemd-gpt-auto-generator/systemd-gpt-auto-generator.xml b/src/systemd-gpt-auto-generator/systemd-gpt-auto-generator.xml new file mode 100644 index 0000000000..e890c4dce2 --- /dev/null +++ b/src/systemd-gpt-auto-generator/systemd-gpt-auto-generator.xml @@ -0,0 +1,186 @@ + + + + + + + + systemd-gpt-auto-generator + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd-gpt-auto-generator + 8 + + + + systemd-gpt-auto-generator + Generator for automatically discovering + and mounting root, /home and + /srv partitions, as well as + discovering and enabling swap partitions, based on GPT + partition type GUIDs. + + + + /usr/lib/systemd/system-generators/systemd-gpt-auto-generator + + + + Description + + systemd-gpt-auto-generator is a unit + generator that automatically discovers root, + /home, /srv and swap + partitions and creates mount and swap units for them, based on the + partition type GUIDs of GUID partition tables (GPT). It implements + the Discoverable + Partitions Specification. Note that this generator has no + effect on non-GPT systems, or where the directories under the + mount points are already non-empty. Also, on systems where the + units are explicitly configured (for example, listed in + fstab5), + the units this generator creates are overridden, but additional + automatic dependencies might be created. + + This generator will only look for root partitions on the + same physical disk the EFI System Partition (ESP) is located on. + It will only look for the other partitions on the same physical + disk the root file system is located on. These partitions will not + be searched on systems where the root file system is distributed + on multiple disks, for example via btrfs RAID. + + systemd-gpt-auto-generator is useful + for centralizing file system configuration in the partition table + and making manual configuration in /etc/fstab + or suchlike unnecessary. + + This generator looks for the partitions based on their + partition type GUID. The following partition type GUIDs are + identified: + + + Partition Type GUIDs + + + + + + + Partition Type GUID + Name + Explanation + + + + + 44479540-f297-41b2-9af7-d131d5f0458a + Root Partition (x86) + On 32-bit x86 systems, the first x86 root partition on the disk the EFI ESP is located on is mounted to the root directory /. + + + 4f68bce3-e8cd-4db1-96e7-fbcaf984b709 + Root Partition (x86-64) + On 64-bit x86 systems, the first x86-64 root partition on the disk the EFI ESP is located on is mounted to the root directory /. + + + 69dad710-2ce4-4e3c-b16c-21a1d49abed3 + Root Partition (32-bit ARM) + On 32-bit ARM systems, the first ARM root partition on the disk the EFI ESP is located on is mounted to the root directory /. + + + b921b045-1df0-41c3-af44-4c6f280d3fae + Root Partition (64-bit ARM) + On 64-bit ARM systems, the first ARM root partition on the disk the EFI ESP is located on is mounted to the root directory /. + + + 933ac7e1-2eb4-4f13-b844-0e14e2aef915 + Home Partition + The first home partition on the disk the root partition is located on is mounted to /home. + + + 3b8f8425-20e0-4f3b-907f-1a25a76f98e8 + Server Data Partition + The first server data partition on the disk the root partition is located on is mounted to /srv. + + + 0657fd6d-a4ab-43c4-84e5-0933c84b4f4f + Swap + All swap partitions located on the disk the root partition is located on are enabled. + + + +
+ + The /home and /srv + partitions may be encrypted in LUKS format. In this case, a device + mapper device is set up under the names + /dev/mapper/home and + /dev/mapper/srv. Note that this might create + conflicts if the same partition is listed in + /etc/crypttab with a different device mapper + device name. + + Mount and automount units for the EFI System Partition (ESP), + mounting it to /boot, are generated on EFI + systems where the boot loader communicates the used ESP to the operating + system. Since this generator creates an automount unit, the mount will + only be activated on-demand, when accessed. On systems where + /boot is an explicitly configured mount + (for example, listed in + fstab5) + or where the /boot mount point is non-empty, no + mount units are generated. + + When using this generator in conjunction with btrfs file + systems, make sure to set the correct default subvolumes on them, + using btrfs subvolume set-default. + + systemd-gpt-auto-generator implements + systemd.generator7. +
+ + + See Also + + systemd1, + systemd.mount5, + systemd.swap5, + systemd-fstab-generator8, + systemd-cryptsetup@.service8, + cryptsetup8, + fstab5, + btrfs8 + + + +
-- cgit v1.2.3-54-g00ecf