]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
boot: Deduplicate old-style loader entries. 13953/head
authorSpencer Michaels <sxmichaels@gmail.com>
Wed, 20 Nov 2019 22:18:32 +0000 (14:18 -0800)
committerSpencer Michaels <sxmichaels@gmail.com>
Thu, 21 Nov 2019 23:50:03 +0000 (15:50 -0800)
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
src/shared/bootspec.h

index ff6d5666bce6a3871fa35b21b6ebc3db126ba54e..699b101b3901688f05311decd72c2a7bf2edc6be 100644 (file)
@@ -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))
index c18d89494a512132be7adde966bf200ef2e3a062..a825b35bc58c829caabb0036e5ea44e4b98c1a89 100644 (file)
@@ -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;
 }