]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
uki-util: move functions for inspecting kernel to uki-util.[ch] 26862/head
authorYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 17 Mar 2023 05:20:29 +0000 (14:20 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 17 Mar 2023 11:40:34 +0000 (20:40 +0900)
src/boot/bootctl-uki.c
src/shared/meson.build
src/shared/uki-util.c [new file with mode: 0644]
src/shared/uki-util.h [new file with mode: 0644]

index cfa491f22ecc61f3945aeb52738a52457b12fc0a..eda79218fc2181f2f6c3754e36f49588e83b35dd 100644 (file)
@@ -1,306 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
-#include "bootctl.h"
+#include "alloc-util.h"
 #include "bootctl-uki.h"
-#include "env-file.h"
-#include "fd-util.h"
-#include "os-util.h"
-#include "parse-util.h"
-#include "pe-header.h"
-#include "string-table.h"
-
-#define MAX_SECTIONS 96
-
-static const uint8_t dos_file_magic[2] = "MZ";
-static const uint8_t pe_file_magic[4] = "PE\0\0";
-
-static const uint8_t name_osrel[8] = ".osrel";
-static const uint8_t name_linux[8] = ".linux";
-static const uint8_t name_initrd[8] = ".initrd";
-static const uint8_t name_cmdline[8] = ".cmdline";
-static const uint8_t name_uname[8] = ".uname";
-
-typedef enum KernelType {
-        KERNEL_TYPE_UNKNOWN,
-        KERNEL_TYPE_UKI,
-        KERNEL_TYPE_PE,
-        _KERNEL_TYPE_MAX,
-        _KERNEL_TYPE_INVALID = -EINVAL,
-} KernelType;
-
-static const char * const kernel_type_table[_KERNEL_TYPE_MAX] = {
-        [KERNEL_TYPE_UNKNOWN] = "unknown",
-        [KERNEL_TYPE_UKI]     = "uki",
-        [KERNEL_TYPE_PE]      = "pe",
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(kernel_type, KernelType);
-
-static int pe_sections(FILE *uki, struct PeSectionHeader **ret, size_t *ret_n) {
-        _cleanup_free_ struct PeSectionHeader *sections = NULL;
-        struct DosFileHeader dos;
-        struct PeHeader pe;
-        size_t scount;
-        uint64_t soff, items;
-
-        assert(uki);
-        assert(ret);
-        assert(ret_n);
-
-        items = fread(&dos, 1, sizeof(dos), uki);
-        if (items < sizeof(dos.Magic))
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "File is smaller than DOS magic (got %"PRIu64" of %zu bytes)",
-                                       items, sizeof(dos.Magic));
-        if (memcmp(dos.Magic, dos_file_magic, sizeof(dos_file_magic)) != 0)
-                goto no_sections;
-
-        if (items != sizeof(dos))
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "File is smaller than DOS header (got %"PRIu64" of %zu bytes)",
-                                       items, sizeof(dos));
-
-        if (fseek(uki, le32toh(dos.ExeHeader), SEEK_SET) < 0)
-                return log_error_errno(errno, "seek to PE header");
-
-        items = fread(&pe, 1, sizeof(pe), uki);
-        if (items != sizeof(pe))
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE header read error");
-        if (memcmp(pe.Magic, pe_file_magic, sizeof(pe_file_magic)) != 0)
-                goto no_sections;
-
-        soff = le32toh(dos.ExeHeader) + sizeof(pe) + le16toh(pe.FileHeader.SizeOfOptionalHeader);
-        if (fseek(uki, soff, SEEK_SET) < 0)
-                return log_error_errno(errno, "seek to PE section headers");
-
-        scount = le16toh(pe.FileHeader.NumberOfSections);
-        if (scount > MAX_SECTIONS)
-                goto no_sections;
-        sections = new(struct PeSectionHeader, scount);
-        if (!sections)
-                return log_oom();
-        items = fread(sections, sizeof(*sections), scount, uki);
-        if (items != scount)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE section header read error");
-
-        *ret = TAKE_PTR(sections);
-        *ret_n = scount;
-        return 0;
-
-no_sections:
-        *ret = NULL;
-        *ret_n = 0;
-        return 0;
-}
-
-static bool find_pe_section(
-                struct PeSectionHeader *sections,
-                size_t scount,
-                const uint8_t *name,
-                size_t namelen,
-                size_t *ret) {
-
-        assert(sections || scount == 0);
-        assert(name || namelen == 0);
-
-        for (size_t s = 0; s < scount; s++)
-                if (memcmp_nn(sections[s].Name, sizeof(sections[s].Name), name, namelen) == 0) {
-                        if (ret)
-                                *ret = s;
-                        return true;
-                }
-
-        return false;
-}
-
-static bool is_uki(struct PeSectionHeader *sections, size_t scount) {
-        assert(sections || scount == 0);
-
-        return
-                find_pe_section(sections, scount, name_osrel, sizeof(name_osrel), NULL) &&
-                find_pe_section(sections, scount, name_linux, sizeof(name_linux), NULL) &&
-                find_pe_section(sections, scount, name_initrd, sizeof(name_initrd), NULL);
-}
-
-static int read_pe_section(
-                FILE *uki,
-                struct PeSectionHeader *sections,
-                size_t scount,
-                const uint8_t *name,
-                size_t name_len,
-                void **ret,
-                size_t *ret_n) {
-
-        struct PeSectionHeader *section;
-        _cleanup_free_ void *data = NULL;
-        uint32_t size, bytes;
-        uint64_t soff;
-        size_t idx;
-
-        assert(uki);
-        assert(sections || scount == 0);
-        assert(ret);
-
-        if (!find_pe_section(sections, scount, name, name_len, &idx)) {
-                *ret = NULL;
-                if (ret_n)
-                        *ret_n = 0;
-                return 0;
-        }
-
-        section = sections + idx;
-        soff = le32toh(section->PointerToRawData);
-        size = le32toh(section->VirtualSize);
-
-        if (size > 16 * 1024)
-                return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "PE section too big");
-
-        if (fseek(uki, soff, SEEK_SET) < 0)
-                return log_error_errno(errno, "seek to PE section");
-
-        data = malloc(size+1);
-        if (!data)
-                return log_oom();
-        ((uint8_t*) data)[size] = 0; /* safety NUL byte */
-
-        bytes = fread(data, 1, size, uki);
-        if (bytes != size)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE section read error");
-
-        *ret = TAKE_PTR(data);
-        if (ret_n)
-                *ret_n = size;
-        return 1;
-}
-
-static int uki_read_pretty_name(
-                FILE *uki,
-                struct PeSectionHeader *sections,
-                size_t scount,
-                char **ret) {
-
-        _cleanup_free_ char *pname = NULL, *name = NULL;
-        _cleanup_fclose_ FILE *s = NULL;
-        _cleanup_free_ void *osrel = NULL;
-        size_t osrel_size = 0;
-        int r;
-
-        assert(uki);
-        assert(sections || scount == 0);
-        assert(ret);
-
-        r = read_pe_section(uki, sections, scount, name_osrel, sizeof(name_osrel), &osrel, &osrel_size);
-        if (r < 0)
-                return r;
-        if (r == 0) {
-                *ret = NULL;
-                return 0;
-        }
-
-        s = fmemopen(osrel, osrel_size, "r");
-        if (!s)
-                return log_warning_errno(errno, "Failed to open embedded os-release file, ignoring: %m");
-
-        r = parse_env_file(s, NULL,
-                           "PRETTY_NAME", &pname,
-                           "NAME",        &name);
-        if (r < 0)
-                return log_warning_errno(r, "Failed to parse embedded os-release file, ignoring: %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(
-                FILE *uki,
-                struct PeSectionHeader *sections,
-                size_t scount,
-                char **ret_cmdline,
-                char **ret_uname,
-                char **ret_pretty_name) {
-
-        _cleanup_free_ char *cmdline = NULL, *uname = NULL, *pname = NULL;
-        int r;
-
-        assert(uki);
-        assert(sections || scount == 0);
-
-        if (ret_cmdline) {
-                r = read_pe_section(uki, sections, scount, name_cmdline, sizeof(name_cmdline), (void**) &cmdline, NULL);
-                if (r < 0)
-                        return r;
-        }
-
-        if (ret_uname) {
-                r = read_pe_section(uki, sections, scount, name_uname, sizeof(name_uname), (void**) &uname, NULL);
-                if (r < 0)
-                        return r;
-        }
-
-        if (ret_pretty_name) {
-                r = uki_read_pretty_name(uki, sections, scount, &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;
-}
-
-static int inspect_kernel(
-                const char *filename,
-                KernelType *ret_type,
-                char **ret_cmdline,
-                char **ret_uname,
-                char **ret_pretty_name) {
-
-        _cleanup_fclose_ FILE *uki = NULL;
-        _cleanup_free_ struct PeSectionHeader *sections = NULL;
-        size_t scount;
-        KernelType t;
-        int r;
-
-        assert(filename);
-
-        uki = fopen(filename, "re");
-        if (!uki)
-                return log_error_errno(errno, "Failed to open UKI file '%s': %m", filename);
-
-        r = pe_sections(uki, &sections, &scount);
-        if (r < 0)
-                return r;
-
-        if (!sections)
-                t = KERNEL_TYPE_UNKNOWN;
-        else if (is_uki(sections, scount)) {
-                t = KERNEL_TYPE_UKI;
-                r = inspect_uki(uki, sections, scount, ret_cmdline, ret_uname, ret_pretty_name);
-                if (r < 0)
-                        return r;
-        } else
-                t = KERNEL_TYPE_PE;
-
-        if (ret_type)
-                *ret_type = t;
-
-        return 0;
-}
+#include "uki-util.h"
 
 int verb_kernel_identify(int argc, char *argv[], void *userdata) {
         KernelType t;
index 8c107fd1614035d2bc4ea0b08e3b335d4dae6c6e..57bea08101fe21507f6e306d56bea00b8e697152 100644 (file)
@@ -150,6 +150,7 @@ shared_sources = files(
         'tpm2-util.c',
         'udev-util.c',
         'uid-alloc-range.c',
+        'uki-util.c',
         'user-record-nss.c',
         'user-record-show.c',
         'user-record.c',
diff --git a/src/shared/uki-util.c b/src/shared/uki-util.c
new file mode 100644 (file)
index 0000000..218e795
--- /dev/null
@@ -0,0 +1,294 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "fd-util.h"
+#include "env-file.h"
+#include "os-util.h"
+#include "parse-util.h"
+#include "pe-header.h"
+#include "string-table.h"
+#include "uki-util.h"
+
+#define MAX_SECTIONS 96
+
+static const uint8_t dos_file_magic[2] = "MZ";
+static const uint8_t pe_file_magic[4] = "PE\0\0";
+
+static const uint8_t name_osrel[8] = ".osrel";
+static const uint8_t name_linux[8] = ".linux";
+static const uint8_t name_initrd[8] = ".initrd";
+static const uint8_t name_cmdline[8] = ".cmdline";
+static const uint8_t name_uname[8] = ".uname";
+
+static const char * const kernel_type_table[_KERNEL_TYPE_MAX] = {
+        [KERNEL_TYPE_UNKNOWN] = "unknown",
+        [KERNEL_TYPE_UKI]     = "uki",
+        [KERNEL_TYPE_PE]      = "pe",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_TO_STRING(kernel_type, KernelType);
+
+static int pe_sections(FILE *uki, struct PeSectionHeader **ret, size_t *ret_n) {
+        _cleanup_free_ struct PeSectionHeader *sections = NULL;
+        struct DosFileHeader dos;
+        struct PeHeader pe;
+        size_t scount;
+        uint64_t soff, items;
+
+        assert(uki);
+        assert(ret);
+        assert(ret_n);
+
+        items = fread(&dos, 1, sizeof(dos), uki);
+        if (items < sizeof(dos.Magic))
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "File is smaller than DOS magic (got %"PRIu64" of %zu bytes)",
+                                       items, sizeof(dos.Magic));
+        if (memcmp(dos.Magic, dos_file_magic, sizeof(dos_file_magic)) != 0)
+                goto no_sections;
+
+        if (items != sizeof(dos))
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "File is smaller than DOS header (got %"PRIu64" of %zu bytes)",
+                                       items, sizeof(dos));
+
+        if (fseek(uki, le32toh(dos.ExeHeader), SEEK_SET) < 0)
+                return log_error_errno(errno, "seek to PE header");
+
+        items = fread(&pe, 1, sizeof(pe), uki);
+        if (items != sizeof(pe))
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE header read error");
+        if (memcmp(pe.Magic, pe_file_magic, sizeof(pe_file_magic)) != 0)
+                goto no_sections;
+
+        soff = le32toh(dos.ExeHeader) + sizeof(pe) + le16toh(pe.FileHeader.SizeOfOptionalHeader);
+        if (fseek(uki, soff, SEEK_SET) < 0)
+                return log_error_errno(errno, "seek to PE section headers");
+
+        scount = le16toh(pe.FileHeader.NumberOfSections);
+        if (scount > MAX_SECTIONS)
+                goto no_sections;
+        sections = new(struct PeSectionHeader, scount);
+        if (!sections)
+                return log_oom();
+        items = fread(sections, sizeof(*sections), scount, uki);
+        if (items != scount)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE section header read error");
+
+        *ret = TAKE_PTR(sections);
+        *ret_n = scount;
+        return 0;
+
+no_sections:
+        *ret = NULL;
+        *ret_n = 0;
+        return 0;
+}
+
+static bool find_pe_section(
+                struct PeSectionHeader *sections,
+                size_t scount,
+                const uint8_t *name,
+                size_t namelen,
+                size_t *ret) {
+
+        assert(sections || scount == 0);
+        assert(name || namelen == 0);
+
+        for (size_t s = 0; s < scount; s++)
+                if (memcmp_nn(sections[s].Name, sizeof(sections[s].Name), name, namelen) == 0) {
+                        if (ret)
+                                *ret = s;
+                        return true;
+                }
+
+        return false;
+}
+
+static bool is_uki(struct PeSectionHeader *sections, size_t scount) {
+        assert(sections || scount == 0);
+
+        return
+                find_pe_section(sections, scount, name_osrel, sizeof(name_osrel), NULL) &&
+                find_pe_section(sections, scount, name_linux, sizeof(name_linux), NULL) &&
+                find_pe_section(sections, scount, name_initrd, sizeof(name_initrd), NULL);
+}
+
+static int read_pe_section(
+                FILE *uki,
+                struct PeSectionHeader *sections,
+                size_t scount,
+                const uint8_t *name,
+                size_t name_len,
+                void **ret,
+                size_t *ret_n) {
+
+        struct PeSectionHeader *section;
+        _cleanup_free_ void *data = NULL;
+        uint32_t size, bytes;
+        uint64_t soff;
+        size_t idx;
+
+        assert(uki);
+        assert(sections || scount == 0);
+        assert(ret);
+
+        if (!find_pe_section(sections, scount, name, name_len, &idx)) {
+                *ret = NULL;
+                if (ret_n)
+                        *ret_n = 0;
+                return 0;
+        }
+
+        section = sections + idx;
+        soff = le32toh(section->PointerToRawData);
+        size = le32toh(section->VirtualSize);
+
+        if (size > 16 * 1024)
+                return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "PE section too big");
+
+        if (fseek(uki, soff, SEEK_SET) < 0)
+                return log_error_errno(errno, "seek to PE section");
+
+        data = malloc(size+1);
+        if (!data)
+                return log_oom();
+        ((uint8_t*) data)[size] = 0; /* safety NUL byte */
+
+        bytes = fread(data, 1, size, uki);
+        if (bytes != size)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE section read error");
+
+        *ret = TAKE_PTR(data);
+        if (ret_n)
+                *ret_n = size;
+        return 1;
+}
+
+static int uki_read_pretty_name(
+                FILE *uki,
+                struct PeSectionHeader *sections,
+                size_t scount,
+                char **ret) {
+
+        _cleanup_free_ char *pname = NULL, *name = NULL;
+        _cleanup_fclose_ FILE *s = NULL;
+        _cleanup_free_ void *osrel = NULL;
+        size_t osrel_size = 0;
+        int r;
+
+        assert(uki);
+        assert(sections || scount == 0);
+        assert(ret);
+
+        r = read_pe_section(uki, sections, scount, name_osrel, sizeof(name_osrel), &osrel, &osrel_size);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                *ret = NULL;
+                return 0;
+        }
+
+        s = fmemopen(osrel, osrel_size, "r");
+        if (!s)
+                return log_warning_errno(errno, "Failed to open embedded os-release file, ignoring: %m");
+
+        r = parse_env_file(s, NULL,
+                           "PRETTY_NAME", &pname,
+                           "NAME",        &name);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to parse embedded os-release file, ignoring: %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(
+                FILE *uki,
+                struct PeSectionHeader *sections,
+                size_t scount,
+                char **ret_cmdline,
+                char **ret_uname,
+                char **ret_pretty_name) {
+
+        _cleanup_free_ char *cmdline = NULL, *uname = NULL, *pname = NULL;
+        int r;
+
+        assert(uki);
+        assert(sections || scount == 0);
+
+        if (ret_cmdline) {
+                r = read_pe_section(uki, sections, scount, name_cmdline, sizeof(name_cmdline), (void**) &cmdline, NULL);
+                if (r < 0)
+                        return r;
+        }
+
+        if (ret_uname) {
+                r = read_pe_section(uki, sections, scount, name_uname, sizeof(name_uname), (void**) &uname, NULL);
+                if (r < 0)
+                        return r;
+        }
+
+        if (ret_pretty_name) {
+                r = uki_read_pretty_name(uki, sections, scount, &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(
+                const char *filename,
+                KernelType *ret_type,
+                char **ret_cmdline,
+                char **ret_uname,
+                char **ret_pretty_name) {
+
+        _cleanup_fclose_ FILE *uki = NULL;
+        _cleanup_free_ struct PeSectionHeader *sections = NULL;
+        size_t scount;
+        KernelType t;
+        int r;
+
+        assert(filename);
+
+        uki = fopen(filename, "re");
+        if (!uki)
+                return log_error_errno(errno, "Failed to open UKI file '%s': %m", filename);
+
+        r = pe_sections(uki, &sections, &scount);
+        if (r < 0)
+                return r;
+
+        if (!sections)
+                t = KERNEL_TYPE_UNKNOWN;
+        else if (is_uki(sections, scount)) {
+                t = KERNEL_TYPE_UKI;
+                r = inspect_uki(uki, sections, scount, ret_cmdline, ret_uname, ret_pretty_name);
+                if (r < 0)
+                        return r;
+        } else
+                t = KERNEL_TYPE_PE;
+
+        if (ret_type)
+                *ret_type = t;
+
+        return 0;
+}
diff --git a/src/shared/uki-util.h b/src/shared/uki-util.h
new file mode 100644 (file)
index 0000000..e579f46
--- /dev/null
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <errno.h>
+
+#include "macro.h"
+
+typedef enum KernelType {
+        KERNEL_TYPE_UNKNOWN,
+        KERNEL_TYPE_UKI,
+        KERNEL_TYPE_PE,
+        _KERNEL_TYPE_MAX,
+        _KERNEL_TYPE_INVALID = -EINVAL,
+} KernelType;
+
+const char* kernel_type_to_string(KernelType t) _const_;
+
+int inspect_kernel(
+                const char *filename,
+                KernelType *ret_type,
+                char **ret_cmdline,
+                char **ret_uname,
+                char **ret_pretty_name);