diff options
Diffstat (limited to 'src/gpt-auto-generator')
-rw-r--r-- | src/gpt-auto-generator/gpt-auto-generator.c | 514 |
1 files changed, 419 insertions, 95 deletions
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index da5f3b647a..6cc1aad705 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -1,5 +1,3 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - /*** This file is part of systemd. @@ -19,78 +17,43 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> +#include <blkid/blkid.h> #include <stdlib.h> #include <sys/statfs.h> -#include <blkid/blkid.h> +#include <unistd.h> -#include "sd-id128.h" #include "libudev.h" -#include "path-util.h" -#include "util.h" -#include "mkdir.h" +#include "sd-id128.h" + +#include "alloc-util.h" +#include "blkid-util.h" +#include "btrfs-util.h" +#include "dirent-util.h" +#include "efivars.h" +#include "fd-util.h" +#include "fileio.h" +#include "fstab-util.h" +#include "generator.h" +#include "gpt.h" #include "missing.h" -#include "udev-util.h" +#include "mkdir.h" +#include "mount-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "proc-cmdline.h" #include "special.h" +#include "stat-util.h" +#include "string-util.h" +#include "udev-util.h" #include "unit-name.h" +#include "util.h" #include "virt.h" -#include "generator.h" -#include "gpt.h" -#include "fileio.h" -#include "efivars.h" -#include "blkid-util.h" -#include "btrfs-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_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); - - fflush(f); - if (ferror(f)) - return log_error_errno(errno, "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; -} - 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; @@ -142,9 +105,9 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi id, what, rw ? "" : "read-only", id); - fflush(f); - if (ferror(f)) - return log_error_errno(errno, "Failed to write file %s: %m", p); + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write file %s: %m", p); from = strjoina("../", n); @@ -202,6 +165,7 @@ static int add_mount( const char *where, const char *fstype, bool rw, + const char *options, const char *description, const char *post) { @@ -262,11 +226,14 @@ static int add_mount( if (fstype) fprintf(f, "Type=%s\n", fstype); - fprintf(f, "Options=%s\n", rw ? "rw" : "ro"); + if (options) + fprintf(f, "Options=%s,%s\n", options, rw ? "rw" : "ro"); + else + fprintf(f, "Options=%s\n", rw ? "rw" : "ro"); - fflush(f); - if (ferror(f)) - return log_error_errno(errno, "Failed to write unit file %s: %m", p); + 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); @@ -281,6 +248,28 @@ static int add_mount( 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, @@ -298,8 +287,7 @@ static int probe_and_add_mount( assert(where); assert(description); - if (path_is_mount_point(where, AT_SYMLINK_FOLLOW) <= 0 && - dir_is_empty(where) <= 0) { + if (path_is_busy(where)) { log_debug("%s already populated, ignoring.", where); return 0; } @@ -312,8 +300,7 @@ static int probe_and_add_mount( if (!b) { if (errno == 0) return log_oom(); - log_error_errno(errno, "Failed to allocate prober: %m"); - return -errno; + return log_error_errno(errno, "Failed to allocate prober: %m"); } blkid_probe_enable_superblocks(b, 1); @@ -335,21 +322,247 @@ static int probe_and_add_mount( 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) { + const char *esp; + int r; + + assert(what); + + if (in_initrd()) { + log_debug("In initrd, ignoring the ESP."); + return 0; + } + + if (detect_container() > 0) { + log_debug("In a container, ignoring the ESP."); + return 0; + } + + /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice */ + esp = access("/efi/", F_OK) >= 0 ? "/efi" : "/boot"; + + /* We create an .automount which is not overridden by the .mount from the fstab generator. */ + if (fstab_is_mount_point(esp)) { + log_debug("%s specified in fstab, ignoring.", esp); + return 0; + } + + if (path_is_busy(esp)) { + log_debug("%s already populated, ignoring.", esp); + return 0; + } + + if (is_efi_boot()) { + _cleanup_blkid_free_probe_ blkid_probe b = NULL; + const char *fstype = NULL, *uuid_string = NULL; + sd_id128_t loader_uuid, part_uuid; + + /* If this is an EFI boot, be extra careful, and only mount the ESP if it was the ESP used for booting. */ + + r = efi_loader_get_device_part_uuid(&loader_uuid); + 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 %s is not a FAT filesystem, ignoring.", esp); + return 0; + } + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid_string, NULL); + if (r != 0) { + log_debug_errno(errno, "Partition for %s does not have a UUID, ignoring.", esp); + return 0; + } + + if (sd_id128_from_string(uuid_string, &part_uuid) < 0) { + log_debug("Partition for %s does not have a valid UUID, ignoring.", esp); + return 0; + } + + if (!sd_id128_equal(part_uuid, loader_uuid)) { + log_debug("Partition for %s does not appear to be the partition we are booted from.", esp); + return 0; + } + } else + log_debug("Not an EFI boot, skipping ESP check."); + + return add_automount("boot", + what, + esp, + "vfat", + true, + "umask=0077", + "EFI System Partition Automount", + 120 * USEC_PER_SEC); +} +#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 *home = NULL, *srv = 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 home_nr = -1, srv_nr = -1; + int boot_nr = -1, home_nr = -1, srv_nr = -1; bool home_rw = true, srv_rw = true; blkid_partlist pl; int r, k; @@ -418,16 +631,22 @@ static int enumerate_partitions(dev_t devnum) { errno = 0; r = blkid_do_safeprobe(b); - if (r == -2 || r == 1) /* no result or uncertain */ + 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) + } 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) - return log_error_errno(errno ?: EIO, - "%s: failed to determine partition table type: %m", node); + 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")) { @@ -463,12 +682,12 @@ static int enumerate_partitions(dev_t devnum) { 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; - unsigned long long flags; q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); if (!q) @@ -492,13 +711,6 @@ static int enumerate_partitions(dev_t devnum) { if (!pp) continue; - flags = blkid_partition_get_flags(pp); - - /* Ignore partitions that are not marked for automatic - * mounting on discovery */ - if (flags & GPT_FLAG_NO_AUTO) - continue; - nr = blkid_partition_get_partno(pp); if (nr < 0) continue; @@ -510,8 +722,13 @@ static int enumerate_partitions(dev_t devnum) { 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; @@ -521,8 +738,27 @@ static int enumerate_partitions(dev_t devnum) { 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; @@ -530,13 +766,15 @@ static int enumerate_partitions(dev_t devnum) { home_nr = nr; home_rw = !(flags & GPT_FLAG_READ_ONLY), - free(home); - home = strdup(subnode); - if (!home) + 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; @@ -544,13 +782,18 @@ static int enumerate_partitions(dev_t devnum) { srv_nr = nr; srv_rw = !(flags & GPT_FLAG_READ_ONLY), - free(srv); - srv = strdup(subnode); - if (!srv) + 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) @@ -573,6 +816,10 @@ static int get_block_device(const char *path, dev_t *dev) { 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; @@ -590,6 +837,76 @@ static int get_block_device(const char *path, dev_t *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; @@ -639,12 +956,19 @@ static int add_root_mount(void) { * 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 @@ -656,11 +980,11 @@ static int add_mounts(void) { dev_t devno; int r; - r = get_block_device("/", &devno); + 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("/usr", &devno); + 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) { @@ -689,7 +1013,7 @@ int main(int argc, char *argv[]) { umask(0022); - if (detect_container(NULL) > 0) { + if (detect_container() > 0) { log_debug("In a container, exiting."); return EXIT_SUCCESS; } |