/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 | 5.56k | G_DEFINE_TYPE(FuEfiFilesystem, fu_efi_filesystem, FU_TYPE_FIRMWARE) |
25 | 5.56k | |
26 | 5.56k | #define FU_EFI_FILESYSTEM_FILES_MAX 10000 |
27 | 6.96k | #define FU_EFI_FILESYSTEM_SIZE_MAX (256 * FU_MB) |
28 | | |
29 | | static gboolean |
30 | | fu_efi_filesystem_parse(FuFirmware *firmware, |
31 | | FuInputStream *stream, |
32 | | FuFirmwareParseFlags flags, |
33 | | GError **error) |
34 | 5.55k | { |
35 | 5.55k | gsize offset = 0; |
36 | 5.55k | gsize streamsz = 0; |
37 | 5.55k | if (!fu_input_stream_size(stream, &streamsz, error)) |
38 | 0 | return FALSE; |
39 | 19.2k | while (offset < streamsz) { |
40 | 18.4k | g_autoptr(FuFirmware) img = fu_efi_file_new(); |
41 | 18.4k | g_autoptr(FuInputStream) stream_tmp = NULL; |
42 | 18.4k | gboolean is_freespace = TRUE; |
43 | | |
44 | | /* ignore free space */ |
45 | 32.2k | for (guint i = 0; i < 0x18; i++) { |
46 | 31.9k | guint8 tmp = 0; |
47 | 31.9k | if (!fu_input_stream_read_u8(stream, offset + i, &tmp, error)) |
48 | 35 | return FALSE; |
49 | 31.9k | if (tmp != 0xff) { |
50 | 18.0k | is_freespace = FALSE; |
51 | 18.0k | break; |
52 | 18.0k | } |
53 | 31.9k | } |
54 | 18.3k | if (is_freespace) { |
55 | 336 | g_debug("ignoring free space @0x%x of 0x%x", |
56 | 336 | (guint)offset, |
57 | 336 | (guint)streamsz); |
58 | 336 | break; |
59 | 336 | } |
60 | 18.0k | stream_tmp = fu_partial_input_stream_new(stream, offset, streamsz - offset, error); |
61 | 18.0k | if (stream_tmp == NULL) { |
62 | 0 | g_prefix_error_literal(error, "failed to cut EFI file: "); |
63 | 0 | return FALSE; |
64 | 0 | } |
65 | 18.0k | if (!fu_firmware_parse_stream(img, |
66 | 18.0k | stream_tmp, |
67 | 18.0k | 0x0, |
68 | 18.0k | flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, |
69 | 18.0k | error)) { |
70 | 4.39k | g_prefix_error(error, "failed to parse EFI file at 0x%x: ", (guint)offset); |
71 | 4.39k | return FALSE; |
72 | 4.39k | } |
73 | 13.6k | 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 | 13.6k | fu_firmware_set_offset(firmware, offset); |
81 | 13.6k | if (!fu_firmware_add_image(firmware, img, error)) |
82 | 3 | return FALSE; |
83 | | |
84 | | /* next! */ |
85 | 13.6k | if (!fu_size_checked_inc(&offset, fu_firmware_get_size(img), error)) |
86 | 0 | return FALSE; |
87 | 13.6k | } |
88 | | |
89 | | /* success */ |
90 | 1.11k | return TRUE; |
91 | 5.55k | } |
92 | | |
93 | | static GByteArray * |
94 | | fu_efi_filesystem_write(FuFirmware *firmware, GError **error) |
95 | 1.01k | { |
96 | 1.01k | g_autoptr(GByteArray) buf = g_byte_array_new(); |
97 | 1.01k | g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); |
98 | | |
99 | | /* sanity check */ |
100 | 1.01k | 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 | 7.98k | for (guint i = 0; i < images->len; i++) { |
111 | 6.96k | FuFirmware *img = g_ptr_array_index(images, i); |
112 | 6.96k | g_autoptr(GByteArray) buf_tmp = NULL; |
113 | | |
114 | 6.96k | fu_firmware_set_offset(img, buf->len); |
115 | 6.96k | buf_tmp = fu_firmware_write_array(img, error); |
116 | 6.96k | if (buf_tmp == NULL) |
117 | 0 | return NULL; |
118 | 6.96k | fu_byte_array_append_array(buf, buf_tmp); |
119 | 6.96k | fu_byte_array_align_up(buf, fu_firmware_get_alignment(firmware), 0xFF); |
120 | | |
121 | | /* sanity check */ |
122 | 6.96k | if (buf->len > FU_EFI_FILESYSTEM_SIZE_MAX) { |
123 | 0 | g_set_error(error, |
124 | 0 | FWUPD_ERROR, |
125 | 0 | FWUPD_ERROR_INVALID_FILE, |
126 | 0 | "EFI filesystem too large, 0x%02x > 0x%02x", |
127 | 0 | buf->len, |
128 | 0 | (guint)FU_EFI_FILESYSTEM_SIZE_MAX); |
129 | 0 | return NULL; |
130 | 0 | } |
131 | 6.96k | } |
132 | | |
133 | | /* success */ |
134 | 1.01k | return g_steal_pointer(&buf); |
135 | 1.01k | } |
136 | | |
137 | | static void |
138 | | fu_efi_filesystem_init(FuEfiFilesystem *self) |
139 | 5.55k | { |
140 | 5.55k | fu_firmware_set_alignment(FU_FIRMWARE(self), FU_FIRMWARE_ALIGNMENT_8); |
141 | 5.55k | } |
142 | | |
143 | | static void |
144 | | fu_efi_filesystem_class_init(FuEfiFilesystemClass *klass) |
145 | 3 | { |
146 | 3 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
147 | 3 | fu_firmware_add_image_gtype(firmware_class, FU_TYPE_EFI_FILESYSTEM); |
148 | 3 | fu_firmware_add_image_gtype(firmware_class, FU_TYPE_EFI_FILE); |
149 | 3 | firmware_class->parse = fu_efi_filesystem_parse; |
150 | 3 | firmware_class->write = fu_efi_filesystem_write; |
151 | 3 | #ifdef HAVE_FUZZER |
152 | 3 | fu_firmware_set_images_max(firmware_class, 50); |
153 | 3 | fu_firmware_set_size_max(firmware_class, 1 * FU_MB); |
154 | | #else |
155 | | fu_firmware_set_images_max(firmware_class, FU_EFI_FILESYSTEM_FILES_MAX); |
156 | | fu_firmware_set_size_max(firmware_class, 1 * FU_GB); |
157 | | #endif |
158 | 3 | } |
159 | | |
160 | | /** |
161 | | * fu_efi_filesystem_new: |
162 | | * |
163 | | * Creates a new #FuFirmware |
164 | | * |
165 | | * Since: 2.0.0 |
166 | | **/ |
167 | | FuFirmware * |
168 | | fu_efi_filesystem_new(void) |
169 | 2.48k | { |
170 | 2.48k | return FU_FIRMWARE(g_object_new(FU_TYPE_EFI_FILESYSTEM, NULL)); |
171 | 2.48k | } |