Merge pull request #22519 from poettering/boot-order-title-revert
sd-boot: rework boot entry sorting
This commit is contained in:
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user