Merge pull request #22519 from poettering/boot-order-title-revert

sd-boot: rework boot entry sorting
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2022-03-18 19:04:48 +01:00
committed by GitHub
12 changed files with 281 additions and 80 deletions
+2
View File
@@ -591,6 +591,8 @@ static int boot_entry_show(
printf(" source: %s\n", link ?: e->path);
}
if (e->sort_key)
printf(" sort-key: %s\n", e->sort_key);
if (e->version)
printf(" version: %s\n", e->version);
if (e->machine_id)
+63 -30
View File
@@ -53,6 +53,7 @@ typedef struct {
CHAR16 *id; /* The unique identifier for this entry (typically the filename of the file defining the entry) */
CHAR16 *title_show; /* The string to actually display (this is made unique before showing) */
CHAR16 *title; /* The raw (human readable) title string of the entry (not necessarily unique) */
CHAR16 *sort_key; /* The string to use as primary sory key, usually ID= from os-release, possibly suffixed */
CHAR16 *version; /* The raw (human readable) version string of the entry */
CHAR16 *machine_id;
EFI_HANDLE *device;
@@ -540,6 +541,7 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) {
ps_string(L" id: %s\n", entry->id);
ps_string(L" title: %s\n", entry->title);
ps_string(L" title show: %s\n", streq_ptr(entry->title, entry->title_show) ? NULL : entry->title_show);
ps_string(L" sort key: %s\n", entry->sort_key);
ps_string(L" version: %s\n", entry->version);
ps_string(L" machine-id: %s\n", entry->machine_id);
if (entry->device)
@@ -1026,6 +1028,7 @@ static void config_entry_free(ConfigEntry *entry) {
FreePool(entry->id);
FreePool(entry->title_show);
FreePool(entry->title);
FreePool(entry->sort_key);
FreePool(entry->version);
FreePool(entry->machine_id);
FreePool(entry->loader);
@@ -1427,6 +1430,12 @@ static void config_entry_add_from_file(
continue;
}
if (strcmpa((CHAR8 *)"sort-key", key) == 0) {
FreePool(entry->sort_key);
entry->sort_key = xstra_to_str(value);
continue;
}
if (strcmpa((CHAR8 *)"version", key) == 0) {
FreePool(entry->version);
entry->version = xstra_to_str(value);
@@ -1531,6 +1540,7 @@ static void config_entry_add_from_file(
entry->device = device;
entry->id = xstrdup(file);
StrLwr(entry->id);
config_add_entry(config, entry);
@@ -1609,6 +1619,8 @@ static void config_load_entries(
assert(device);
assert(root_dir);
/* Adds Boot Loader Type #1 entries (i.e. /loader/entries/….conf) */
err = open_directory(root_dir, L"\\loader\\entries", &entries_dir);
if (EFI_ERROR(err))
return;
@@ -1642,24 +1654,40 @@ static INTN config_entry_compare(const ConfigEntry *a, const ConfigEntry *b) {
assert(a);
assert(b);
/* Order entries that have no tries left towards the end of the list. They have
* proven to be bad and should not be selected automatically. */
if (a->tries_left != 0 && b->tries_left == 0)
return -1;
/* Order entries that have no tries left to the end of the list */
if (a->tries_left == 0 && b->tries_left != 0)
return 1;
if (a->tries_left != 0 && b->tries_left == 0)
return -1;
r = strcasecmp_ptr(a->title ?: a->id, b->title ?: b->id);
if (r != 0)
/* If there's a sort key defined for *both* entries, then we do new-style ordering, i.e. by
* sort-key/machine-id/version, with a final fallback to id. If there's no sort key for either, we do
* old-style ordering, i.e. by id only. If one has sort key and the other does not, we put new-style
* before old-style. */
r = CMP(!a->sort_key, !b->sort_key);
if (r != 0) /* one is old-style, one new-style */
return r;
if (a->sort_key && b->sort_key) {
/* Sort by machine id now so that different installations don't interleave their versions. */
r = strcasecmp_ptr(a->machine_id, b->machine_id);
if (r != 0)
return r;
r = strcmp(a->sort_key, b->sort_key);
if (r != 0)
return r;
/* Reverse version comparison order so that higher versions are preferred. */
r = strverscmp_improved(b->version, a->version);
/* If multiple installations of the same OS are around, group by machine ID */
r = strcmp_ptr(a->machine_id, b->machine_id);
if (r != 0)
return r;
/* If the sort key was defined, then order by version now (downwards, putting the newest first) */
r = -strverscmp_improved(a->version, b->version);
if (r != 0)
return r;
}
/* Now order by ID (the version is likely part of the ID, thus note that this might put the oldest
* version last, not first, i.e. specifying a sort key explicitly is thus generally preferable, to
* take benefit of the explicit sorting above.) */
r = strverscmp_improved(a->id, b->id);
if (r != 0)
return r;
@@ -1668,19 +1696,18 @@ static INTN config_entry_compare(const ConfigEntry *a, const ConfigEntry *b) {
return 0;
/* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */
if (a->tries_left > b->tries_left)
return -1;
if (a->tries_left < b->tries_left)
return 1;
if (a->tries_left > b->tries_left)
return -1;
/* If they have the same number of tries left, then let the one win which was tried fewer times so far */
if (a->tries_done < b->tries_done)
return -1;
if (a->tries_done > b->tries_done)
return 1;
if (a->tries_done < b->tries_done)
return -1;
/* As a last resort, use the id (file name). */
return strverscmp_improved(a->id, b->id);
return 0;
}
static UINTN config_entry_find(Config *config, const CHAR16 *needle) {
@@ -1724,7 +1751,7 @@ static void config_default_entry_select(Config *config) {
return;
}
/* Select the first suitable entry. */
/* select the first suitable entry */
for (i = 0; i < config->entry_count; i++) {
if (config->entries[i]->type == LOADER_AUTO || config->entries[i]->call)
continue;
@@ -1853,6 +1880,7 @@ static ConfigEntry *config_entry_add_loader(
CHAR16 key,
const CHAR16 *title,
const CHAR16 *loader,
const CHAR16 *sort_key,
const CHAR16 *version) {
ConfigEntry *entry;
@@ -1871,11 +1899,14 @@ static ConfigEntry *config_entry_add_loader(
.device = device,
.loader = xstrdup(loader),
.id = xstrdup(id),
.sort_key = xstrdup(sort_key),
.key = key,
.tries_done = UINTN_MAX,
.tries_left = UINTN_MAX,
};
StrLwr(entry->id);
config_add_entry(config, entry);
return entry;
}
@@ -1943,7 +1974,7 @@ static ConfigEntry *config_entry_add_loader_auto(
if (EFI_ERROR(err))
return NULL;
return config_entry_add_loader(config, device, LOADER_AUTO, id, key, title, loader, NULL);
return config_entry_add_loader(config, device, LOADER_AUTO, id, key, title, loader, NULL, NULL);
}
static void config_entry_add_osx(Config *config) {
@@ -2097,6 +2128,8 @@ static void config_entry_add_linux(
UINTN f_size = 0;
EFI_STATUS err;
/* Adds Boot Loader Type #2 entries (i.e. /EFI/Linux/….efi) */
assert(config);
assert(device);
assert(root_dir);
@@ -2121,7 +2154,7 @@ static void config_entry_add_linux(
_cleanup_freepool_ CHAR16 *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL,
*os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL,
*path = NULL;
const CHAR16 *good_name, *good_version;
const CHAR16 *good_name, *good_version, *good_sort_key;
_cleanup_freepool_ CHAR8 *content = NULL;
UINTN offs[_SECTION_MAX] = {};
UINTN szs[_SECTION_MAX] = {};
@@ -2202,7 +2235,7 @@ static void config_entry_add_linux(
}
}
if (!bootspec_pick_name_version(
if (!bootspec_pick_name_version_sort_key(
os_pretty_name,
os_image_id,
os_name,
@@ -2212,7 +2245,8 @@ static void config_entry_add_linux(
os_version_id,
os_build_id,
&good_name,
&good_version))
&good_version,
&good_sort_key))
continue;
path = xpool_print(L"\\EFI\\Linux\\%s", f->FileName);
@@ -2220,10 +2254,11 @@ static void config_entry_add_linux(
config,
device,
LOADER_UNIFIED_LINUX,
f->FileName,
/* id= */ f->FileName,
/* key= */ 'l',
good_name,
path,
/* title= */ good_name,
/* loader= */ path,
/* sort_key= */ good_sort_key,
good_version);
config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi");
@@ -2449,14 +2484,12 @@ static void config_load_all_entries(
/* Similar, but on any XBOOTLDR partition */
config_load_xbootldr(config, loaded_image->DeviceHandle);
/* Add these now, so they get sorted with the rest. */
config_entry_add_osx(config);
config_entry_add_windows(config, loaded_image->DeviceHandle, root_dir);
/* sort entries after version number */
sort_pointer_array((void **) config->entries, config->entry_count, (compare_pointer_func_t) config_entry_compare);
/* if we find some well-known loaders, add them to the end of the list */
config_entry_add_osx(config);
config_entry_add_windows(config, loaded_image->DeviceHandle, root_dir);
config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, NULL,
L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, loaded_image_path,