From d292874bf6845605b733ad8e6926a5bee23ef5d3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 20 Mar 2026 10:21:11 +0100 Subject: [PATCH] bootspec: add bootspec_extract_osrelease() helper --- src/shared/bootspec.c | 147 +++++++++++++++++++++++++++++++++--------- src/shared/bootspec.h | 2 + 2 files changed, 120 insertions(+), 29 deletions(-) diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index ebd63788c0..c0111d1f9c 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -31,6 +31,7 @@ #include "string-util.h" #include "strv.h" #include "uki.h" +#include "utf8.h" static const char* const boot_entry_type_description_table[_BOOT_ENTRY_TYPE_MAX] = { [BOOT_ENTRY_TYPE1] = "Boot Loader Specification Type #1 (.conf)", @@ -711,6 +712,110 @@ static int boot_entries_find_type1( return 0; } +static void mangle_osrelease_string(char **s, const char *field) { + assert(s); + assert(field); + + if (!isempty(*s) && !string_has_cc(*s, /* ok= */ NULL) && utf8_is_valid(*s)) + return; + + if (*s) { + log_debug("OS release field '%s' is not clean, suppressing.", field); + *s = mfree(*s); + } +} + +int bootspec_extract_osrelease( + const char *text, + char **ret_good_name, + char **ret_good_version, + char **ret_good_sort_key, + char **ret_os_id, + char **ret_os_version_id, + char **ret_image_id, + char **ret_image_version) { + + int r; + + assert(text); + + _cleanup_free_ char *os_pretty_name = NULL, *image_id = NULL, *os_name = NULL, *os_id = NULL, + *image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL; + r = parse_env_data(text, /* size= */ SIZE_MAX, + "os-release", + "PRETTY_NAME", &os_pretty_name, + "IMAGE_ID", &image_id, + "NAME", &os_name, + "ID", &os_id, + "IMAGE_VERSION", &image_version, + "VERSION", &os_version, + "VERSION_ID", &os_version_id, + "BUILD_ID", &os_build_id); + if (r < 0) + return r; + + mangle_osrelease_string(&os_pretty_name, "PRETTY_NAME"); + mangle_osrelease_string(&image_id, "IMAGE_ID"); + mangle_osrelease_string(&os_name, "NAME"); + mangle_osrelease_string(&os_id, "ID"); + mangle_osrelease_string(&image_version, "IMAGE_VERSION"); + mangle_osrelease_string(&os_version, "VERSION"); + mangle_osrelease_string(&os_version_id, "VERSION_ID"); + mangle_osrelease_string(&os_build_id, "BUILD_ID"); + + const char *good_name, *good_version, *good_sort_key; + if (!bootspec_pick_name_version_sort_key( + os_pretty_name, + image_id, + os_name, + os_id, + image_version, + os_version, + os_version_id, + os_build_id, + &good_name, + &good_version, + &good_sort_key)) + return -EBADMSG; + + _cleanup_free_ char *copy_good_name = NULL, *copy_good_version = NULL, *copy_good_sort_key = NULL; + if (ret_good_name) { + copy_good_name = strdup(good_name); + if (!copy_good_name) + return -ENOMEM; + } + + if (ret_good_version && good_version) { + copy_good_version = strdup(good_version); + if (!copy_good_version) + return -ENOMEM; + } + + if (ret_good_sort_key && good_sort_key) { + copy_good_sort_key = strdup(good_sort_key); + if (!copy_good_sort_key) + return -ENOMEM; + } + + if (ret_good_name) + *ret_good_name = TAKE_PTR(copy_good_name); + if (ret_good_version) + *ret_good_version = TAKE_PTR(copy_good_version); + if (ret_good_sort_key) + *ret_good_sort_key = TAKE_PTR(copy_good_sort_key); + + if (ret_os_id) + *ret_os_id = TAKE_PTR(os_id); + if (ret_os_version_id) + *ret_os_version_id = TAKE_PTR(os_version_id); + if (ret_image_id) + *ret_image_id = TAKE_PTR(image_id); + if (ret_image_version) + *ret_image_version = TAKE_PTR(image_version); + + return 0; +} + static int boot_entry_load_unified( const char *root, const BootEntrySource source, @@ -721,9 +826,6 @@ static int boot_entry_load_unified( const char *cmdline_text, BootEntry *ret) { - _cleanup_free_ char *fname = NULL, *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; - const char *k, *good_name, *good_version, *good_sort_key; int r; assert(root); @@ -731,36 +833,22 @@ static int boot_entry_load_unified( assert(osrelease_text); assert(ret); - k = path_startswith(path, root); + const char *k = path_startswith(path, root); if (!k) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path is not below root: %s", path); - r = parse_env_data(osrelease_text, /* size= */ SIZE_MAX, - ".osrel", - "PRETTY_NAME", &os_pretty_name, - "IMAGE_ID", &os_image_id, - "NAME", &os_name, - "ID", &os_id, - "IMAGE_VERSION", &os_image_version, - "VERSION", &os_version, - "VERSION_ID", &os_version_id, - "BUILD_ID", &os_build_id); + _cleanup_free_ char *good_name = NULL, *good_version = NULL, *good_sort_key = NULL, *os_id = NULL, *os_version_id = NULL; + r = bootspec_extract_osrelease( + osrelease_text, + &good_name, + &good_version, + &good_sort_key, + &os_id, + &os_version_id, + /* ret_image_id= */ NULL, + /* ret_image_version= */ NULL); if (r < 0) - return log_error_errno(r, "Failed to parse os-release data from unified kernel image %s: %m", path); - - if (!bootspec_pick_name_version_sort_key( - os_pretty_name, - os_image_id, - os_name, - os_id, - os_image_version, - os_version, - os_version_id, - os_build_id, - &good_name, - &good_version, - &good_sort_key)) - return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path); + return log_error_errno(r, "Failed to extract name/version/sort-key from os-release data from unified kernel image %s, refusing: %m", path); _cleanup_free_ char *profile_id = NULL, *profile_title = NULL; if (profile_text) { @@ -773,6 +861,7 @@ static int boot_entry_load_unified( return log_error_errno(r, "Failed to parse profile data from unified kernel image '%s': %m", path); } + _cleanup_free_ char *fname = NULL; r = path_extract_filename(path, &fname); if (r < 0) return log_error_errno(r, "Failed to extract file name from '%s': %m", path); diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index 01abe05e72..d5f6930be9 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -164,3 +164,5 @@ int boot_filename_extract_tries(const char *fname, char **ret_stripped, unsigned int boot_entry_to_json(const BootConfig *c, size_t i, sd_json_variant **ret); int pe_find_uki_sections(int fd, const char *path, unsigned profile, char **ret_osrelease, char **ret_profile, char **ret_cmdline); + +int bootspec_extract_osrelease(const char *text, char **ret_good_name, char **ret_good_version, char **ret_good_sort_key, char **ret_os_id, char **ret_os_version_id, char **ret_image_id, char **ret_image_version);