From: Lennart Poettering Date: Fri, 7 Mar 2014 03:31:26 +0000 (+0100) Subject: gpt-auto-generator: automatically find the root disk of the system X-Git-Tag: v211~79 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=73b80ec2d999c45ce13f3e034704249d80829f7e gpt-auto-generator: automatically find the root disk of the system When run in an initrd and no root= argument is set (or is set to root=gpt-auto) we will automatically look for the root partition on the same disk the EFI ESP is located on. Since we look for swap, /home and /srv on the disk the root partition is located on, we hence have a fully discoverable chain: Firmware discovers the EFI ESP partition → the initrd discovers the root partition → the host OS discovers swap, /home, and /srv. Note that this requires an EFI boot loader that sets the LoaderDevicePartUUID EFI variable, such as Gummiboot. --- diff --git a/man/systemd-gpt-auto-generator.xml b/man/systemd-gpt-auto-generator.xml index 75313994d..a753fb0be 100644 --- a/man/systemd-gpt-auto-generator.xml +++ b/man/systemd-gpt-auto-generator.xml @@ -43,7 +43,8 @@ systemd-gpt-auto-generator Generator for automatically discovering - and mounting /home and /srv, as well as + and mounting root, /home and + /srv partitions, as well as discovering and enabling swap partitions, based on GPT partition type GUIDs. @@ -56,7 +57,7 @@ Description systemd-gpt-auto-generator - is a unit generator that automatically discovers + is a unit generator that automatically discovers root, /home, /srv and swap partitions and creates mount and swap units for them, based on the the partition type GUIDs of @@ -67,11 +68,14 @@ fstab5), or where the mount points are non-empty. - This generator will only look for partitions on - the same physical disk the root file system is stored - on. This generator has no effect on systems where the - root file system is distributed on multiple disks, for - example via btrfs RAID. + 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 search 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 @@ -87,30 +91,40 @@ Partition Type GUIDs - + Partition Type GUID - Location + Name Explanation - 0657fd6d-a4ab-43c4-84e50933c84b4f4f - Swap - All swap partitions are enabled. + 44479540-f297-41b2-9af7d131d5f0458a + Root Partition (x86) + On 32bit 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-96e7fbcaf984b709 + Root Partition (x86-64) + On 64bit x86 systems the first x86-64 root partition on the disk the EFI ESP is located on is mounted to the root directory /. 933ac7e1-2eb4-4f13-b8440e14e2aef915 - /home - The first home partition on the disk is mounted to /home. + Home Partition + The first home partition on the disk the root partition is located on is mounted to /home. 3b8f8425-20e0-4f3b-907f1a25a76f98e8 - /srv - The first server data partition on the disk is mounted to /srv. + Server Data Partition + The first server data partition on the disk the root partition is located on is mounted to /srv. + + + 0657fd6d-a4ab-43c4-84e50933c84b4f4f + Swap + All swap partitions located on the disk the root partition is located on are enabled. @@ -128,8 +142,13 @@ Also note that systemd-efi-boot-generator8 - will mount the EFI System Partition to - /boot is not otherwise mounted. + will mount the EFI System Partition (ESP) to + /boot if not otherwise mounted. + + 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 the systemd-efi-boot-generator8, systemd-cryptsetup@.service8, cryptsetup8, - fstab5 + fstab5, + btrfs8 diff --git a/src/core/main.c b/src/core/main.c index 18d0927b7..6ebfe6418 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -374,6 +374,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { log_warning("Environment variable name '%s' is not valid. Ignoring.", value); } else if (!streq(key, "systemd.restore_state") && + !streq(key, "systemd.gpt_auto") && (startswith(key, "systemd.") || startswith(key, "rd.systemd."))) { const char *c; diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index 3b7bc212d..e4ce48ca9 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -43,8 +43,12 @@ #include "generator.h" #include "gpt.h" #include "fileio.h" +#include "efivars.h" static const char *arg_dest = "/tmp"; +static bool arg_enabled = true; +static bool arg_root_enabled = true; +static bool arg_root_rw = false; DEFINE_TRIVIAL_CLEANUP_FUNC(blkid_probe, blkid_free_probe); #define _cleanup_blkid_freep_probe_ _cleanup_(blkid_free_probep) @@ -287,7 +291,15 @@ static int add_cryptsetup(const char *id, const char *what, char **device) { return 0; } -static int add_mount(const char *id, const char *what, const char *where, const char *fstype, const char *description) { +static int add_mount( + const char *id, + const char *what, + const char *where, + const char *fstype, + 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; @@ -295,7 +307,6 @@ static int add_mount(const char *id, const char *what, const char *where, const assert(id); assert(what); assert(where); - assert(fstype); assert(description); if (path_is_mount_point(where, true) <= 0 && @@ -304,9 +315,9 @@ static int add_mount(const char *id, const char *what, const char *where, const return 0; } - log_debug("Adding %s: %s %s", where, what, fstype); + log_debug("Adding %s: %s %s", where, what, strna(fstype)); - if (streq(fstype, "crypto_LUKS")) { + if (streq_ptr(fstype, "crypto_LUKS")) { r = add_cryptsetup(id, what, &crypto_what); if (r < 0) @@ -337,6 +348,9 @@ static int add_mount(const char *id, const char *what, const char *where, const "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; @@ -348,26 +362,28 @@ static int add_mount(const char *id, const char *what, const char *where, const "Where=%s\n", what, where); - if (fstype) { - fprintf(f, - "Type=%s\n", - fstype); - } + if (fstype) + fprintf(f, "Type=%s\n", fstype); + + if (options) + fprintf(f, "Options=%s\n", options); fflush(f); if (ferror(f)) { - log_error("Failed to write unit file %s: %m", unit); + log_error("Failed to write unit file %s: %m", p); return -errno; } - lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".requires/", unit, NULL); - if (!lnk) - return log_oom(); + if (post) { + lnk = strjoin(arg_dest, "/", post, ".requires/", unit, NULL); + if (!lnk) + return log_oom(); - mkdir_parents_label(lnk, 0755); - if (symlink(unit, lnk) < 0) { - log_error("Failed to create symlink %s: %m", lnk); - return -errno; + mkdir_parents_label(lnk, 0755); + if (symlink(p, lnk) < 0) { + log_error("Failed to create symlink %s: %m", lnk); + return -errno; + } } return 0; @@ -380,7 +396,7 @@ static int enumerate_partitions(struct udev *udev, dev_t dev) { unsigned home_nr = (unsigned) -1, srv_nr = (unsigned )-1; struct udev_list_entry *first, *item; struct udev_device *parent = NULL; - int r; + int r, k; e = udev_enumerate_new(udev); if (!e) @@ -408,6 +424,8 @@ static int enumerate_partitions(struct udev *udev, dev_t dev) { return r; } + r = 0; + first = udev_enumerate_get_list_entry(e); udev_list_entry_foreach(item, first) { _cleanup_udev_device_unref_ struct udev_device *q; @@ -430,21 +448,26 @@ static int enumerate_partitions(struct udev *udev, dev_t dev) { if (!node) return log_oom(); - r = verify_gpt_partition(node, &type_id, &nr, &fstype); - if (r < 0) { + k = verify_gpt_partition(node, &type_id, &nr, &fstype); + if (k < 0) { /* skip child devices which are not detected properly */ - if (r == -EBADSLT) + if (k == -EBADSLT) continue; - log_error("Failed to verify GPT partition %s: %s", node, strerror(-r)); - return r; + + log_error("Failed to verify GPT partition %s: %s", node, strerror(-k)); + r = k; + continue; } - if (r == 0) + if (k == 0) continue; - if (sd_id128_equal(type_id, GPT_SWAP)) - add_swap(node, fstype); + if (sd_id128_equal(type_id, GPT_SWAP)) { + + k = add_swap(node, fstype); + if (k < 0) + r = k; - else if (sd_id128_equal(type_id, GPT_HOME)) { + } else if (sd_id128_equal(type_id, GPT_HOME)) { /* We only care for the first /home partition */ if (home && nr >= home_nr) @@ -480,11 +503,17 @@ static int enumerate_partitions(struct udev *udev, dev_t dev) { } } - if (home && home_fstype) - add_mount("home", home, "/home", home_fstype, "Home Partition"); + if (home && home_fstype) { + k = add_mount("home", home, "/home", home_fstype, NULL, "Home Partition", SPECIAL_LOCAL_FS_TARGET); + if (k < 0) + r = k; + } - if (srv && srv_fstype) - add_mount("srv", srv, "/srv", srv_fstype, "Server Data Partition"); + if (srv && srv_fstype) { + k = add_mount("srv", srv, "/srv", srv_fstype, NULL, "Server Data Partition", SPECIAL_LOCAL_FS_TARGET); + if (k < 0) + r = k; + } return r; } @@ -582,55 +611,95 @@ static int devno_to_devnode(struct udev *udev, dev_t devno, char **ret) { return 0; } -int main(int argc, char *argv[]) { - _cleanup_udev_unref_ struct udev *udev = NULL; - _cleanup_free_ char *node = NULL; - dev_t devno; - int r = 0; +static int parse_proc_cmdline_item(const char *key, const char *value) { + int r; - if (argc > 1 && argc != 4) { - log_error("This program takes three or no arguments."); - return EXIT_FAILURE; - } + assert(key); - if (argc > 1) - arg_dest = argv[3]; + if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto") && value) { - log_set_target(LOG_TARGET_SAFE); - log_parse_environment(); - log_open(); + r = parse_boolean(value); + if (r < 0) + log_warning("Failed to parse gpt-auto switch %s. Ignoring.", value); - umask(0022); + arg_enabled = r; - if (in_initrd()) { - log_debug("In initrd, exiting."); - return EXIT_SUCCESS; + } 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; + else if (startswith(key, "systemd.gpt-auto.") || startswith(key, "rd.systemd.gpt-auto.")) + log_warning("Unknown kernel switch %s. Ignoring.", key); + + 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; } - if (detect_container(NULL) > 0) { - log_debug("In a container, exiting."); - return EXIT_SUCCESS; + r = efi_loader_get_device_part_uuid(NULL); + if (r == -ENOENT) { + log_debug("EFI loader partition unknown, exiting."); + return 0; + } else if (r < 0) { + log_error("Failed to read ESP partition UUID: %s", strerror(-r)); + return r; } + /* 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. */ + + return add_mount( + "root", + "/dev/disk/by-id/gpt-auto-root", + in_initrd() ? "/sysroot" : "/", + NULL, + arg_root_rw ? "rw" : "ro", + "Root Partition", + in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET); +#else + return 0; +#endif +} + +static int add_mounts(void) { + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_free_ char *node = NULL; + dev_t devno; + int r; + r = get_block_device("/", &devno); if (r < 0) { log_error("Failed to determine block device of root file system: %s", strerror(-r)); - return EXIT_FAILURE; + return r; } else if (r == 0) { log_debug("Root file system not on a (single) block device."); - return EXIT_SUCCESS; + return 0; } udev = udev_new(); - if (!udev) { - log_oom(); - return EXIT_FAILURE; - } + if (!udev) + return log_oom(); r = devno_to_devnode(udev, devno, &node); if (r < 0) { log_error("Failed to determine block device node from major/minor: %s", strerror(-r)); - return EXIT_FAILURE; + return r; } log_debug("Root device %s.", node); @@ -638,16 +707,56 @@ int main(int argc, char *argv[]) { r = verify_gpt_partition(node, NULL, NULL, NULL); if (r < 0) { log_error("Failed to verify GPT partition %s: %s", node, strerror(-r)); - return EXIT_FAILURE; + return r; } if (r == 0) { log_debug("Not a GPT disk, exiting."); + return 0; + } + + return enumerate_partitions(udev, 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(NULL) > 0) { + log_debug("In a container, exiting."); return EXIT_SUCCESS; } - r = enumerate_partitions(udev, devno); - if (r < 0) + if (parse_proc_cmdline(parse_proc_cmdline_item) < 0) return EXIT_FAILURE; - return EXIT_SUCCESS; + 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/shared/efivars.c b/src/shared/efivars.c index 5ee8f1eb0..f18f5c4b1 100644 --- a/src/shared/efivars.c +++ b/src/shared/efivars.c @@ -440,9 +440,6 @@ int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) { int efi_loader_get_device_part_uuid(sd_id128_t *u) { _cleanup_free_ char *p = NULL; int r, parsed[16]; - unsigned i; - - assert(u); r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderDevicePartUUID", &p); if (r < 0) @@ -455,8 +452,12 @@ int efi_loader_get_device_part_uuid(sd_id128_t *u) { &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16) return -EIO; - for (i = 0; i < ELEMENTSOF(parsed); i++) - u->bytes[i] = parsed[i]; + if (u) { + unsigned i; + + for (i = 0; i < ELEMENTSOF(parsed); i++) + u->bytes[i] = parsed[i]; + } return 0; }