/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-common.h" |
11 | | #include "fu-efi-file.h" |
12 | | #include "fu-efi-filesystem.h" |
13 | | #include "fu-input-stream.h" |
14 | | #include "fu-partial-input-stream.h" |
15 | | |
16 | | /** |
17 | | * FuEfiFilesystem: |
18 | | * |
19 | | * A UEFI filesystem. |
20 | | * |
21 | | * See also: [class@FuFirmware] |
22 | | */ |
23 | | |
24 | 46.6k | G_DEFINE_TYPE(FuEfiFilesystem, fu_efi_filesystem, FU_TYPE_FIRMWARE) |
25 | 46.6k | |
26 | 46.6k | #define FU_EFI_FILESYSTEM_FILES_MAX 10000 |
27 | 46.6k | #define FU_EFI_FILESYSTEM_SIZE_MAX (256 * FU_MB) |
28 | | |
29 | | static gboolean |
30 | | fu_efi_filesystem_parse(FuFirmware *firmware, |
31 | | GInputStream *stream, |
32 | | FuFirmwareParseFlags flags, |
33 | | GError **error) |
34 | 5.03k | { |
35 | 5.03k | gsize offset = 0; |
36 | 5.03k | gsize streamsz = 0; |
37 | 5.03k | if (!fu_input_stream_size(stream, &streamsz, error)) |
38 | 0 | return FALSE; |
39 | 10.3k | while (offset < streamsz) { |
40 | 9.47k | g_autoptr(FuFirmware) img = fu_efi_file_new(); |
41 | 9.47k | g_autoptr(GInputStream) stream_tmp = NULL; |
42 | 9.47k | gboolean is_freespace = TRUE; |
43 | | |
44 | | /* ignore free space */ |
45 | 17.3k | for (guint i = 0; i < 0x18; i++) { |
46 | 17.3k | guint8 tmp = 0; |
47 | 17.3k | if (!fu_input_stream_read_u8(stream, offset + i, &tmp, error)) |
48 | 33 | return FALSE; |
49 | 17.2k | if (tmp != 0xff) { |
50 | 9.39k | is_freespace = FALSE; |
51 | 9.39k | break; |
52 | 9.39k | } |
53 | 17.2k | } |
54 | 9.44k | if (is_freespace) { |
55 | 52 | g_debug("ignoring free space @0x%x of 0x%x", |
56 | 52 | (guint)offset, |
57 | 52 | (guint)streamsz); |
58 | 52 | break; |
59 | 52 | } |
60 | 9.39k | stream_tmp = fu_partial_input_stream_new(stream, offset, streamsz - offset, error); |
61 | 9.39k | if (stream_tmp == NULL) { |
62 | 0 | g_prefix_error_literal(error, "failed to cut EFI file: "); |
63 | 0 | return FALSE; |
64 | 0 | } |
65 | 9.39k | if (!fu_firmware_parse_stream(img, |
66 | 9.39k | stream_tmp, |
67 | 9.39k | 0x0, |
68 | 9.39k | flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, |
69 | 9.39k | error)) { |
70 | 4.03k | g_prefix_error(error, "failed to parse EFI file at 0x%x: ", (guint)offset); |
71 | 4.03k | return FALSE; |
72 | 4.03k | } |
73 | 5.35k | if (fu_firmware_get_size(img) == 0) { |
74 | 0 | g_set_error_literal(error, |
75 | 0 | FWUPD_ERROR, |
76 | 0 | FWUPD_ERROR_INVALID_FILE, |
77 | 0 | "EFI file has invalid size of 0"); |
78 | 0 | return FALSE; |
79 | 0 | } |
80 | 5.35k | fu_firmware_set_offset(firmware, offset); |
81 | 5.35k | if (!fu_firmware_add_image(firmware, img, error)) |
82 | 1 | return FALSE; |
83 | | |
84 | | /* next! */ |
85 | 5.35k | if (!fu_size_checked_inc(&offset, fu_firmware_get_size(img), error)) |
86 | 0 | return FALSE; |
87 | 5.35k | } |
88 | | |
89 | | /* success */ |
90 | 961 | return TRUE; |
91 | 5.03k | } |
92 | | |
93 | | static GByteArray * |
94 | | fu_efi_filesystem_write(FuFirmware *firmware, GError **error) |
95 | 885 | { |
96 | 885 | g_autoptr(GByteArray) buf = g_byte_array_new(); |
97 | 885 | g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); |
98 | | |
99 | | /* sanity check */ |
100 | 885 | if (fu_firmware_get_alignment(firmware) > FU_FIRMWARE_ALIGNMENT_1M) { |
101 | 0 | g_set_error(error, |
102 | 0 | FWUPD_ERROR, |
103 | 0 | FWUPD_ERROR_INVALID_FILE, |
104 | 0 | "alignment invalid, got 0x%02x", |
105 | 0 | fu_firmware_get_alignment(firmware)); |
106 | 0 | return NULL; |
107 | 0 | } |
108 | | |
109 | | /* add each file */ |
110 | 3.05k | for (guint i = 0; i < images->len; i++) { |
111 | 2.42k | FuFirmware *img = g_ptr_array_index(images, i); |
112 | 2.42k | g_autoptr(GBytes) blob = NULL; |
113 | 2.42k | fu_firmware_set_offset(img, buf->len); |
114 | 2.42k | blob = fu_firmware_write(img, error); |
115 | 2.42k | if (blob == NULL) |
116 | 258 | return NULL; |
117 | 2.17k | fu_byte_array_append_bytes(buf, blob); |
118 | 2.17k | fu_byte_array_align_up(buf, fu_firmware_get_alignment(firmware), 0xFF); |
119 | | |
120 | | /* sanity check */ |
121 | 2.17k | if (buf->len > FU_EFI_FILESYSTEM_SIZE_MAX) { |
122 | 0 | g_set_error(error, |
123 | 0 | FWUPD_ERROR, |
124 | 0 | FWUPD_ERROR_INVALID_FILE, |
125 | 0 | "EFI filesystem too large, 0x%02x > 0x%02x", |
126 | 0 | (guint)buf->len, |
127 | 0 | (guint)FU_EFI_FILESYSTEM_SIZE_MAX); |
128 | 0 | return NULL; |
129 | 0 | } |
130 | 2.17k | } |
131 | | |
132 | | /* success */ |
133 | 627 | return g_steal_pointer(&buf); |
134 | 885 | } |
135 | | |
136 | | static void |
137 | | fu_efi_filesystem_init(FuEfiFilesystem *self) |
138 | 5.03k | { |
139 | 5.03k | #ifdef HAVE_FUZZER |
140 | | /* if fuzzing, artificially limit the number of files to avoid using large amounts of RSS |
141 | | * when printing the FuEfiFilesystem XML output */ |
142 | 5.03k | fu_firmware_set_images_max(FU_FIRMWARE(self), 50); |
143 | 5.03k | fu_firmware_set_size_max(FU_FIRMWARE(self), 1 * FU_MB); |
144 | | #else |
145 | | fu_firmware_set_images_max(FU_FIRMWARE(self), FU_EFI_FILESYSTEM_FILES_MAX); |
146 | | fu_firmware_set_size_max(FU_FIRMWARE(self), 1 * FU_GB); |
147 | | #endif |
148 | 5.03k | fu_firmware_set_alignment(FU_FIRMWARE(self), FU_FIRMWARE_ALIGNMENT_8); |
149 | 5.03k | fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_EFI_FILESYSTEM); |
150 | 5.03k | fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_EFI_FILE); |
151 | 5.03k | } |
152 | | |
153 | | static void |
154 | | fu_efi_filesystem_class_init(FuEfiFilesystemClass *klass) |
155 | 2 | { |
156 | 2 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
157 | 2 | firmware_class->parse = fu_efi_filesystem_parse; |
158 | 2 | firmware_class->write = fu_efi_filesystem_write; |
159 | 2 | } |
160 | | |
161 | | /** |
162 | | * fu_efi_filesystem_new: |
163 | | * |
164 | | * Creates a new #FuFirmware |
165 | | * |
166 | | * Since: 2.0.0 |
167 | | **/ |
168 | | FuFirmware * |
169 | | fu_efi_filesystem_new(void) |
170 | 1.34k | { |
171 | 1.34k | return FU_FIRMWARE(g_object_new(FU_TYPE_EFI_FILESYSTEM, NULL)); |
172 | 1.34k | } |