From 15b82eecb6461b55c8a70abd3276318af8dee0b9 Mon Sep 17 00:00:00 2001 From: Spencer Michaels Date: Wed, 20 Nov 2019 14:18:32 -0800 Subject: [PATCH] boot: Deduplicate old-style loader entries. In cases where systemd (and thus bootctl) is updated to a version including the earlier unique-ID fix, but the corresponding new version of systemd-boot is not installed to the ESP and run at least once, the bootloader will report old-style entry IDs cached in the LoaderEntries EFI variable, while bootctl will report new-style IDs for the same entries, producing duplicate entries. This commit makes bootctl compute and retain old-style IDs for non-auto entries so that it can properly deduplicate entries even if the cache contains old-style IDs. --- src/shared/bootspec.c | 17 ++++++++++++----- src/shared/bootspec.h | 8 ++++++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index ff6d5666bc..699b101b39 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -36,6 +36,7 @@ static void boot_entry_free(BootEntry *entry) { assert(entry); free(entry->id); + free(entry->id_old); free(entry->path); free(entry->root); free(entry->title); @@ -74,7 +75,8 @@ static int boot_entry_load( b = basename(path); tmp.id = strdup(b); - if (!tmp.id) + tmp.id_old = strndup(b, c - b); + if (!tmp.id || !tmp.id_old) return log_oom(); if (!efi_loader_entry_name_valid(tmp.id)) @@ -283,7 +285,7 @@ static int boot_entry_load_unified( const char *cmdline, BootEntry *ret) { - _cleanup_free_ char *os_pretty_name = NULL; + _cleanup_free_ char *os_pretty_name = NULL, *os_id = NULL, *version_id = NULL, *build_id = NULL; _cleanup_(boot_entry_free) BootEntry tmp = { .type = BOOT_ENTRY_UNIFIED, }; @@ -304,16 +306,21 @@ static int boot_entry_load_unified( if (!f) return log_error_errno(errno, "Failed to open os-release buffer: %m"); - r = parse_env_file(f, "os-release", "PRETTY_NAME", &os_pretty_name); + r = parse_env_file(f, "os-release", + "PRETTY_NAME", &os_pretty_name, + "ID", &os_id, + "VERSION_ID", &version_id, + "BUILD_ID", &build_id); if (r < 0) return log_error_errno(r, "Failed to parse os-release data from unified kernel image %s: %m", path); - if (!os_pretty_name) + if (!os_pretty_name || !os_id || !(version_id || build_id)) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path); b = basename(path); tmp.id = strdup(b); - if (!tmp.id) + tmp.id_old = strjoin(os_id, "-", version_id ?: build_id); + if (!tmp.id || !tmp.id_old) return log_oom(); if (!efi_loader_entry_name_valid(tmp.id)) diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index c18d89494a..a825b35bc5 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -21,6 +21,7 @@ typedef enum BootEntryType { typedef struct BootEntry { BootEntryType type; char *id; /* This is the file basename without extension */ + char *id_old; /* Old-style ID, for deduplication purposes. */ char *path; /* This is the full path to the drop-in file */ char *root; /* The root path in which the drop-in was found, i.e. to which 'kernel', 'efi' and 'initrd' are relative */ char *title; @@ -54,9 +55,12 @@ typedef struct BootConfig { static inline bool boot_config_has_entry(BootConfig *config, const char *id) { size_t j; - for (j = 0; j < config->n_entries; j++) - if (streq(config->entries[j].id, id)) + for (j = 0; j < config->n_entries; j++) { + const char* entry_id_old = config->entries[j].id_old; + if (streq(config->entries[j].id, id) || + (entry_id_old && streq(entry_id_old, id))) return true; + } return false; } -- 2.25.1