diff options
Diffstat (limited to 'src/shared/kernel-image.c')
-rw-r--r-- | src/shared/kernel-image.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/shared/kernel-image.c b/src/shared/kernel-image.c new file mode 100644 index 0000000..7dc9e01 --- /dev/null +++ b/src/shared/kernel-image.c @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "fd-util.h" +#include "fileio.h" +#include "env-file.h" +#include "kernel-image.h" +#include "os-util.h" +#include "parse-util.h" +#include "pe-binary.h" +#include "string-table.h" + +#define PE_SECTION_READ_MAX (16U*1024U) + +static const char * const kernel_image_type_table[_KERNEL_IMAGE_TYPE_MAX] = { + [KERNEL_IMAGE_TYPE_UNKNOWN] = "unknown", + [KERNEL_IMAGE_TYPE_UKI] = "uki", + [KERNEL_IMAGE_TYPE_PE] = "pe", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(kernel_image_type, KernelImageType); + +static int uki_read_pretty_name( + int fd, + const PeHeader *pe_header, + const IMAGE_SECTION_HEADER *sections, + char **ret) { + + _cleanup_free_ char *pname = NULL, *name = NULL; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ void *osrel = NULL; + size_t osrel_size; + int r; + + assert(fd >= 0); + assert(pe_header); + assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0); + assert(ret); + + r = pe_read_section_data( + fd, + pe_header, + sections, + ".osrel", + /* max_size=*/ PE_SECTION_READ_MAX, + &osrel, + &osrel_size); + if (r == -ENXIO) { /* Section not found */ + *ret = NULL; + return 0; + } + + f = fmemopen(osrel, osrel_size, "r"); + if (!f) + return log_error_errno(errno, "Failed to open embedded os-release file: %m"); + + r = parse_env_file( + f, NULL, + "PRETTY_NAME", &pname, + "NAME", &name); + if (r < 0) + return log_error_errno(r, "Failed to parse embedded os-release file: %m"); + + /* follow the same logic as os_release_pretty_name() */ + if (!isempty(pname)) + *ret = TAKE_PTR(pname); + else if (!isempty(name)) + *ret = TAKE_PTR(name); + else { + char *n = strdup("Linux"); + if (!n) + return log_oom(); + + *ret = n; + } + + return 0; +} + +static int inspect_uki( + int fd, + const PeHeader *pe_header, + const IMAGE_SECTION_HEADER *sections, + char **ret_cmdline, + char **ret_uname, + char **ret_pretty_name) { + + _cleanup_free_ char *cmdline = NULL, *uname = NULL, *pname = NULL; + int r; + + assert(fd >= 0); + assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0); + + if (ret_cmdline) { + r = pe_read_section_data(fd, pe_header, sections, ".cmdline", PE_SECTION_READ_MAX, (void**) &cmdline, NULL); + if (r < 0 && r != -ENXIO) /* If the section doesn't exist, that's fine */ + return r; + } + + if (ret_uname) { + r = pe_read_section_data(fd, pe_header, sections, ".uname", PE_SECTION_READ_MAX, (void**) &uname, NULL); + if (r < 0 && r != -ENXIO) /* If the section doesn't exist, that's fine */ + return r; + } + + if (ret_pretty_name) { + r = uki_read_pretty_name(fd, pe_header, sections, &pname); + if (r < 0) + return r; + } + + if (ret_cmdline) + *ret_cmdline = TAKE_PTR(cmdline); + if (ret_uname) + *ret_uname = TAKE_PTR(uname); + if (ret_pretty_name) + *ret_pretty_name = TAKE_PTR(pname); + + return 0; +} + +int inspect_kernel( + int dir_fd, + const char *filename, + KernelImageType *ret_type, + char **ret_cmdline, + char **ret_uname, + char **ret_pretty_name) { + + _cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL; + _cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL; + KernelImageType t = KERNEL_IMAGE_TYPE_UNKNOWN; + _cleanup_free_ PeHeader *pe_header = NULL; + _cleanup_close_ int fd = -EBADF; + int r; + + assert(dir_fd >= 0 || dir_fd == AT_FDCWD); + assert(filename); + + fd = openat(dir_fd, filename, O_RDONLY|O_CLOEXEC); + if (fd < 0) + return log_error_errno(errno, "Failed to open kernel image file '%s': %m", filename); + + r = pe_load_headers(fd, &dos_header, &pe_header); + if (r == -EBADMSG) /* not a valid PE file */ + goto not_uki; + if (r < 0) + return log_error_errno(r, "Failed to parse kernel image file '%s': %m", filename); + + r = pe_load_sections(fd, dos_header, pe_header, §ions); + if (r == -EBADMSG) /* not a valid PE file */ + goto not_uki; + if (r < 0) + return log_error_errno(r, "Failed to load PE sections from kernel image file '%s': %m", filename); + + if (pe_is_uki(pe_header, sections)) { + r = inspect_uki(fd, pe_header, sections, ret_cmdline, ret_uname, ret_pretty_name); + if (r < 0) + return r; + + t = KERNEL_IMAGE_TYPE_UKI; + goto done; + } else + t = KERNEL_IMAGE_TYPE_PE; + +not_uki: + if (ret_cmdline) + *ret_cmdline = NULL; + if (ret_uname) + *ret_uname = NULL; + if (ret_pretty_name) + *ret_pretty_name = NULL; + +done: + if (ret_type) + *ret_type = t; + + return 0; +} |