/src/fwupd/libfwupdplugin/fu-cab-firmware.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2023 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | 0 | #define G_LOG_DOMAIN "FuCabFirmware" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include <zlib.h> |
12 | | |
13 | | #include "fu-byte-array.h" |
14 | | #include "fu-cab-firmware-private.h" |
15 | | #include "fu-cab-image.h" |
16 | | #include "fu-cab-struct.h" |
17 | | #include "fu-chunk-array.h" |
18 | | #include "fu-common.h" |
19 | | #include "fu-composite-input-stream.h" |
20 | | #include "fu-input-stream.h" |
21 | | #include "fu-mem-private.h" |
22 | | #include "fu-partial-input-stream.h" |
23 | | #include "fu-string.h" |
24 | | |
25 | | typedef struct { |
26 | | gboolean compressed; |
27 | | gboolean only_basename; |
28 | | } FuCabFirmwarePrivate; |
29 | | |
30 | | G_DEFINE_TYPE_WITH_PRIVATE(FuCabFirmware, fu_cab_firmware, FU_TYPE_FIRMWARE) |
31 | 21.4k | #define GET_PRIVATE(o) (fu_cab_firmware_get_instance_private(o)) |
32 | | |
33 | 2.29k | #define FU_CAB_FIRMWARE_MAX_FILES 1024 |
34 | 2.33k | #define FU_CAB_FIRMWARE_MAX_FOLDERS 64 |
35 | | |
36 | 2.20k | #define FU_CAB_FIRMWARE_DECOMPRESS_BUFSZ 0x4000 /* bytes */ |
37 | | |
38 | | /** |
39 | | * fu_cab_firmware_get_compressed: |
40 | | * @self: a #FuCabFirmware |
41 | | * |
42 | | * Gets if the cabinet archive should be compressed. |
43 | | * |
44 | | * Returns: boolean |
45 | | * |
46 | | * Since: 1.9.7 |
47 | | **/ |
48 | | gboolean |
49 | | fu_cab_firmware_get_compressed(FuCabFirmware *self) |
50 | 0 | { |
51 | 0 | FuCabFirmwarePrivate *priv = GET_PRIVATE(self); |
52 | 0 | g_return_val_if_fail(FU_IS_CAB_FIRMWARE(self), FALSE); |
53 | 0 | return priv->compressed; |
54 | 0 | } |
55 | | |
56 | | /** |
57 | | * fu_cab_firmware_set_compressed: |
58 | | * @self: a #FuCabFirmware |
59 | | * @compressed: boolean |
60 | | * |
61 | | * Sets if the cabinet archive should be compressed. |
62 | | * |
63 | | * Since: 1.9.7 |
64 | | **/ |
65 | | void |
66 | | fu_cab_firmware_set_compressed(FuCabFirmware *self, gboolean compressed) |
67 | 0 | { |
68 | 0 | FuCabFirmwarePrivate *priv = GET_PRIVATE(self); |
69 | 0 | g_return_if_fail(FU_IS_CAB_FIRMWARE(self)); |
70 | 0 | priv->compressed = compressed; |
71 | 0 | } |
72 | | |
73 | | /** |
74 | | * fu_cab_firmware_get_only_basename: |
75 | | * @self: a #FuCabFirmware |
76 | | * |
77 | | * Gets if the cabinet archive filenames should have the path component removed. |
78 | | * |
79 | | * Returns: boolean |
80 | | * |
81 | | * Since: 1.9.7 |
82 | | **/ |
83 | | gboolean |
84 | | fu_cab_firmware_get_only_basename(FuCabFirmware *self) |
85 | 0 | { |
86 | 0 | FuCabFirmwarePrivate *priv = GET_PRIVATE(self); |
87 | 0 | g_return_val_if_fail(FU_IS_CAB_FIRMWARE(self), FALSE); |
88 | 0 | return priv->only_basename; |
89 | 0 | } |
90 | | |
91 | | /** |
92 | | * fu_cab_firmware_set_only_basename: |
93 | | * @self: a #FuCabFirmware |
94 | | * @only_basename: boolean |
95 | | * |
96 | | * Sets if the cabinet archive filenames should have the path component removed. |
97 | | * |
98 | | * Since: 1.9.7 |
99 | | **/ |
100 | | void |
101 | | fu_cab_firmware_set_only_basename(FuCabFirmware *self, gboolean only_basename) |
102 | 0 | { |
103 | 0 | FuCabFirmwarePrivate *priv = GET_PRIVATE(self); |
104 | 0 | g_return_if_fail(FU_IS_CAB_FIRMWARE(self)); |
105 | 0 | priv->only_basename = only_basename; |
106 | 0 | } |
107 | | |
108 | | typedef struct { |
109 | | GInputStream *stream; |
110 | | FuFirmwareParseFlags parse_flags; |
111 | | gsize rsvd_folder; |
112 | | gsize rsvd_block; |
113 | | gsize size_total; |
114 | | FuCabCompression compression; |
115 | | GPtrArray *folder_data; /* of FuCompositeInputStream */ |
116 | | z_stream zstrm; |
117 | | guint8 *decompress_buf; |
118 | | gsize decompress_bufsz; |
119 | | gsize ndatabsz; |
120 | | } FuCabFirmwareParseHelper; |
121 | | |
122 | | static void |
123 | | fu_cab_firmware_parse_helper_free(FuCabFirmwareParseHelper *helper) |
124 | 2.20k | { |
125 | 2.20k | inflateEnd(&helper->zstrm); |
126 | 2.20k | if (helper->stream != NULL) |
127 | 2.20k | g_object_unref(helper->stream); |
128 | 2.20k | if (helper->folder_data != NULL) |
129 | 2.20k | g_ptr_array_unref(helper->folder_data); |
130 | 2.20k | g_free(helper->decompress_buf); |
131 | 2.20k | g_free(helper); |
132 | 2.20k | } |
133 | | |
134 | | G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCabFirmwareParseHelper, fu_cab_firmware_parse_helper_free) |
135 | | |
136 | | /* compute the MS cabinet checksum */ |
137 | | gboolean |
138 | | fu_cab_firmware_compute_checksum(const guint8 *buf, gsize bufsz, guint32 *checksum, GError **error) |
139 | 4.47k | { |
140 | 4.47k | guint32 tmp = *checksum; |
141 | 2.51M | for (gsize i = 0; i < bufsz; i += 4) { |
142 | 2.50M | gsize chunksz = bufsz - i; |
143 | 2.50M | if (G_LIKELY(chunksz >= 4)) { |
144 | | /* 3,2,1,0 */ |
145 | 2.50M | tmp ^= ((guint32)buf[i + 3] << 24) | ((guint32)buf[i + 2] << 16) | |
146 | 2.50M | ((guint32)buf[i + 1] << 8) | (guint32)buf[i + 0]; |
147 | 2.50M | } else if (chunksz == 3) { |
148 | | /* 0,1,2 -- yes, weird */ |
149 | 599 | tmp ^= ((guint32)buf[i + 0] << 16) | ((guint32)buf[i + 1] << 8) | |
150 | 599 | (guint32)buf[i + 2]; |
151 | 1.40k | } else if (chunksz == 2) { |
152 | | /* 0,1 -- yes, weird */ |
153 | 590 | tmp ^= ((guint32)buf[i + 0] << 8) | (guint32)buf[i + 1]; |
154 | 815 | } else { |
155 | | /* 0 */ |
156 | 815 | tmp ^= (guint32)buf[i + 0]; |
157 | 815 | } |
158 | 2.50M | } |
159 | 4.47k | *checksum = tmp; |
160 | 4.47k | return TRUE; |
161 | 4.47k | } |
162 | | |
163 | | static gboolean |
164 | | fu_cab_firmware_compute_checksum_stream_cb(const guint8 *buf, |
165 | | gsize bufsz, |
166 | | gpointer user_data, |
167 | | GError **error) |
168 | 2.31k | { |
169 | 2.31k | guint32 *checksum = (guint32 *)user_data; |
170 | 2.31k | return fu_cab_firmware_compute_checksum(buf, bufsz, checksum, error); |
171 | 2.31k | } |
172 | | |
173 | | static voidpf |
174 | | fu_cab_firmware_zalloc(voidpf opaque, uInt items, uInt size) |
175 | 2.73k | { |
176 | 2.73k | return g_malloc0_n(items, size); |
177 | 2.73k | } |
178 | | |
179 | | static void |
180 | | fu_cab_firmware_zfree(voidpf opaque, voidpf address) |
181 | 2.73k | { |
182 | 2.73k | g_free(address); |
183 | 2.73k | } |
184 | | |
185 | | typedef z_stream z_stream_deflater; |
186 | | |
187 | | static void |
188 | | fu_cab_firmware_zstream_deflater_free(z_stream_deflater *zstrm) |
189 | 0 | { |
190 | 0 | deflateEnd(zstrm); |
191 | 0 | } |
192 | | |
193 | | G_DEFINE_AUTOPTR_CLEANUP_FUNC(z_stream_deflater, fu_cab_firmware_zstream_deflater_free) |
194 | | |
195 | | static gboolean |
196 | | fu_cab_firmware_parse_data(FuCabFirmware *self, |
197 | | FuCabFirmwareParseHelper *helper, |
198 | | gsize *offset, |
199 | | GInputStream *folder_data, |
200 | | GError **error) |
201 | 702k | { |
202 | 702k | gsize blob_comp; |
203 | 702k | gsize blob_uncomp; |
204 | 702k | gsize hdr_sz; |
205 | 702k | gsize size_max = fu_firmware_get_size_max(FU_FIRMWARE(self)); |
206 | 702k | g_autoptr(FuStructCabData) st = NULL; |
207 | 702k | g_autoptr(GInputStream) partial_stream = NULL; |
208 | | |
209 | | /* parse header */ |
210 | 702k | st = fu_struct_cab_data_parse_stream(helper->stream, *offset, error); |
211 | 702k | if (st == NULL) |
212 | 142 | return FALSE; |
213 | | |
214 | | /* sanity check */ |
215 | 701k | blob_comp = fu_struct_cab_data_get_comp(st); |
216 | 701k | blob_uncomp = fu_struct_cab_data_get_uncomp(st); |
217 | 701k | if (helper->compression == FU_CAB_COMPRESSION_NONE && blob_comp != blob_uncomp) { |
218 | 103 | g_set_error_literal(error, |
219 | 103 | FWUPD_ERROR, |
220 | 103 | FWUPD_ERROR_NOT_SUPPORTED, |
221 | 103 | "mismatched compressed data"); |
222 | 103 | return FALSE; |
223 | 103 | } |
224 | 701k | helper->size_total += blob_uncomp; |
225 | 701k | if (size_max > 0 && helper->size_total > size_max) { |
226 | 0 | g_autofree gchar *sz_val = g_format_size(helper->size_total); |
227 | 0 | g_autofree gchar *sz_max = g_format_size(size_max); |
228 | 0 | g_set_error(error, |
229 | 0 | FWUPD_ERROR, |
230 | 0 | FWUPD_ERROR_INVALID_DATA, |
231 | 0 | "uncompressed data too large (%s, limit %s)", |
232 | 0 | sz_val, |
233 | 0 | sz_max); |
234 | 0 | return FALSE; |
235 | 0 | } |
236 | | |
237 | 701k | hdr_sz = st->len + helper->rsvd_block; |
238 | | |
239 | | /* verify checksum */ |
240 | 701k | partial_stream = |
241 | 701k | fu_partial_input_stream_new(helper->stream, *offset + hdr_sz, blob_comp, error); |
242 | 701k | if (partial_stream == NULL) { |
243 | 51 | g_prefix_error_literal(error, "failed to cut cabinet checksum: "); |
244 | 51 | return FALSE; |
245 | 51 | } |
246 | 701k | if ((helper->parse_flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { |
247 | 239k | guint32 checksum = fu_struct_cab_data_get_checksum(st); |
248 | 239k | if (checksum != 0) { |
249 | 2.15k | guint32 checksum_actual = 0; |
250 | 2.15k | g_autoptr(GByteArray) hdr = g_byte_array_new(); |
251 | | |
252 | 2.15k | if (!fu_input_stream_chunkify(partial_stream, |
253 | 2.15k | fu_cab_firmware_compute_checksum_stream_cb, |
254 | 2.15k | &checksum_actual, |
255 | 2.15k | error)) |
256 | 0 | return FALSE; |
257 | 2.15k | fu_byte_array_append_uint16(hdr, blob_comp, G_LITTLE_ENDIAN); |
258 | 2.15k | fu_byte_array_append_uint16(hdr, blob_uncomp, G_LITTLE_ENDIAN); |
259 | 2.15k | if (!fu_cab_firmware_compute_checksum(hdr->data, |
260 | 2.15k | hdr->len, |
261 | 2.15k | &checksum_actual, |
262 | 2.15k | error)) |
263 | 0 | return FALSE; |
264 | 2.15k | if (checksum_actual != checksum) { |
265 | 708 | g_set_error(error, |
266 | 708 | FWUPD_ERROR, |
267 | 708 | FWUPD_ERROR_NOT_SUPPORTED, |
268 | 708 | "invalid checksum at 0x%x, expected 0x%x, got 0x%x", |
269 | 708 | (guint)*offset, |
270 | 708 | checksum, |
271 | 708 | checksum_actual); |
272 | 708 | return FALSE; |
273 | 708 | } |
274 | 2.15k | } |
275 | 239k | } |
276 | | |
277 | | /* decompress Zlib data after removing *another *header... */ |
278 | 701k | if (helper->compression == FU_CAB_COMPRESSION_MSZIP) { |
279 | 3.28k | int zret; |
280 | 3.28k | g_autofree gchar *kind = NULL; |
281 | 3.28k | g_autoptr(GByteArray) buf = g_byte_array_new(); |
282 | 3.28k | g_autoptr(GBytes) bytes_comp = NULL; |
283 | 3.28k | g_autoptr(GBytes) bytes_uncomp = NULL; |
284 | | |
285 | | /* check compressed header */ |
286 | 3.28k | bytes_comp = fu_input_stream_read_bytes(helper->stream, |
287 | 3.28k | *offset + hdr_sz, |
288 | 3.28k | blob_comp, |
289 | 3.28k | NULL, |
290 | 3.28k | error); |
291 | 3.28k | if (bytes_comp == NULL) |
292 | 8 | return FALSE; |
293 | 3.27k | kind = fu_memstrsafe(g_bytes_get_data(bytes_comp, NULL), |
294 | 3.27k | g_bytes_get_size(bytes_comp), |
295 | 3.27k | 0x0, |
296 | 3.27k | 2, |
297 | 3.27k | error); |
298 | 3.27k | if (kind == NULL) |
299 | 91 | return FALSE; |
300 | 3.18k | if (g_strcmp0(kind, "CK") != 0) { |
301 | 34 | g_set_error(error, |
302 | 34 | FWUPD_ERROR, |
303 | 34 | FWUPD_ERROR_NOT_SUPPORTED, |
304 | 34 | "compressed header invalid: %s", |
305 | 34 | kind); |
306 | 34 | return FALSE; |
307 | 34 | } |
308 | 3.15k | if (helper->decompress_buf == NULL) |
309 | 537 | helper->decompress_buf = g_malloc0(helper->decompress_bufsz); |
310 | 3.15k | helper->zstrm.avail_in = g_bytes_get_size(bytes_comp) - 2; |
311 | 3.15k | helper->zstrm.next_in = (z_const Bytef *)g_bytes_get_data(bytes_comp, NULL) + 2; |
312 | 8.03k | while (1) { |
313 | 8.03k | helper->zstrm.avail_out = helper->decompress_bufsz; |
314 | 8.03k | helper->zstrm.next_out = helper->decompress_buf; |
315 | 8.03k | zret = inflate(&helper->zstrm, Z_BLOCK); |
316 | 8.03k | if (zret == Z_STREAM_END) |
317 | 3.14k | break; |
318 | 4.89k | g_byte_array_append(buf, |
319 | 4.89k | helper->decompress_buf, |
320 | 4.89k | helper->decompress_bufsz - helper->zstrm.avail_out); |
321 | 4.89k | if (zret != Z_OK) { |
322 | 8 | g_set_error(error, |
323 | 8 | FWUPD_ERROR, |
324 | 8 | FWUPD_ERROR_NOT_SUPPORTED, |
325 | 8 | "inflate error @0x%x: %s", |
326 | 8 | (guint)*offset, |
327 | 8 | zError(zret)); |
328 | 8 | return FALSE; |
329 | 8 | } |
330 | 4.89k | } |
331 | 3.14k | zret = inflateReset(&helper->zstrm); |
332 | 3.14k | if (zret != Z_OK) { |
333 | 0 | g_set_error(error, |
334 | 0 | FWUPD_ERROR, |
335 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
336 | 0 | "failed to reset inflate: %s", |
337 | 0 | zError(zret)); |
338 | 0 | return FALSE; |
339 | 0 | } |
340 | 3.14k | zret = inflateSetDictionary(&helper->zstrm, buf->data, buf->len); |
341 | 3.14k | if (zret != Z_OK) { |
342 | 0 | g_set_error(error, |
343 | 0 | FWUPD_ERROR, |
344 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
345 | 0 | "failed to set inflate dictionary: %s", |
346 | 0 | zError(zret)); |
347 | 0 | return FALSE; |
348 | 0 | } |
349 | 3.14k | bytes_uncomp = |
350 | 3.14k | g_byte_array_free_to_bytes(g_steal_pointer(&buf)); /* nocheck:blocked */ |
351 | 3.14k | fu_composite_input_stream_add_bytes(FU_COMPOSITE_INPUT_STREAM(folder_data), |
352 | 3.14k | bytes_uncomp); |
353 | 697k | } else { |
354 | 697k | fu_composite_input_stream_add_partial_stream( |
355 | 697k | FU_COMPOSITE_INPUT_STREAM(folder_data), |
356 | 697k | FU_PARTIAL_INPUT_STREAM(partial_stream)); |
357 | 697k | } |
358 | | |
359 | | /* success */ |
360 | 700k | *offset += blob_comp + hdr_sz; |
361 | 700k | return TRUE; |
362 | 701k | } |
363 | | |
364 | | static gboolean |
365 | | fu_cab_firmware_parse_folder(FuCabFirmware *self, |
366 | | FuCabFirmwareParseHelper *helper, |
367 | | guint idx, |
368 | | gsize offset, |
369 | | GInputStream *folder_data, |
370 | | GError **error) |
371 | 6.60k | { |
372 | 6.60k | FuCabFirmwarePrivate *priv = GET_PRIVATE(self); |
373 | 6.60k | g_autoptr(GByteArray) st = NULL; |
374 | | |
375 | | /* parse header */ |
376 | 6.60k | st = fu_struct_cab_folder_parse_stream(helper->stream, offset, error); |
377 | 6.60k | if (st == NULL) |
378 | 134 | return FALSE; |
379 | | |
380 | | /* sanity check */ |
381 | 6.46k | if (fu_struct_cab_folder_get_ndatab(st) == 0) { |
382 | 13 | g_set_error_literal(error, |
383 | 13 | FWUPD_ERROR, |
384 | 13 | FWUPD_ERROR_NOT_SUPPORTED, |
385 | 13 | "no CFDATA blocks"); |
386 | 13 | return FALSE; |
387 | 13 | } |
388 | 6.45k | helper->compression = fu_struct_cab_folder_get_compression(st); |
389 | 6.45k | if (helper->compression != FU_CAB_COMPRESSION_NONE) |
390 | 3.38k | priv->compressed = TRUE; |
391 | 6.45k | if (helper->compression != FU_CAB_COMPRESSION_NONE && |
392 | 6.45k | helper->compression != FU_CAB_COMPRESSION_MSZIP) { |
393 | 69 | g_set_error(error, |
394 | 69 | FWUPD_ERROR, |
395 | 69 | FWUPD_ERROR_NOT_SUPPORTED, |
396 | 69 | "compression %s not supported", |
397 | 69 | fu_cab_compression_to_string(helper->compression)); |
398 | 69 | return FALSE; |
399 | 69 | } |
400 | | |
401 | | /* parse CDATA, either using the stream offset or the per-spec FuStructCabFolder.ndatab */ |
402 | 6.38k | if (helper->ndatabsz > 0) { |
403 | 0 | for (gsize off = fu_struct_cab_folder_get_offset(st); off < helper->ndatabsz;) { |
404 | 0 | if (!fu_cab_firmware_parse_data(self, helper, &off, folder_data, error)) |
405 | 0 | return FALSE; |
406 | 0 | } |
407 | 6.38k | } else { |
408 | 6.38k | gsize off = fu_struct_cab_folder_get_offset(st); |
409 | 707k | for (guint16 i = 0; i < fu_struct_cab_folder_get_ndatab(st); i++) { |
410 | 702k | if (!fu_cab_firmware_parse_data(self, helper, &off, folder_data, error)) |
411 | 1.14k | return FALSE; |
412 | 702k | } |
413 | 6.38k | } |
414 | | |
415 | | /* success */ |
416 | 5.24k | return TRUE; |
417 | 6.38k | } |
418 | | |
419 | | static gboolean |
420 | | fu_cab_firmware_parse_file(FuCabFirmware *self, |
421 | | FuCabFirmwareParseHelper *helper, |
422 | | gsize *offset, |
423 | | GError **error) |
424 | 14.7k | { |
425 | 14.7k | FuCabFirmwarePrivate *priv = GET_PRIVATE(self); |
426 | 14.7k | GInputStream *folder_data; |
427 | 14.7k | guint16 date; |
428 | 14.7k | guint16 index; |
429 | 14.7k | guint16 time; |
430 | 14.7k | g_autoptr(FuCabImage) img = fu_cab_image_new(); |
431 | 14.7k | g_autoptr(GByteArray) st = NULL; |
432 | 14.7k | g_autoptr(GDateTime) created = NULL; |
433 | 14.7k | g_autoptr(GInputStream) stream = NULL; |
434 | 14.7k | g_autoptr(GString) filename = g_string_new(NULL); |
435 | 14.7k | g_autoptr(GTimeZone) tz_utc = g_time_zone_new_utc(); |
436 | | |
437 | | /* parse header */ |
438 | 14.7k | st = fu_struct_cab_file_parse_stream(helper->stream, *offset, error); |
439 | 14.7k | if (st == NULL) |
440 | 405 | return FALSE; |
441 | 14.3k | fu_firmware_set_offset(FU_FIRMWARE(img), fu_struct_cab_file_get_uoffset(st)); |
442 | 14.3k | fu_firmware_set_size(FU_FIRMWARE(img), fu_struct_cab_file_get_usize(st)); |
443 | | |
444 | | /* sanity check */ |
445 | 14.3k | index = fu_struct_cab_file_get_index(st); |
446 | 14.3k | if (index >= helper->folder_data->len) { |
447 | 39 | g_set_error(error, |
448 | 39 | FWUPD_ERROR, |
449 | 39 | FWUPD_ERROR_NOT_SUPPORTED, |
450 | 39 | "failed to get folder data for 0x%x", |
451 | 39 | index); |
452 | 39 | return FALSE; |
453 | 39 | } |
454 | 14.2k | folder_data = g_ptr_array_index(helper->folder_data, index); |
455 | | |
456 | | /* parse filename */ |
457 | 14.2k | *offset += FU_STRUCT_CAB_FILE_SIZE; |
458 | 212k | for (guint i = 0; i < 255; i++) { |
459 | 212k | guint8 value = 0; |
460 | 212k | if (!fu_input_stream_read_u8(helper->stream, *offset + i, &value, error)) |
461 | 104 | return FALSE; |
462 | 212k | if (value == 0) |
463 | 13.6k | break; |
464 | 198k | if (!g_ascii_isprint((gchar)value)) { |
465 | 40 | g_set_error(error, |
466 | 40 | FWUPD_ERROR, |
467 | 40 | FWUPD_ERROR_NOT_SUPPORTED, |
468 | 40 | "non-ASCII filenames are not supported: 0x%02x", |
469 | 40 | value); |
470 | 40 | return FALSE; |
471 | 40 | } |
472 | | /* convert to UNIX path */ |
473 | 198k | if (value == '\\') |
474 | 6.74k | value = '/'; |
475 | 198k | g_string_append_c(filename, (gchar)value); |
476 | 198k | } |
477 | | |
478 | | /* add image */ |
479 | 14.1k | if (priv->only_basename) { |
480 | 0 | g_autofree gchar *id = g_path_get_basename(filename->str); |
481 | 0 | fu_firmware_set_id(FU_FIRMWARE(img), id); |
482 | 14.1k | } else { |
483 | 14.1k | fu_firmware_set_id(FU_FIRMWARE(img), filename->str); |
484 | 14.1k | } |
485 | 14.1k | stream = fu_partial_input_stream_new(folder_data, |
486 | 14.1k | fu_struct_cab_file_get_uoffset(st), |
487 | 14.1k | fu_struct_cab_file_get_usize(st), |
488 | 14.1k | error); |
489 | 14.1k | if (stream == NULL) { |
490 | 66 | g_prefix_error_literal(error, "failed to cut cabinet image: "); |
491 | 66 | return FALSE; |
492 | 66 | } |
493 | 14.0k | if (!fu_firmware_parse_stream(FU_FIRMWARE(img), stream, 0x0, helper->parse_flags, error)) |
494 | 4 | return FALSE; |
495 | 14.0k | if (!fu_firmware_add_image_full(FU_FIRMWARE(self), FU_FIRMWARE(img), error)) |
496 | 0 | return FALSE; |
497 | | |
498 | | /* set created date time */ |
499 | 14.0k | date = fu_struct_cab_file_get_date(st); |
500 | 14.0k | time = fu_struct_cab_file_get_time(st); |
501 | 14.0k | created = g_date_time_new(tz_utc, |
502 | 14.0k | 1980 + ((date & 0xFE00) >> 9), |
503 | 14.0k | (date & 0x01E0) >> 5, |
504 | 14.0k | date & 0x001F, |
505 | 14.0k | (time & 0xF800) >> 11, |
506 | 14.0k | (time & 0x07E0) >> 5, |
507 | 14.0k | (time & 0x001F) * 2); |
508 | 14.0k | fu_cab_image_set_created(img, created); |
509 | | |
510 | | /* offset to next entry */ |
511 | 14.0k | *offset += filename->len + 1; |
512 | 14.0k | return TRUE; |
513 | 14.0k | } |
514 | | |
515 | | static gboolean |
516 | | fu_cab_firmware_validate(FuFirmware *firmware, GInputStream *stream, gsize offset, GError **error) |
517 | 3.58M | { |
518 | 3.58M | return fu_struct_cab_header_validate_stream(stream, offset, error); |
519 | 3.58M | } |
520 | | |
521 | | static FuCabFirmwareParseHelper * |
522 | | fu_cab_firmware_parse_helper_new(GInputStream *stream, FuFirmwareParseFlags flags, GError **error) |
523 | 2.20k | { |
524 | 2.20k | int zret; |
525 | 2.20k | g_autoptr(FuCabFirmwareParseHelper) helper = g_new0(FuCabFirmwareParseHelper, 1); |
526 | | |
527 | | /* zlib */ |
528 | 2.20k | helper->zstrm.zalloc = fu_cab_firmware_zalloc; |
529 | 2.20k | helper->zstrm.zfree = fu_cab_firmware_zfree; |
530 | 2.20k | zret = inflateInit2(&helper->zstrm, -MAX_WBITS); |
531 | 2.20k | if (zret != Z_OK) { |
532 | 0 | g_set_error(error, |
533 | 0 | FWUPD_ERROR, |
534 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
535 | 0 | "failed to initialize inflate: %s", |
536 | 0 | zError(zret)); |
537 | 0 | return NULL; |
538 | 0 | } |
539 | | |
540 | 2.20k | helper->stream = g_object_ref(stream); |
541 | 2.20k | helper->parse_flags = flags; |
542 | 2.20k | helper->folder_data = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); |
543 | 2.20k | helper->decompress_bufsz = FU_CAB_FIRMWARE_DECOMPRESS_BUFSZ; |
544 | 2.20k | return g_steal_pointer(&helper); |
545 | 2.20k | } |
546 | | |
547 | | static gboolean |
548 | | fu_cab_firmware_parse(FuFirmware *firmware, |
549 | | GInputStream *stream, |
550 | | FuFirmwareParseFlags flags, |
551 | | GError **error) |
552 | 2.38k | { |
553 | 2.38k | FuCabFirmware *self = FU_CAB_FIRMWARE(firmware); |
554 | 2.38k | gsize off_cffile = 0; |
555 | 2.38k | gsize offset = 0; |
556 | 2.38k | gsize streamsz = 0; |
557 | 2.38k | g_autoptr(GByteArray) st = NULL; |
558 | 2.38k | g_autoptr(FuCabFirmwareParseHelper) helper = NULL; |
559 | | |
560 | | /* get size */ |
561 | 2.38k | if (!fu_input_stream_size(stream, &streamsz, error)) |
562 | 0 | return FALSE; |
563 | | |
564 | | /* parse header */ |
565 | 2.38k | st = fu_struct_cab_header_parse_stream(stream, offset, error); |
566 | 2.38k | if (st == NULL) |
567 | 0 | return FALSE; |
568 | | |
569 | | /* sanity checks */ |
570 | 2.38k | if (fu_struct_cab_header_get_size(st) < streamsz) { |
571 | 39 | g_set_error(error, |
572 | 39 | FWUPD_ERROR, |
573 | 39 | FWUPD_ERROR_NOT_SUPPORTED, |
574 | 39 | "buffer size 0x%x is less than stream size 0x%x", |
575 | 39 | (guint)streamsz, |
576 | 39 | fu_struct_cab_header_get_size(st)); |
577 | 39 | return FALSE; |
578 | 39 | } |
579 | 2.34k | if (fu_struct_cab_header_get_idx_cabinet(st) != 0) { |
580 | 32 | g_set_error_literal(error, |
581 | 32 | FWUPD_ERROR, |
582 | 32 | FWUPD_ERROR_NOT_SUPPORTED, |
583 | 32 | "chained archive not supported"); |
584 | 32 | return FALSE; |
585 | 32 | } |
586 | 2.31k | if (fu_struct_cab_header_get_nr_folders(st) == 0 || |
587 | 2.31k | fu_struct_cab_header_get_nr_files(st) == 0) { |
588 | 6 | g_set_error_literal(error, |
589 | 6 | FWUPD_ERROR, |
590 | 6 | FWUPD_ERROR_NOT_SUPPORTED, |
591 | 6 | "archive is empty"); |
592 | 6 | return FALSE; |
593 | 6 | } |
594 | 2.30k | if (fu_struct_cab_header_get_nr_folders(st) > FU_CAB_FIRMWARE_MAX_FOLDERS) { |
595 | 25 | g_set_error(error, |
596 | 25 | FWUPD_ERROR, |
597 | 25 | FWUPD_ERROR_NOT_SUPPORTED, |
598 | 25 | "too many CFFOLDERS, parsed %u and limit was %u", |
599 | 25 | fu_struct_cab_header_get_nr_folders(st), |
600 | 25 | (guint)FU_CAB_FIRMWARE_MAX_FOLDERS); |
601 | 25 | return FALSE; |
602 | 25 | } |
603 | 2.28k | if (fu_struct_cab_header_get_nr_files(st) > FU_CAB_FIRMWARE_MAX_FILES) { |
604 | 16 | g_set_error(error, |
605 | 16 | FWUPD_ERROR, |
606 | 16 | FWUPD_ERROR_NOT_SUPPORTED, |
607 | 16 | "too many CFFILES, parsed %u and limit was %u", |
608 | 16 | fu_struct_cab_header_get_nr_files(st), |
609 | 16 | (guint)FU_CAB_FIRMWARE_MAX_FILES); |
610 | 16 | return FALSE; |
611 | 16 | } |
612 | 2.26k | off_cffile = fu_struct_cab_header_get_off_cffile(st); |
613 | 2.26k | if (off_cffile > streamsz) { |
614 | 63 | g_set_error_literal(error, |
615 | 63 | FWUPD_ERROR, |
616 | 63 | FWUPD_ERROR_NOT_SUPPORTED, |
617 | 63 | "archive is corrupt"); |
618 | 63 | return FALSE; |
619 | 63 | } |
620 | | |
621 | | /* create helper */ |
622 | 2.20k | helper = fu_cab_firmware_parse_helper_new(stream, flags, error); |
623 | 2.20k | if (helper == NULL) |
624 | 0 | return FALSE; |
625 | | |
626 | | /* if the only folder is >= 2GB then FuStructCabFolder.ndatab will overflow */ |
627 | 2.20k | if (streamsz >= 0x8000 * 0xFFFF && fu_struct_cab_header_get_nr_folders(st) == 1) |
628 | 0 | helper->ndatabsz = streamsz; |
629 | | |
630 | | /* reserved sizes */ |
631 | 2.20k | offset += st->len; |
632 | 2.20k | if (fu_struct_cab_header_get_flags(st) & 0x0004) { |
633 | 88 | g_autoptr(GByteArray) st2 = NULL; |
634 | 88 | st2 = fu_struct_cab_header_reserve_parse_stream(stream, offset, error); |
635 | 88 | if (st2 == NULL) |
636 | 19 | return FALSE; |
637 | 69 | offset += st2->len; |
638 | 69 | offset += fu_struct_cab_header_reserve_get_rsvd_hdr(st2); |
639 | 69 | helper->rsvd_block = fu_struct_cab_header_reserve_get_rsvd_block(st2); |
640 | 69 | helper->rsvd_folder = fu_struct_cab_header_reserve_get_rsvd_folder(st2); |
641 | 69 | } |
642 | | |
643 | | /* parse CFFOLDER */ |
644 | 7.41k | for (guint i = 0; i < fu_struct_cab_header_get_nr_folders(st); i++) { |
645 | 6.60k | g_autoptr(GInputStream) folder_data = fu_composite_input_stream_new(); |
646 | 6.60k | if (!fu_cab_firmware_parse_folder(self, helper, i, offset, folder_data, error)) |
647 | 1.36k | return FALSE; |
648 | 5.24k | if (!fu_input_stream_size(folder_data, &streamsz, error)) |
649 | 0 | return FALSE; |
650 | 5.24k | if (streamsz == 0) { |
651 | 6 | g_set_error_literal(error, |
652 | 6 | FWUPD_ERROR, |
653 | 6 | FWUPD_ERROR_NOT_SUPPORTED, |
654 | 6 | "no folder data"); |
655 | 6 | return FALSE; |
656 | 6 | } |
657 | 5.23k | g_ptr_array_add(helper->folder_data, g_steal_pointer(&folder_data)); |
658 | 5.23k | offset += FU_STRUCT_CAB_FOLDER_SIZE + helper->rsvd_folder; |
659 | 5.23k | } |
660 | | |
661 | | /* parse CFFILEs */ |
662 | 14.8k | for (guint i = 0; i < fu_struct_cab_header_get_nr_files(st); i++) { |
663 | 14.7k | if (!fu_cab_firmware_parse_file(self, helper, &off_cffile, error)) |
664 | 658 | return FALSE; |
665 | 14.7k | } |
666 | | |
667 | | /* success */ |
668 | 157 | return TRUE; |
669 | 815 | } |
670 | | |
671 | | static GByteArray * |
672 | | fu_cab_firmware_write(FuFirmware *firmware, GError **error) |
673 | 157 | { |
674 | 157 | FuCabFirmware *self = FU_CAB_FIRMWARE(firmware); |
675 | 157 | FuCabFirmwarePrivate *priv = GET_PRIVATE(self); |
676 | 157 | gsize archive_size; |
677 | 157 | gsize offset; |
678 | 157 | guint32 index_into = 0; |
679 | 157 | g_autoptr(GByteArray) st_hdr = fu_struct_cab_header_new(); |
680 | 157 | g_autoptr(GByteArray) st_folder = fu_struct_cab_folder_new(); |
681 | 157 | g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); |
682 | 157 | g_autoptr(GByteArray) cfdata_linear = g_byte_array_new(); |
683 | 157 | g_autoptr(GBytes) cfdata_linear_blob = NULL; |
684 | 157 | g_autoptr(FuChunkArray) chunks = NULL; |
685 | 157 | g_autoptr(GPtrArray) chunks_zlib = |
686 | 157 | g_ptr_array_new_with_free_func((GDestroyNotify)g_byte_array_unref); |
687 | | |
688 | | /* create linear CFDATA block */ |
689 | 157 | for (guint i = 0; i < imgs->len; i++) { |
690 | 157 | FuFirmware *img = g_ptr_array_index(imgs, i); |
691 | 157 | const gchar *filename_win32 = fu_cab_image_get_win32_filename(FU_CAB_IMAGE(img)); |
692 | 157 | g_autoptr(GBytes) img_blob = NULL; |
693 | | |
694 | 157 | if (filename_win32 == NULL) { |
695 | 13 | g_set_error_literal(error, |
696 | 13 | FWUPD_ERROR, |
697 | 13 | FWUPD_ERROR_NOT_SUPPORTED, |
698 | 13 | "no image filename"); |
699 | 13 | return NULL; |
700 | 13 | } |
701 | 144 | img_blob = fu_firmware_get_bytes(img, error); |
702 | 144 | if (img_blob == NULL) |
703 | 144 | return NULL; |
704 | 0 | fu_byte_array_append_bytes(cfdata_linear, img_blob); |
705 | 0 | } |
706 | | |
707 | | /* chunkify and compress with a fixed size */ |
708 | 0 | if (cfdata_linear->len == 0) { |
709 | 0 | g_set_error_literal(error, |
710 | 0 | FWUPD_ERROR, |
711 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
712 | 0 | "no data to compress"); |
713 | 0 | return NULL; |
714 | 0 | } |
715 | 0 | cfdata_linear_blob = |
716 | 0 | g_byte_array_free_to_bytes(g_steal_pointer(&cfdata_linear)); /* nocheck:blocked */ |
717 | 0 | chunks = fu_chunk_array_new_from_bytes(cfdata_linear_blob, |
718 | 0 | FU_CHUNK_ADDR_OFFSET_NONE, |
719 | 0 | FU_CHUNK_PAGESZ_NONE, |
720 | 0 | 0x8000); |
721 | 0 | for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { |
722 | 0 | g_autoptr(FuChunk) chk = NULL; |
723 | 0 | g_autoptr(GByteArray) chunk_zlib = g_byte_array_new(); |
724 | 0 | g_autoptr(GByteArray) buf = g_byte_array_new(); |
725 | |
|
726 | 0 | chk = fu_chunk_array_index(chunks, i, error); |
727 | 0 | if (chk == NULL) |
728 | 0 | return NULL; |
729 | 0 | fu_byte_array_set_size(chunk_zlib, fu_chunk_get_data_sz(chk) * 2, 0x0); |
730 | 0 | if (priv->compressed) { |
731 | 0 | int zret; |
732 | 0 | z_stream zstrm = { |
733 | 0 | .zalloc = fu_cab_firmware_zalloc, |
734 | 0 | .zfree = fu_cab_firmware_zfree, |
735 | 0 | .opaque = Z_NULL, |
736 | 0 | .next_in = (guint8 *)fu_chunk_get_data(chk), |
737 | 0 | .avail_in = fu_chunk_get_data_sz(chk), |
738 | 0 | .next_out = chunk_zlib->data, |
739 | 0 | .avail_out = chunk_zlib->len, |
740 | 0 | }; |
741 | 0 | g_autoptr(z_stream_deflater) zstrm_deflater = &zstrm; |
742 | 0 | zret = deflateInit2(zstrm_deflater, |
743 | 0 | Z_DEFAULT_COMPRESSION, |
744 | 0 | Z_DEFLATED, |
745 | 0 | -15, |
746 | 0 | 8, |
747 | 0 | Z_DEFAULT_STRATEGY); |
748 | 0 | if (zret != Z_OK) { |
749 | 0 | g_set_error(error, |
750 | 0 | FWUPD_ERROR, |
751 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
752 | 0 | "failed to initialize deflate: %s", |
753 | 0 | zError(zret)); |
754 | 0 | return NULL; |
755 | 0 | } |
756 | 0 | zret = deflate(zstrm_deflater, Z_FINISH); |
757 | 0 | if (zret != Z_OK && zret != Z_STREAM_END) { |
758 | 0 | g_set_error(error, |
759 | 0 | FWUPD_ERROR, |
760 | 0 | FWUPD_ERROR_NOT_SUPPORTED, |
761 | 0 | "zlib deflate failed: %s", |
762 | 0 | zError(zret)); |
763 | 0 | return NULL; |
764 | 0 | } |
765 | 0 | fu_byte_array_append_uint8(buf, (guint8)'C'); |
766 | 0 | fu_byte_array_append_uint8(buf, (guint8)'K'); |
767 | 0 | g_byte_array_append(buf, chunk_zlib->data, zstrm.total_out); |
768 | 0 | } else { |
769 | 0 | g_byte_array_append(buf, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); |
770 | 0 | } |
771 | 0 | g_ptr_array_add(chunks_zlib, g_steal_pointer(&buf)); |
772 | 0 | } |
773 | | |
774 | | /* create header */ |
775 | 0 | archive_size = FU_STRUCT_CAB_HEADER_SIZE; |
776 | 0 | archive_size += FU_STRUCT_CAB_FOLDER_SIZE; |
777 | 0 | for (guint i = 0; i < imgs->len; i++) { |
778 | 0 | FuFirmware *img = g_ptr_array_index(imgs, i); |
779 | 0 | const gchar *filename_win32 = fu_cab_image_get_win32_filename(FU_CAB_IMAGE(img)); |
780 | 0 | archive_size += FU_STRUCT_CAB_FILE_SIZE + strlen(filename_win32) + 1; |
781 | 0 | } |
782 | 0 | for (guint i = 0; i < chunks_zlib->len; i++) { |
783 | 0 | GByteArray *chunk = g_ptr_array_index(chunks_zlib, i); |
784 | 0 | archive_size += FU_STRUCT_CAB_DATA_SIZE + chunk->len; |
785 | 0 | } |
786 | 0 | offset = FU_STRUCT_CAB_HEADER_SIZE; |
787 | 0 | offset += FU_STRUCT_CAB_FOLDER_SIZE; |
788 | 0 | fu_struct_cab_header_set_size(st_hdr, archive_size); |
789 | 0 | fu_struct_cab_header_set_off_cffile(st_hdr, offset); |
790 | 0 | fu_struct_cab_header_set_nr_files(st_hdr, imgs->len); |
791 | | |
792 | | /* create folder */ |
793 | 0 | for (guint i = 0; i < imgs->len; i++) { |
794 | 0 | FuFirmware *img = g_ptr_array_index(imgs, i); |
795 | 0 | const gchar *filename_win32 = fu_cab_image_get_win32_filename(FU_CAB_IMAGE(img)); |
796 | 0 | offset += FU_STRUCT_CAB_FILE_SIZE; |
797 | 0 | offset += strlen(filename_win32) + 1; |
798 | 0 | } |
799 | 0 | fu_struct_cab_folder_set_offset(st_folder, offset); |
800 | 0 | fu_struct_cab_folder_set_ndatab(st_folder, fu_chunk_array_length(chunks)); |
801 | 0 | fu_struct_cab_folder_set_compression(st_folder, |
802 | 0 | priv->compressed ? FU_CAB_COMPRESSION_MSZIP |
803 | 0 | : FU_CAB_COMPRESSION_NONE); |
804 | 0 | g_byte_array_append(st_hdr, st_folder->data, st_folder->len); |
805 | | |
806 | | /* create each CFFILE */ |
807 | 0 | for (guint i = 0; i < imgs->len; i++) { |
808 | 0 | FuFirmware *img = g_ptr_array_index(imgs, i); |
809 | 0 | FuCabFileAttribute fattr = FU_CAB_FILE_ATTRIBUTE_NONE; |
810 | 0 | GDateTime *created = fu_cab_image_get_created(FU_CAB_IMAGE(img)); |
811 | 0 | const gchar *filename_win32 = fu_cab_image_get_win32_filename(FU_CAB_IMAGE(img)); |
812 | 0 | g_autoptr(GByteArray) st_file = fu_struct_cab_file_new(); |
813 | 0 | g_autoptr(GBytes) img_blob = fu_firmware_get_bytes(img, NULL); |
814 | |
|
815 | 0 | if (!g_str_is_ascii(filename_win32)) |
816 | 0 | fattr |= FU_CAB_FILE_ATTRIBUTE_NAME_UTF8; |
817 | 0 | fu_struct_cab_file_set_fattr(st_file, fattr); |
818 | 0 | fu_struct_cab_file_set_usize(st_file, g_bytes_get_size(img_blob)); |
819 | 0 | fu_struct_cab_file_set_uoffset(st_file, index_into); |
820 | 0 | if (created != NULL) { |
821 | 0 | fu_struct_cab_file_set_date(st_file, |
822 | 0 | ((g_date_time_get_year(created) - 1980) << 9) + |
823 | 0 | (g_date_time_get_month(created) << 5) + |
824 | 0 | g_date_time_get_day_of_month(created)); |
825 | 0 | fu_struct_cab_file_set_time(st_file, |
826 | 0 | (g_date_time_get_hour(created) << 11) + |
827 | 0 | (g_date_time_get_minute(created) << 5) + |
828 | 0 | (g_date_time_get_second(created) / 2)); |
829 | 0 | } |
830 | 0 | g_byte_array_append(st_hdr, st_file->data, st_file->len); |
831 | |
|
832 | 0 | g_byte_array_append(st_hdr, (const guint8 *)filename_win32, strlen(filename_win32)); |
833 | 0 | fu_byte_array_append_uint8(st_hdr, 0x0); |
834 | 0 | index_into += g_bytes_get_size(img_blob); |
835 | 0 | } |
836 | | |
837 | | /* create each CFDATA */ |
838 | 0 | for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { |
839 | 0 | guint32 checksum = 0; |
840 | 0 | GByteArray *chunk_zlib = g_ptr_array_index(chunks_zlib, i); |
841 | 0 | g_autoptr(FuChunk) chk = NULL; |
842 | 0 | g_autoptr(GByteArray) hdr = g_byte_array_new(); |
843 | 0 | g_autoptr(GByteArray) st_data = fu_struct_cab_data_new(); |
844 | | |
845 | | /* prepare chunk */ |
846 | 0 | chk = fu_chunk_array_index(chunks, i, error); |
847 | 0 | if (chk == NULL) |
848 | 0 | return NULL; |
849 | | |
850 | | /* first do the 'checksum' on the data, then the partial header -- slightly crazy */ |
851 | 0 | if (!fu_cab_firmware_compute_checksum(chunk_zlib->data, |
852 | 0 | chunk_zlib->len, |
853 | 0 | &checksum, |
854 | 0 | error)) |
855 | 0 | return NULL; |
856 | 0 | fu_byte_array_append_uint16(hdr, chunk_zlib->len, G_LITTLE_ENDIAN); |
857 | 0 | fu_byte_array_append_uint16(hdr, fu_chunk_get_data_sz(chk), G_LITTLE_ENDIAN); |
858 | 0 | if (!fu_cab_firmware_compute_checksum(hdr->data, hdr->len, &checksum, error)) |
859 | 0 | return NULL; |
860 | | |
861 | 0 | fu_struct_cab_data_set_checksum(st_data, checksum); |
862 | 0 | fu_struct_cab_data_set_comp(st_data, chunk_zlib->len); |
863 | 0 | fu_struct_cab_data_set_uncomp(st_data, fu_chunk_get_data_sz(chk)); |
864 | 0 | g_byte_array_append(st_hdr, st_data->data, st_data->len); |
865 | 0 | g_byte_array_append(st_hdr, chunk_zlib->data, chunk_zlib->len); |
866 | 0 | } |
867 | | |
868 | | /* success */ |
869 | 0 | return g_steal_pointer(&st_hdr); |
870 | 0 | } |
871 | | |
872 | | static gboolean |
873 | | fu_cab_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) |
874 | 0 | { |
875 | 0 | FuCabFirmware *self = FU_CAB_FIRMWARE(firmware); |
876 | 0 | FuCabFirmwarePrivate *priv = GET_PRIVATE(self); |
877 | 0 | const gchar *tmp; |
878 | | |
879 | | /* simple properties */ |
880 | 0 | tmp = xb_node_query_text(n, "compressed", NULL); |
881 | 0 | if (tmp != NULL) { |
882 | 0 | if (!fu_strtobool(tmp, &priv->compressed, error)) |
883 | 0 | return FALSE; |
884 | 0 | } |
885 | 0 | tmp = xb_node_query_text(n, "only_basename", NULL); |
886 | 0 | if (tmp != NULL) { |
887 | 0 | if (!fu_strtobool(tmp, &priv->only_basename, error)) |
888 | 0 | return FALSE; |
889 | 0 | } |
890 | | |
891 | | /* success */ |
892 | 0 | return TRUE; |
893 | 0 | } |
894 | | |
895 | | static void |
896 | | fu_cab_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) |
897 | 0 | { |
898 | 0 | FuCabFirmware *self = FU_CAB_FIRMWARE(firmware); |
899 | 0 | FuCabFirmwarePrivate *priv = GET_PRIVATE(self); |
900 | 0 | fu_xmlb_builder_insert_kb(bn, "compressed", priv->compressed); |
901 | 0 | fu_xmlb_builder_insert_kb(bn, "only_basename", priv->only_basename); |
902 | 0 | } |
903 | | |
904 | | static void |
905 | | fu_cab_firmware_class_init(FuCabFirmwareClass *klass) |
906 | 1 | { |
907 | 1 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
908 | 1 | firmware_class->validate = fu_cab_firmware_validate; |
909 | 1 | firmware_class->parse = fu_cab_firmware_parse; |
910 | 1 | firmware_class->write = fu_cab_firmware_write; |
911 | 1 | firmware_class->build = fu_cab_firmware_build; |
912 | 1 | firmware_class->export = fu_cab_firmware_export; |
913 | 1 | } |
914 | | |
915 | | static void |
916 | | fu_cab_firmware_init(FuCabFirmware *self) |
917 | 2.91k | { |
918 | 2.91k | g_type_ensure(FU_TYPE_CAB_IMAGE); |
919 | 2.91k | fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE); |
920 | 2.91k | fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); |
921 | 2.91k | fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_DEDUPE_ID); |
922 | 2.91k | fu_firmware_set_images_max(FU_FIRMWARE(self), G_MAXUINT16); |
923 | 2.91k | } |
924 | | |
925 | | /** |
926 | | * fu_cab_firmware_new: |
927 | | * |
928 | | * Returns: (transfer full): a #FuCabFirmware |
929 | | * |
930 | | * Since: 1.9.7 |
931 | | **/ |
932 | | FuCabFirmware * |
933 | | fu_cab_firmware_new(void) |
934 | 0 | { |
935 | 0 | return g_object_new(FU_TYPE_CAB_FIRMWARE, NULL); |
936 | 0 | } |