/src/fwupd/libfwupdplugin/fu-efi-filesystem.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2021 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | | #include "config.h" |
8 | | |
9 | | #include "fu-byte-array.h" |
10 | | #include "fu-efi-file.h" |
11 | | #include "fu-efi-filesystem.h" |
12 | | #include "fu-input-stream.h" |
13 | | #include "fu-partial-input-stream.h" |
14 | | |
15 | | /** |
16 | | * FuEfiFilesystem: |
17 | | * |
18 | | * A UEFI filesystem. |
19 | | * |
20 | | * See also: [class@FuFirmware] |
21 | | */ |
22 | | |
23 | 21.7k | G_DEFINE_TYPE(FuEfiFilesystem, fu_efi_filesystem, FU_TYPE_FIRMWARE) |
24 | 21.7k | |
25 | 21.7k | #define FU_EFI_FILESYSTEM_FILES_MAX 10000 |
26 | 21.7k | #define FU_EFI_FILESYSTEM_SIZE_MAX 0x10000000 /* 256 MB */ |
27 | | |
28 | | static gboolean |
29 | | fu_efi_filesystem_parse(FuFirmware *firmware, |
30 | | GInputStream *stream, |
31 | | FuFirmwareParseFlags flags, |
32 | | GError **error) |
33 | 5.68k | { |
34 | 5.68k | gsize offset = 0; |
35 | 5.68k | gsize streamsz = 0; |
36 | 5.68k | if (!fu_input_stream_size(stream, &streamsz, error)) |
37 | 0 | return FALSE; |
38 | 32.5k | while (offset < streamsz) { |
39 | 31.8k | g_autoptr(FuFirmware) img = fu_efi_file_new(); |
40 | 31.8k | g_autoptr(GInputStream) stream_tmp = NULL; |
41 | 31.8k | gboolean is_freespace = TRUE; |
42 | | |
43 | | /* ignore free space */ |
44 | 47.7k | for (guint i = 0; i < 0x18; i++) { |
45 | 47.2k | guint8 tmp = 0; |
46 | 47.2k | if (!fu_input_stream_read_u8(stream, offset + i, &tmp, error)) |
47 | 21 | return FALSE; |
48 | 47.2k | if (tmp != 0xff) { |
49 | 31.2k | is_freespace = FALSE; |
50 | 31.2k | break; |
51 | 31.2k | } |
52 | 47.2k | } |
53 | 31.8k | if (is_freespace) { |
54 | 531 | g_debug("ignoring free space @0x%x of 0x%x", |
55 | 531 | (guint)offset, |
56 | 531 | (guint)streamsz); |
57 | 531 | break; |
58 | 531 | } |
59 | 31.2k | stream_tmp = fu_partial_input_stream_new(stream, offset, streamsz - offset, error); |
60 | 31.2k | if (stream_tmp == NULL) { |
61 | 0 | g_prefix_error_literal(error, "failed to cut EFI file: "); |
62 | 0 | return FALSE; |
63 | 0 | } |
64 | 31.2k | if (!fu_firmware_parse_stream(img, |
65 | 31.2k | stream_tmp, |
66 | 31.2k | 0x0, |
67 | 31.2k | flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, |
68 | 31.2k | error)) { |
69 | 4.40k | g_prefix_error(error, "failed to parse EFI file at 0x%x: ", (guint)offset); |
70 | 4.40k | return FALSE; |
71 | 4.40k | } |
72 | 26.8k | fu_firmware_set_offset(firmware, offset); |
73 | 26.8k | if (!fu_firmware_add_image(firmware, img, error)) |
74 | 1 | return FALSE; |
75 | | |
76 | | /* next! */ |
77 | 26.8k | offset += fu_firmware_get_size(img); |
78 | 26.8k | } |
79 | | |
80 | | /* success */ |
81 | 1.25k | return TRUE; |
82 | 5.68k | } |
83 | | |
84 | | static GByteArray * |
85 | | fu_efi_filesystem_write(FuFirmware *firmware, GError **error) |
86 | 1.06k | { |
87 | 1.06k | g_autoptr(GByteArray) buf = g_byte_array_new(); |
88 | 1.06k | g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); |
89 | | |
90 | | /* sanity check */ |
91 | 1.06k | if (fu_firmware_get_alignment(firmware) > FU_FIRMWARE_ALIGNMENT_1M) { |
92 | 0 | g_set_error(error, |
93 | 0 | FWUPD_ERROR, |
94 | 0 | FWUPD_ERROR_INVALID_FILE, |
95 | 0 | "alignment invalid, got 0x%02x", |
96 | 0 | fu_firmware_get_alignment(firmware)); |
97 | 0 | return NULL; |
98 | 0 | } |
99 | | |
100 | | /* add each file */ |
101 | 8.18k | for (guint i = 0; i < images->len; i++) { |
102 | 7.53k | FuFirmware *img = g_ptr_array_index(images, i); |
103 | 7.53k | g_autoptr(GBytes) blob = NULL; |
104 | 7.53k | fu_firmware_set_offset(img, buf->len); |
105 | 7.53k | blob = fu_firmware_write(img, error); |
106 | 7.53k | if (blob == NULL) |
107 | 410 | return NULL; |
108 | 7.12k | fu_byte_array_append_bytes(buf, blob); |
109 | 7.12k | fu_byte_array_align_up(buf, fu_firmware_get_alignment(firmware), 0xFF); |
110 | | |
111 | | /* sanity check */ |
112 | 7.12k | if (buf->len > FU_EFI_FILESYSTEM_SIZE_MAX) { |
113 | 0 | g_set_error(error, |
114 | 0 | FWUPD_ERROR, |
115 | 0 | FWUPD_ERROR_INVALID_FILE, |
116 | 0 | "EFI filesystem too large, 0x%02x > 0x%02x", |
117 | 0 | (guint)buf->len, |
118 | 0 | (guint)FU_EFI_FILESYSTEM_SIZE_MAX); |
119 | 0 | return NULL; |
120 | 0 | } |
121 | 7.12k | } |
122 | | |
123 | | /* success */ |
124 | 650 | return g_steal_pointer(&buf); |
125 | 1.06k | } |
126 | | |
127 | | static void |
128 | | fu_efi_filesystem_init(FuEfiFilesystem *self) |
129 | 5.68k | { |
130 | 5.68k | #ifdef HAVE_FUZZER |
131 | | /* if fuzzing, artificially limit the number of files to avoid using large amounts of RSS |
132 | | * when printing the FuEfiFilesystem XML output */ |
133 | 5.68k | fu_firmware_set_images_max(FU_FIRMWARE(self), 50); |
134 | | #else |
135 | | fu_firmware_set_images_max(FU_FIRMWARE(self), FU_EFI_FILESYSTEM_FILES_MAX); |
136 | | #endif |
137 | 5.68k | fu_firmware_set_alignment(FU_FIRMWARE(self), FU_FIRMWARE_ALIGNMENT_8); |
138 | 5.68k | g_type_ensure(FU_TYPE_EFI_FILE); |
139 | 5.68k | } |
140 | | |
141 | | static void |
142 | | fu_efi_filesystem_class_init(FuEfiFilesystemClass *klass) |
143 | 2 | { |
144 | 2 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
145 | 2 | firmware_class->parse = fu_efi_filesystem_parse; |
146 | 2 | firmware_class->write = fu_efi_filesystem_write; |
147 | 2 | } |
148 | | |
149 | | /** |
150 | | * fu_efi_filesystem_new: |
151 | | * |
152 | | * Creates a new #FuFirmware |
153 | | * |
154 | | * Since: 2.0.0 |
155 | | **/ |
156 | | FuFirmware * |
157 | | fu_efi_filesystem_new(void) |
158 | 2.17k | { |
159 | 2.17k | return FU_FIRMWARE(g_object_new(FU_TYPE_EFI_FILESYSTEM, NULL)); |
160 | 2.17k | } |