Coverage Report

Created: 2026-03-11 07:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-firmware.c
Line
Count
Source
1
/*
2
 * Copyright 2019 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
319k
#define G_LOG_DOMAIN "FuFirmware"
8
9
#include "config.h"
10
11
#include "fu-byte-array.h"
12
#include "fu-bytes.h"
13
#include "fu-chunk-private.h"
14
#include "fu-common.h"
15
#include "fu-firmware-private.h"
16
#include "fu-fuzzer.h"
17
#include "fu-input-stream.h"
18
#include "fu-mem.h"
19
#include "fu-partial-input-stream.h"
20
#include "fu-string.h"
21
22
/**
23
 * FuFirmware:
24
 *
25
 * A firmware file which can have children which represent the images within.
26
 *
27
 * See also: [class@FuDfuFirmware], [class@FuIhexFirmware], [class@FuSrecFirmware]
28
 */
29
30
typedef struct {
31
  FuFirmwareFlags flags;
32
  FuFirmware *parent; /* noref */
33
  GPtrArray *images;  /* FuFirmware */
34
  gchar *version;
35
  guint64 version_raw;
36
  FwupdVersionFormat version_format;
37
  GBytes *bytes;
38
  GInputStream *stream;
39
  gsize streamsz;
40
  FuFirmwareAlignment alignment;
41
  gchar *id;
42
  gchar *filename;
43
  guint64 idx;
44
  guint64 addr;
45
  guint64 offset;
46
  gsize size;
47
  gsize size_max;
48
  guint images_max;
49
  GArray *image_gtypes; /* nullable, element-type GType */
50
  guint depth;
51
  GPtrArray *chunks;  /* nullable, element-type FuChunk */
52
  GPtrArray *patches; /* nullable, element-type FuFirmwarePatch */
53
  GPtrArray *magic;   /* nullable, element-type FuFirmwarePatch */
54
} FuFirmwarePrivate;
55
56
static void
57
fu_firmware_fuzzer_iface_init(FuFuzzerInterface *iface);
58
59
261M
G_DEFINE_TYPE_EXTENDED(FuFirmware,
60
261M
           fu_firmware,
61
261M
           G_TYPE_OBJECT,
62
261M
           0,
63
261M
           G_ADD_PRIVATE(FuFirmware)
64
261M
         G_IMPLEMENT_INTERFACE(FU_TYPE_FUZZER, fu_firmware_fuzzer_iface_init));
65
261M
66
261M
#define GET_PRIVATE(o) (fu_firmware_get_instance_private(o))
67
68
enum { PROP_0, PROP_PARENT, PROP_LAST };
69
70
1.07M
#define FU_FIRMWARE_IMAGE_DEPTH_MAX 50
71
72
typedef struct {
73
  gsize offset;
74
  GBytes *blob;
75
} FuFirmwarePatch;
76
77
static void
78
fu_firmware_patch_free(FuFirmwarePatch *ptch)
79
61.7k
{
80
61.7k
  g_bytes_unref(ptch->blob);
81
61.7k
  g_free(ptch);
82
61.7k
}
83
84
/**
85
 * fu_firmware_add_flag:
86
 * @self: a #FuFirmware
87
 * @flag: the firmware flag
88
 *
89
 * Adds a specific firmware flag to the firmware.
90
 *
91
 * Since: 1.5.0
92
 **/
93
void
94
fu_firmware_add_flag(FuFirmware *self, FuFirmwareFlags flag)
95
4.98M
{
96
4.98M
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
97
4.98M
  g_return_if_fail(FU_IS_FIRMWARE(self));
98
4.98M
  priv->flags |= flag;
99
4.98M
}
100
101
/**
102
 * fu_firmware_has_flag:
103
 * @self: a #FuFirmware
104
 * @flag: the firmware flag
105
 *
106
 * Finds if the firmware has a specific firmware flag.
107
 *
108
 * Returns: %TRUE if the flag is set
109
 *
110
 * Since: 1.5.0
111
 **/
112
gboolean
113
fu_firmware_has_flag(FuFirmware *self, FuFirmwareFlags flag)
114
4.58M
{
115
4.58M
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
116
4.58M
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE);
117
4.58M
  return (priv->flags & flag) > 0;
118
4.58M
}
119
120
/**
121
 * fu_firmware_get_version:
122
 * @self: a #FuFirmware
123
 *
124
 * Gets an optional version that represents the firmware.
125
 *
126
 * Returns: a string, or %NULL
127
 *
128
 * Since: 1.3.3
129
 **/
130
const gchar *
131
fu_firmware_get_version(FuFirmware *self)
132
67.9k
{
133
67.9k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
134
67.9k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
135
67.9k
  return priv->version;
136
67.9k
}
137
138
/**
139
 * fu_firmware_set_version:
140
 * @self: a #FuFirmware
141
 * @version: (nullable): optional string version
142
 *
143
 * Sets an optional version that represents the firmware.
144
 *
145
 * Since: 1.3.3
146
 **/
147
void
148
fu_firmware_set_version(FuFirmware *self, const gchar *version)
149
17.7k
{
150
17.7k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
151
17.7k
  g_return_if_fail(FU_IS_FIRMWARE(self));
152
153
  /* not changed */
154
17.7k
  if (g_strcmp0(priv->version, version) == 0)
155
1.45k
    return;
156
157
16.3k
  g_free(priv->version);
158
16.3k
  priv->version = g_strdup(version);
159
16.3k
}
160
161
/**
162
 * fu_firmware_get_version_raw:
163
 * @self: a #FuFirmware
164
 *
165
 * Gets an raw version that represents the firmware. This is most frequently
166
 * used when building firmware with `<version_raw>0x123456</version_raw>` in a
167
 * `firmware.builder.xml` file to avoid string splitting and sanity checks.
168
 *
169
 * Returns: an integer, or %G_MAXUINT64 for invalid
170
 *
171
 * Since:  1.5.7
172
 **/
173
guint64
174
fu_firmware_get_version_raw(FuFirmware *self)
175
15.7k
{
176
15.7k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
177
15.7k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), G_MAXUINT64);
178
15.7k
  return priv->version_raw;
179
15.7k
}
180
181
/**
182
 * fu_firmware_set_version_raw:
183
 * @self: a #FuFirmware
184
 * @version_raw: a raw version, or %G_MAXUINT64 for invalid
185
 *
186
 * Sets an raw version that represents the firmware.
187
 *
188
 * This is optional, and is typically only used for debugging.
189
 *
190
 * Since: 1.5.7
191
 **/
192
void
193
fu_firmware_set_version_raw(FuFirmware *self, guint64 version_raw)
194
77.2k
{
195
77.2k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
196
77.2k
  FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self);
197
198
77.2k
  g_return_if_fail(FU_IS_FIRMWARE(self));
199
200
77.2k
  priv->version_raw = version_raw;
201
202
  /* convert this */
203
77.2k
  if (klass->convert_version != NULL) {
204
2.75k
    g_autofree gchar *version = klass->convert_version(self, version_raw);
205
2.75k
    if (version != NULL)
206
2.75k
      fu_firmware_set_version(self, version); /* nocheck:set-version */
207
2.75k
  }
208
77.2k
}
209
210
/**
211
 * fu_firmware_get_version_format:
212
 * @self: a #FuFirmware
213
 *
214
 * Gets the version format.
215
 *
216
 * Returns: the version format, or %FWUPD_VERSION_FORMAT_UNKNOWN if unset
217
 *
218
 * Since: 2.0.0
219
 **/
220
FwupdVersionFormat
221
fu_firmware_get_version_format(FuFirmware *self)
222
2.43k
{
223
2.43k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
224
2.43k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FWUPD_VERSION_FORMAT_UNKNOWN);
225
2.43k
  return priv->version_format;
226
2.43k
}
227
228
/**
229
 * fu_firmware_set_version_format:
230
 * @self: a #FuFirmware
231
 * @version_format: the version format, e.g. %FWUPD_VERSION_FORMAT_NUMBER
232
 *
233
 * Sets the version format.
234
 *
235
 * Since: 2.0.0
236
 **/
237
void
238
fu_firmware_set_version_format(FuFirmware *self, FwupdVersionFormat version_format)
239
5.70k
{
240
5.70k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
241
5.70k
  FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self);
242
243
5.70k
  g_return_if_fail(FU_IS_FIRMWARE(self));
244
245
  /* same */
246
5.70k
  if (priv->version_format == version_format)
247
0
    return;
248
5.70k
  priv->version_format = version_format;
249
250
  /* convert this, now we know */
251
5.70k
  if (klass->convert_version != NULL && priv->version != NULL && priv->version_raw != 0) {
252
0
    g_autofree gchar *version = klass->convert_version(self, priv->version_raw);
253
0
    fu_firmware_set_version(self, version); /* nocheck:set-version */
254
0
  }
255
5.70k
}
256
257
/**
258
 * fu_firmware_get_filename:
259
 * @self: a #FuFirmware
260
 *
261
 * Gets an optional filename that represents the image source or destination.
262
 *
263
 * Returns: a string, or %NULL
264
 *
265
 * Since: 1.6.0
266
 **/
267
const gchar *
268
fu_firmware_get_filename(FuFirmware *self)
269
0
{
270
0
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
271
0
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
272
0
  return priv->filename;
273
0
}
274
275
/**
276
 * fu_firmware_set_filename:
277
 * @self: a #FuFirmware
278
 * @filename: (nullable): a string filename
279
 *
280
 * Sets an optional filename that represents the image source or destination.
281
 *
282
 * Since: 1.6.0
283
 **/
284
void
285
fu_firmware_set_filename(FuFirmware *self, const gchar *filename)
286
0
{
287
0
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
288
0
  g_return_if_fail(FU_IS_FIRMWARE(self));
289
290
  /* not changed */
291
0
  if (g_strcmp0(priv->filename, filename) == 0)
292
0
    return;
293
294
0
  g_free(priv->filename);
295
0
  priv->filename = g_strdup(filename);
296
0
}
297
298
/**
299
 * fu_firmware_set_id:
300
 * @self: a #FuPlugin
301
 * @id: (nullable): image ID, e.g. `config`
302
 *
303
 * Since: 1.6.0
304
 **/
305
void
306
fu_firmware_set_id(FuFirmware *self, const gchar *id)
307
593k
{
308
593k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
309
593k
  g_return_if_fail(FU_IS_FIRMWARE(self));
310
311
  /* not changed */
312
593k
  if (g_strcmp0(priv->id, id) == 0)
313
147k
    return;
314
315
446k
  g_free(priv->id);
316
446k
  priv->id = g_strdup(id);
317
446k
}
318
319
/**
320
 * fu_firmware_get_id:
321
 * @self: a #FuPlugin
322
 *
323
 * Gets the image ID, typically set at construction.
324
 *
325
 * Returns: image ID, e.g. `config`
326
 *
327
 * Since: 1.6.0
328
 **/
329
const gchar *
330
fu_firmware_get_id(FuFirmware *self)
331
998k
{
332
998k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
333
998k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
334
998k
  return priv->id;
335
998k
}
336
337
/**
338
 * fu_firmware_set_addr:
339
 * @self: a #FuPlugin
340
 * @addr: integer
341
 *
342
 * Sets the base address of the image.
343
 *
344
 * Since: 1.6.0
345
 **/
346
void
347
fu_firmware_set_addr(FuFirmware *self, guint64 addr)
348
74.2k
{
349
74.2k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
350
74.2k
  g_return_if_fail(FU_IS_FIRMWARE(self));
351
74.2k
  priv->addr = addr;
352
74.2k
}
353
354
/**
355
 * fu_firmware_get_addr:
356
 * @self: a #FuPlugin
357
 *
358
 * Gets the base address of the image.
359
 *
360
 * Returns: integer
361
 *
362
 * Since: 1.6.0
363
 **/
364
guint64
365
fu_firmware_get_addr(FuFirmware *self)
366
6.03k
{
367
6.03k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
368
6.03k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), G_MAXUINT64);
369
6.03k
  return priv->addr;
370
6.03k
}
371
372
/**
373
 * fu_firmware_set_offset:
374
 * @self: a #FuPlugin
375
 * @offset: integer
376
 *
377
 * Sets the base offset of the image.
378
 *
379
 * Since: 1.6.0
380
 **/
381
void
382
fu_firmware_set_offset(FuFirmware *self, guint64 offset)
383
4.40M
{
384
4.40M
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
385
4.40M
  g_return_if_fail(FU_IS_FIRMWARE(self));
386
4.40M
  priv->offset = offset;
387
4.40M
}
388
389
/**
390
 * fu_firmware_get_offset:
391
 * @self: a #FuPlugin
392
 *
393
 * Gets the base offset of the image.
394
 *
395
 * Returns: integer
396
 *
397
 * Since: 1.6.0
398
 **/
399
guint64
400
fu_firmware_get_offset(FuFirmware *self)
401
807
{
402
807
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
403
807
  g_return_val_if_fail(FU_IS_FIRMWARE(self), G_MAXUINT64);
404
807
  return priv->offset;
405
807
}
406
407
/**
408
 * fu_firmware_get_parent:
409
 * @self: a #FuFirmware
410
 *
411
 * Gets the parent.
412
 *
413
 * Returns: (transfer none): the parent firmware, or %NULL if unset
414
 *
415
 * Since: 1.8.2
416
 **/
417
FuFirmware *
418
fu_firmware_get_parent(FuFirmware *self)
419
484k
{
420
484k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
421
484k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
422
484k
  return priv->parent;
423
484k
}
424
425
/**
426
 * fu_firmware_set_parent:
427
 * @self: a #FuFirmware
428
 * @parent: (nullable): another #FuFirmware
429
 *
430
 * Sets the parent. Only used internally.
431
 *
432
 * Since: 1.8.2
433
 **/
434
void
435
fu_firmware_set_parent(FuFirmware *self, FuFirmware *parent)
436
17.4M
{
437
17.4M
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
438
17.4M
  g_return_if_fail(FU_IS_FIRMWARE(self));
439
440
17.4M
  if (priv->parent != NULL)
441
110k
    g_object_remove_weak_pointer(G_OBJECT(priv->parent), (gpointer *)&priv->parent);
442
17.4M
  if (parent != NULL)
443
1.07M
    g_object_add_weak_pointer(G_OBJECT(parent), (gpointer *)&priv->parent);
444
17.4M
  priv->parent = parent;
445
17.4M
}
446
447
/**
448
 * fu_firmware_set_size:
449
 * @self: a #FuPlugin
450
 * @size: integer
451
 *
452
 * Sets the total size of the image, which should be the same size as the
453
 * data from fu_firmware_write().
454
 *
455
 * Since: 1.6.0
456
 **/
457
void
458
fu_firmware_set_size(FuFirmware *self, gsize size)
459
6.09M
{
460
6.09M
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
461
6.09M
  g_return_if_fail(FU_IS_FIRMWARE(self));
462
6.09M
  priv->size = size;
463
6.09M
}
464
465
/**
466
 * fu_firmware_get_size:
467
 * @self: a #FuPlugin
468
 *
469
 * Gets the total size of the image, which is typically the same size as the
470
 * data from fu_firmware_write().
471
 *
472
 * If the size has not been explicitly set, and fu_firmware_set_bytes() has been
473
 * used then the size of this is used instead.
474
 *
475
 * Returns: integer
476
 *
477
 * Since: 1.6.0
478
 **/
479
gsize
480
fu_firmware_get_size(FuFirmware *self)
481
618k
{
482
618k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
483
618k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), G_MAXSIZE);
484
618k
  if (priv->size != 0)
485
596k
    return priv->size;
486
22.5k
  if (priv->stream != NULL && priv->streamsz != 0)
487
20.1k
    return priv->streamsz;
488
2.38k
  if (priv->bytes != NULL)
489
2.00k
    return g_bytes_get_size(priv->bytes);
490
383
  return 0;
491
2.38k
}
492
493
/**
494
 * fu_firmware_set_size_max:
495
 * @self: a #FuPlugin
496
 * @size_max: integer, or 0 for no limit
497
 *
498
 * Sets the maximum size of the image allowed during parsing.
499
 * Implementations should query fu_firmware_get_size_max() during parsing when adding images to
500
 * ensure the limit is not exceeded.
501
 *
502
 * Since: 1.9.7
503
 **/
504
void
505
fu_firmware_set_size_max(FuFirmware *self, gsize size_max)
506
211k
{
507
211k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
508
211k
  g_return_if_fail(FU_IS_FIRMWARE(self));
509
211k
  priv->size_max = size_max;
510
211k
}
511
512
/**
513
 * fu_firmware_get_size_max:
514
 * @self: a #FuPlugin
515
 *
516
 * Gets the maximum size of the image allowed during parsing.
517
 *
518
 * Returns: integer, or 0 if not set
519
 *
520
 * Since: 1.9.7
521
 **/
522
gsize
523
fu_firmware_get_size_max(FuFirmware *self)
524
395k
{
525
395k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
526
395k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), G_MAXSIZE);
527
395k
  return priv->size_max;
528
395k
}
529
530
/**
531
 * fu_firmware_set_idx:
532
 * @self: a #FuPlugin
533
 * @idx: integer
534
 *
535
 * Sets the index of the image which is used for ordering.
536
 *
537
 * Since: 1.6.0
538
 **/
539
void
540
fu_firmware_set_idx(FuFirmware *self, guint64 idx)
541
3.60M
{
542
3.60M
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
543
3.60M
  g_return_if_fail(FU_IS_FIRMWARE(self));
544
3.60M
  priv->idx = idx;
545
3.60M
}
546
547
/**
548
 * fu_firmware_get_idx:
549
 * @self: a #FuPlugin
550
 *
551
 * Gets the index of the image which is used for ordering.
552
 *
553
 * Returns: integer
554
 *
555
 * Since: 1.6.0
556
 **/
557
guint64
558
fu_firmware_get_idx(FuFirmware *self)
559
51.4M
{
560
51.4M
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
561
51.4M
  g_return_val_if_fail(FU_IS_FIRMWARE(self), G_MAXUINT64);
562
51.4M
  return priv->idx;
563
51.4M
}
564
565
/**
566
 * fu_firmware_set_bytes:
567
 * @self: a #FuPlugin
568
 * @bytes: data blob
569
 *
570
 * Sets the contents of the image if not created with fu_firmware_new_from_bytes().
571
 *
572
 * Since: 1.6.0
573
 **/
574
void
575
fu_firmware_set_bytes(FuFirmware *self, GBytes *bytes)
576
235k
{
577
235k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
578
235k
  g_return_if_fail(FU_IS_FIRMWARE(self));
579
235k
  g_return_if_fail(bytes != NULL);
580
235k
  if (priv->bytes == bytes)
581
0
    return;
582
235k
  if (priv->bytes != NULL)
583
0
    g_bytes_unref(priv->bytes);
584
235k
  priv->bytes = g_bytes_ref(bytes);
585
586
  /* the input stream is no longer valid */
587
235k
  g_clear_object(&priv->stream);
588
235k
}
589
590
/**
591
 * fu_firmware_get_bytes:
592
 * @self: a #FuPlugin
593
 * @error: (nullable): optional return location for an error
594
 *
595
 * Gets the firmware payload, which does not have any header or footer included.
596
 *
597
 * If there is more than one potential payload or image section then fu_firmware_add_image()
598
 * should be used instead.
599
 *
600
 * Returns: (transfer full): a #GBytes, or %NULL if the payload has never been set
601
 *
602
 * Since: 1.6.0
603
 **/
604
GBytes *
605
fu_firmware_get_bytes(FuFirmware *self, GError **error)
606
40.3k
{
607
40.3k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
608
40.3k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
609
40.3k
  if (priv->bytes != NULL)
610
10.4k
    return g_bytes_ref(priv->bytes);
611
29.9k
  if (priv->stream != NULL) {
612
28.5k
    if (priv->streamsz == 0) {
613
308
      g_set_error_literal(error,
614
308
              FWUPD_ERROR,
615
308
              FWUPD_ERROR_INVALID_DATA,
616
308
              "stream size unknown");
617
308
      return NULL;
618
308
    }
619
28.1k
    return fu_input_stream_read_bytes(priv->stream, 0x0, priv->streamsz, NULL, error);
620
28.5k
  }
621
1.43k
  g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no payload set");
622
1.43k
  return NULL;
623
29.9k
}
624
625
/**
626
 * fu_firmware_get_bytes_with_patches:
627
 * @self: a #FuPlugin
628
 * @error: (nullable): optional return location for an error
629
 *
630
 * Gets the firmware payload, with any defined patches applied.
631
 *
632
 * Returns: (transfer full): a #GBytes, or %NULL if the payload has never been set
633
 *
634
 * Since: 1.7.4
635
 **/
636
GBytes *
637
fu_firmware_get_bytes_with_patches(FuFirmware *self, GError **error)
638
9.51k
{
639
9.51k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
640
9.51k
  g_autoptr(GByteArray) buf = g_byte_array_new();
641
642
9.51k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
643
644
9.51k
  if (priv->bytes == NULL) {
645
8.00k
    if (priv->stream != NULL)
646
6.19k
      return fu_firmware_get_bytes(self, error);
647
1.81k
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no payload set");
648
1.81k
    return NULL;
649
8.00k
  }
650
651
  /* usual case */
652
1.50k
  if (priv->patches == NULL)
653
1.50k
    return fu_firmware_get_bytes(self, error);
654
655
  /* convert to a mutable buffer, apply each patch, aborting if the offset isn't valid */
656
0
  fu_byte_array_append_bytes(buf, priv->bytes);
657
0
  for (guint i = 0; i < priv->patches->len; i++) {
658
0
    FuFirmwarePatch *ptch = g_ptr_array_index(priv->patches, i);
659
0
    if (!fu_memcpy_safe(buf->data,
660
0
            buf->len,
661
0
            ptch->offset, /* dst */
662
0
            g_bytes_get_data(ptch->blob, NULL),
663
0
            g_bytes_get_size(ptch->blob),
664
0
            0x0, /* src */
665
0
            g_bytes_get_size(ptch->blob),
666
0
            error)) {
667
0
      g_prefix_error(error, "failed to apply patch @0x%x: ", (guint)ptch->offset);
668
0
      return NULL;
669
0
    }
670
0
  }
671
672
  /* success */
673
0
  return g_bytes_new(buf->data, buf->len);
674
0
}
675
676
/**
677
 * fu_firmware_set_alignment:
678
 * @self: a #FuFirmware
679
 * @alignment: a #FuFirmwareAlignment
680
 *
681
 * Sets the alignment of the firmware.
682
 *
683
 * This allows a firmware to pad to a power of 2 boundary, where @alignment
684
 * is the bit position to align to.
685
 *
686
 * Since: 1.6.0
687
 **/
688
void
689
fu_firmware_set_alignment(FuFirmware *self, FuFirmwareAlignment alignment)
690
51.6k
{
691
51.6k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
692
51.6k
  g_return_if_fail(FU_IS_FIRMWARE(self));
693
51.6k
  priv->alignment = alignment;
694
51.6k
}
695
696
/**
697
 * fu_firmware_get_stream:
698
 * @self: a #FuPlugin
699
 * @error: (nullable): optional return location for an error
700
 *
701
 * Gets the input stream which was used to parse the firmware.
702
 *
703
 * Returns: (transfer full): a #GInputStream, or %NULL if the payload has never been set
704
 *
705
 * Since: 2.0.0
706
 **/
707
GInputStream *
708
fu_firmware_get_stream(FuFirmware *self, GError **error)
709
1.57k
{
710
1.57k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
711
1.57k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
712
1.57k
  if (priv->stream != NULL)
713
0
    return g_object_ref(priv->stream);
714
1.57k
  if (priv->bytes != NULL)
715
1.57k
    return g_memory_input_stream_new_from_bytes(priv->bytes);
716
0
  g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no stream or bytes set");
717
0
  return NULL;
718
1.57k
}
719
720
/**
721
 * fu_firmware_set_stream:
722
 * @self: a #FuPlugin
723
 * @stream: (nullable): #GInputStream
724
 * @error: (nullable): optional return location for an error
725
 *
726
 * Sets the input stream.
727
 *
728
 * Returns: %TRUE on success
729
 *
730
 * Since: 2.0.0
731
 **/
732
gboolean
733
fu_firmware_set_stream(FuFirmware *self, GInputStream *stream, GError **error)
734
221k
{
735
221k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
736
221k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE);
737
221k
  g_return_val_if_fail(stream == NULL || G_IS_INPUT_STREAM(stream), FALSE);
738
221k
  if (stream != NULL) {
739
219k
    if (!fu_input_stream_size(stream, &priv->streamsz, error))
740
0
      return FALSE;
741
219k
  } else {
742
1.23k
    priv->streamsz = 0;
743
1.23k
  }
744
221k
  g_set_object(&priv->stream, stream);
745
221k
  return TRUE;
746
221k
}
747
748
/**
749
 * fu_firmware_get_alignment:
750
 * @self: a #FuFirmware
751
 *
752
 * Gets the alignment of the firmware.
753
 *
754
 * This allows a firmware to pad to a power of 2 boundary, where @alignment
755
 * is the bit position to align to.
756
 *
757
 * Returns: a #FuFirmwareAlignment
758
 *
759
 * Since: 1.6.0
760
 **/
761
FuFirmwareAlignment
762
fu_firmware_get_alignment(FuFirmware *self)
763
21.6k
{
764
21.6k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
765
21.6k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FU_FIRMWARE_ALIGNMENT_LAST);
766
21.6k
  return priv->alignment;
767
21.6k
}
768
769
/**
770
 * fu_firmware_get_chunks:
771
 * @self: a #FuFirmware
772
 * @error: (nullable): optional return location for an error
773
 *
774
 * Gets the optional image chunks.
775
 *
776
 * Returns: (transfer container) (element-type FuChunk) (nullable): chunk data, or %NULL
777
 *
778
 * Since: 1.6.0
779
 **/
780
GPtrArray *
781
fu_firmware_get_chunks(FuFirmware *self, GError **error)
782
489
{
783
489
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
784
785
489
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
786
489
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
787
788
  /* set */
789
489
  if (priv->chunks != NULL)
790
489
    return g_ptr_array_ref(priv->chunks);
791
792
  /* lets build something plausible */
793
0
  if (priv->bytes != NULL) {
794
0
    g_autoptr(GPtrArray) chunks = NULL;
795
0
    g_autoptr(FuChunk) chk = NULL;
796
0
    chunks = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
797
0
    chk = fu_chunk_bytes_new(priv->bytes);
798
0
    fu_chunk_set_idx(chk, priv->idx);
799
0
    fu_chunk_set_address(chk, priv->addr);
800
0
    g_ptr_array_add(chunks, g_steal_pointer(&chk));
801
0
    return g_steal_pointer(&chunks);
802
0
  }
803
804
  /* nothing to do */
805
0
  g_set_error_literal(error,
806
0
          FWUPD_ERROR,
807
0
          FWUPD_ERROR_NOT_FOUND,
808
0
          "no bytes or chunks found in firmware");
809
0
  return NULL;
810
0
}
811
812
/**
813
 * fu_firmware_add_chunk:
814
 * @self: a #FuFirmware
815
 * @chk: a #FuChunk
816
 *
817
 * Adds a chunk to the image.
818
 *
819
 * Since: 1.6.0
820
 **/
821
void
822
fu_firmware_add_chunk(FuFirmware *self, FuChunk *chk)
823
846k
{
824
846k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
825
846k
  g_return_if_fail(FU_IS_FIRMWARE(self));
826
846k
  g_return_if_fail(FU_IS_CHUNK(chk));
827
846k
  if (priv->chunks == NULL)
828
735
    priv->chunks = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
829
846k
  g_ptr_array_add(priv->chunks, g_object_ref(chk));
830
846k
}
831
832
/**
833
 * fu_firmware_add_magic:
834
 * @self: a #FuFirmware
835
 * @buf: some data
836
 * @bufsz: sizeof @buf
837
 * @offset: offset to start parsing, typically 0x0
838
 *
839
 * Adds a possible magic signature to the image.
840
 *
841
 * Since: 2.0.18
842
 **/
843
void
844
fu_firmware_add_magic(FuFirmware *self, const guint8 *buf, gsize bufsz, gsize offset)
845
61.7k
{
846
61.7k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
847
61.7k
  g_autofree FuFirmwarePatch *patch = g_new0(FuFirmwarePatch, 1);
848
849
61.7k
  g_return_if_fail(FU_IS_FIRMWARE(self));
850
61.7k
  g_return_if_fail(buf != NULL);
851
61.7k
  g_return_if_fail(bufsz != 0);
852
853
61.7k
  if (priv->magic == NULL)
854
61.7k
    priv->magic =
855
61.7k
        g_ptr_array_new_with_free_func((GDestroyNotify)fu_firmware_patch_free);
856
61.7k
  patch->blob = g_bytes_new(buf, bufsz);
857
61.7k
  patch->offset = offset;
858
61.7k
  g_ptr_array_add(priv->magic, g_steal_pointer(&patch));
859
61.7k
}
860
861
/**
862
 * fu_firmware_get_checksum:
863
 * @self: a #FuPlugin
864
 * @csum_kind: a checksum type, e.g. %G_CHECKSUM_SHA256
865
 * @error: (nullable): optional return location for an error
866
 *
867
 * Returns a checksum of the payload data.
868
 *
869
 * Returns: (transfer full): a checksum string, or %NULL if the checksum is not available
870
 *
871
 * Since: 1.6.0
872
 **/
873
gchar *
874
fu_firmware_get_checksum(FuFirmware *self, GChecksumType csum_kind, GError **error)
875
0
{
876
0
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
877
0
  FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self);
878
0
  g_autoptr(GBytes) blob = NULL;
879
880
0
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
881
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
882
883
  /* subclassed */
884
0
  if (klass->get_checksum != NULL) {
885
0
    g_autoptr(GError) error_local = NULL;
886
0
    g_autofree gchar *checksum = klass->get_checksum(self, csum_kind, &error_local);
887
0
    if (checksum != NULL)
888
0
      return g_steal_pointer(&checksum);
889
0
    if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) {
890
0
      g_propagate_error(error, g_steal_pointer(&error_local));
891
0
      return NULL;
892
0
    }
893
0
  }
894
895
  /* write */
896
0
  if (klass->write != NULL) {
897
0
    blob = fu_firmware_write(self, error);
898
0
    if (blob == NULL)
899
0
      return NULL;
900
0
    return g_compute_checksum_for_bytes(csum_kind, blob);
901
0
  }
902
903
  /* internal data */
904
0
  if (priv->bytes != NULL)
905
0
    return g_compute_checksum_for_bytes(csum_kind, priv->bytes);
906
0
  if (priv->stream != NULL)
907
0
    return fu_input_stream_compute_checksum(priv->stream, csum_kind, error);
908
909
  /* nothing to do */
910
0
  g_set_error_literal(error,
911
0
          FWUPD_ERROR,
912
0
          FWUPD_ERROR_NOT_FOUND,
913
0
          "no input data, as no FuFirmware->write, stream or bytes");
914
0
  return NULL;
915
0
}
916
917
/**
918
 * fu_firmware_tokenize:
919
 * @self: a #FuFirmware
920
 * @stream: a #GInputStream
921
 * @flags: #FuFirmwareParseFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE
922
 * @error: (nullable): optional return location for an error
923
 *
924
 * Tokenizes a firmware, typically breaking the firmware into records.
925
 *
926
 * Records can be enumerated using subclass-specific functionality, for example
927
 * using fu_srec_firmware_get_records().
928
 *
929
 * Returns: %TRUE for success
930
 *
931
 * Since: 2.0.0
932
 **/
933
gboolean
934
fu_firmware_tokenize(FuFirmware *self,
935
         GInputStream *stream,
936
         FuFirmwareParseFlags flags,
937
         GError **error)
938
0
{
939
0
  FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self);
940
941
0
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE);
942
0
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
943
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
944
945
  /* optionally subclassed */
946
0
  if (klass->tokenize != NULL)
947
0
    return klass->tokenize(self, stream, flags, error);
948
0
  return TRUE;
949
0
}
950
951
/**
952
 * fu_firmware_check_compatible:
953
 * @self: a #FuFirmware
954
 * @other: a #FuFirmware
955
 * @flags: #FuFirmwareParseFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE
956
 * @error: (nullable): optional return location for an error
957
 *
958
 * Check a new firmware is compatible with the existing firmware.
959
 *
960
 * Returns: %TRUE for success
961
 *
962
 * Since: 1.8.4
963
 **/
964
gboolean
965
fu_firmware_check_compatible(FuFirmware *self,
966
           FuFirmware *other,
967
           FuFirmwareParseFlags flags,
968
           GError **error)
969
0
{
970
0
  FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self);
971
972
0
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE);
973
0
  g_return_val_if_fail(FU_IS_FIRMWARE(other), FALSE);
974
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
975
976
  /* optionally subclassed */
977
0
  if (klass->check_compatible == NULL)
978
0
    return TRUE;
979
0
  return klass->check_compatible(self, other, flags, error);
980
0
}
981
982
static gboolean
983
fu_firmware_validate_for_offset(FuFirmware *self,
984
        GInputStream *stream,
985
        gsize offset,
986
        gsize *offset_found,
987
        FuFirmwareParseFlags flags,
988
        GError **error)
989
4.25M
{
990
4.25M
  FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self);
991
4.25M
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
992
4.25M
  gsize streamsz = 0;
993
994
  /* not implemented */
995
4.25M
  if (klass->validate == NULL)
996
3.93M
    return TRUE;
997
998
  /* fuzzing */
999
318k
  if (!fu_firmware_has_flag(self, FU_FIRMWARE_FLAG_ALWAYS_SEARCH) &&
1000
267k
      (flags & FU_FIRMWARE_PARSE_FLAG_NO_SEARCH) > 0) {
1001
267k
    return klass->validate(self, stream, offset, error);
1002
267k
  }
1003
1004
  /* try all the magic values, if provided */
1005
50.7k
  if (priv->magic != NULL) {
1006
58.7k
    for (guint i = 0; i < priv->magic->len; i++) {
1007
50.7k
      FuFirmwarePatch *patch = g_ptr_array_index(priv->magic, i);
1008
50.7k
      gsize offset_tmp = 0;
1009
50.7k
      g_debug("searching for 0x%x bytes of magic",
1010
50.7k
        (guint)g_bytes_get_size(patch->blob));
1011
50.7k
      if (fu_input_stream_find(stream,
1012
50.7k
             g_bytes_get_data(patch->blob, NULL),
1013
50.7k
             g_bytes_get_size(patch->blob),
1014
50.7k
             offset,
1015
50.7k
             &offset_tmp,
1016
50.7k
             NULL)) {
1017
42.6k
        offset_tmp -= patch->offset;
1018
42.6k
        g_debug("found magic @0x%x", (guint)offset_tmp);
1019
42.6k
        if (offset_found != NULL)
1020
42.6k
          *offset_found = offset_tmp;
1021
42.6k
        return klass->validate(self, stream, *offset_found, error);
1022
42.6k
      }
1023
50.7k
    }
1024
8.06k
    g_set_error_literal(error,
1025
8.06k
            FWUPD_ERROR,
1026
8.06k
            FWUPD_ERROR_INVALID_FILE,
1027
8.06k
            "failed to find magic bytes");
1028
8.06k
    return FALSE;
1029
50.7k
  }
1030
1031
  /* limit the size of firmware we search as brute force is expensive */
1032
0
  if (!fu_input_stream_size(stream, &streamsz, error))
1033
0
    return FALSE;
1034
0
  if (streamsz < FU_FIRMWARE_SEARCH_MAGIC_BUFSZ_MAX) {
1035
0
    for (gsize offset_tmp = offset; offset_tmp < streamsz; offset_tmp++) {
1036
0
      if (klass->validate(self, stream, offset_tmp, NULL)) {
1037
0
        *offset_found = offset_tmp;
1038
0
        return TRUE;
1039
0
      }
1040
0
    }
1041
0
  }
1042
1043
  /* check in more detail */
1044
0
  return klass->validate(self, stream, offset, error);
1045
0
}
1046
1047
/**
1048
 * fu_firmware_parse_stream:
1049
 * @self: a #FuFirmware
1050
 * @stream: input stream
1051
 * @offset: start offset
1052
 * @flags: #FuFirmwareParseFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE
1053
 * @error: (nullable): optional return location for an error
1054
 *
1055
 * Parses a firmware from a stream, typically breaking the firmware into images.
1056
 *
1057
 * Returns: %TRUE for success
1058
 *
1059
 * Since: 2.0.0
1060
 **/
1061
gboolean
1062
fu_firmware_parse_stream(FuFirmware *self,
1063
       GInputStream *stream,
1064
       gsize offset,
1065
       FuFirmwareParseFlags flags,
1066
       GError **error)
1067
4.25M
{
1068
4.25M
  FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self);
1069
4.25M
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
1070
4.25M
  gsize streamsz = 0;
1071
4.25M
  g_autoptr(GInputStream) partial_stream = NULL;
1072
4.25M
  g_autoptr(GInputStream) seekable_stream = NULL;
1073
4.25M
  g_autoptr(GBytes) blob = NULL;
1074
1075
4.25M
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE);
1076
4.25M
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
1077
4.25M
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1078
1079
  /* sanity check */
1080
4.25M
  if (fu_firmware_has_flag(self, FU_FIRMWARE_FLAG_DONE_PARSE)) {
1081
0
    g_set_error_literal(error,
1082
0
            FWUPD_ERROR,
1083
0
            FWUPD_ERROR_NOT_SUPPORTED,
1084
0
            "firmware object cannot be reused");
1085
0
    return FALSE;
1086
0
  }
1087
1088
  /* ensure the stream is seekable */
1089
4.25M
  if (!G_IS_SEEKABLE(stream) || !g_seekable_can_seek(G_SEEKABLE(stream))) {
1090
0
    blob = fu_input_stream_read_bytes(stream, offset, G_MAXUINT32, NULL, error);
1091
0
    if (blob == NULL)
1092
0
      return FALSE;
1093
0
    seekable_stream = g_memory_input_stream_new_from_bytes(blob);
1094
4.25M
  } else {
1095
4.25M
    seekable_stream = g_object_ref(stream);
1096
4.25M
  }
1097
1098
  /* check size */
1099
4.25M
  if (!fu_input_stream_size(seekable_stream, &streamsz, error))
1100
0
    return FALSE;
1101
4.25M
  if (streamsz <= offset) {
1102
464
    g_set_error(error,
1103
464
          FWUPD_ERROR,
1104
464
          FWUPD_ERROR_NOT_SUPPORTED,
1105
464
          "stream size 0x%x is smaller than offset 0x%x",
1106
464
          (guint)streamsz,
1107
464
          (guint)offset);
1108
464
    return FALSE;
1109
464
  }
1110
1111
  /* optional */
1112
4.25M
  if (!fu_firmware_validate_for_offset(self, seekable_stream, offset, &offset, flags, error))
1113
206k
    return FALSE;
1114
4.05M
  fu_firmware_set_offset(self, offset);
1115
1116
  /* save stream size */
1117
4.05M
  priv->streamsz = streamsz - offset;
1118
4.05M
  if (priv->streamsz == 0) {
1119
0
    g_set_error_literal(error,
1120
0
            FWUPD_ERROR,
1121
0
            FWUPD_ERROR_NOT_SUPPORTED,
1122
0
            "invalid firmware as zero sized");
1123
0
    return FALSE;
1124
0
  }
1125
4.05M
  if (priv->size_max > 0 && priv->streamsz > priv->size_max) {
1126
7.00k
    g_autofree gchar *sz_val = g_format_size(priv->streamsz);
1127
7.00k
    g_autofree gchar *sz_max = g_format_size(priv->size_max);
1128
7.00k
    g_set_error(error,
1129
7.00k
          FWUPD_ERROR,
1130
7.00k
          FWUPD_ERROR_INVALID_FILE,
1131
7.00k
          "firmware is too large (%s, limit %s)",
1132
7.00k
          sz_val,
1133
7.00k
          sz_max);
1134
7.00k
    return FALSE;
1135
7.00k
  }
1136
1137
  /* any FuFirmware subclass that gets past this point might have allocated memory in
1138
   * ->tokenize() or ->parse() and needs to be destroyed before parsing again */
1139
4.04M
  fu_firmware_add_flag(self, FU_FIRMWARE_FLAG_DONE_PARSE);
1140
1141
  /* this allows devices to skip reading the old firmware if the GType is unsuitable */
1142
4.04M
  if (klass->check_compatible != NULL)
1143
467
    fu_firmware_add_flag(self, FU_FIRMWARE_FLAG_HAS_CHECK_COMPATIBLE);
1144
1145
  /* save stream */
1146
4.04M
  if (offset == 0) {
1147
616k
    partial_stream = g_object_ref(seekable_stream);
1148
3.42M
  } else {
1149
3.42M
    partial_stream =
1150
3.42M
        fu_partial_input_stream_new(seekable_stream, offset, priv->streamsz, error);
1151
3.42M
    if (partial_stream == NULL) {
1152
0
      g_prefix_error_literal(error, "failed to cut firmware: ");
1153
0
      return FALSE;
1154
0
    }
1155
3.42M
  }
1156
1157
  /* cache */
1158
4.04M
  if (flags & FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB) {
1159
204k
    if (blob == NULL) {
1160
204k
      blob = fu_input_stream_read_bytes(partial_stream,
1161
204k
                0x0,
1162
204k
                priv->streamsz,
1163
204k
                NULL,
1164
204k
                error);
1165
204k
      if (blob == NULL)
1166
0
        return FALSE;
1167
204k
    }
1168
204k
    fu_firmware_set_bytes(self, blob);
1169
204k
  }
1170
4.04M
  if (flags & FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM)
1171
0
    g_set_object(&priv->stream, partial_stream);
1172
1173
  /* optional */
1174
4.04M
  if (klass->tokenize != NULL) {
1175
4.78k
    if (!klass->tokenize(self, partial_stream, flags, error))
1176
1.55k
      return FALSE;
1177
4.78k
  }
1178
1179
  /* optional */
1180
4.04M
  if (klass->parse_full != NULL)
1181
8.95k
    return klass->parse_full(self, seekable_stream, offset, flags, error);
1182
4.03M
  else if (klass->parse != NULL)
1183
3.88M
    return klass->parse(self, partial_stream, flags, error);
1184
1185
  /* verify alignment */
1186
153k
  if (streamsz % (1ull << priv->alignment) != 0) {
1187
0
    g_autofree gchar *str = NULL;
1188
0
    str = g_format_size_full(1ull << priv->alignment, G_FORMAT_SIZE_IEC_UNITS);
1189
0
    g_set_error(error,
1190
0
          FWUPD_ERROR,
1191
0
          FWUPD_ERROR_INVALID_FILE,
1192
0
          "raw firmware is not aligned to 0x%x (%s)",
1193
0
          (guint)(1ull << priv->alignment),
1194
0
          str);
1195
0
    return FALSE;
1196
0
  }
1197
1198
  /* success */
1199
153k
  return TRUE;
1200
153k
}
1201
1202
/**
1203
 * fu_firmware_parse_bytes:
1204
 * @self: a #FuFirmware
1205
 * @fw: firmware blob
1206
 * @offset: start offset, useful for ignoring a bootloader
1207
 * @flags: #FuFirmwareParseFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE
1208
 * @error: (nullable): optional return location for an error
1209
 *
1210
 * Parses a firmware, typically breaking the firmware into images.
1211
 *
1212
 * Returns: %TRUE for success
1213
 *
1214
 * Since: 2.0.1
1215
 **/
1216
gboolean
1217
fu_firmware_parse_bytes(FuFirmware *self,
1218
      GBytes *fw,
1219
      gsize offset,
1220
      FuFirmwareParseFlags flags,
1221
      GError **error)
1222
3.54M
{
1223
3.54M
  g_autoptr(GInputStream) stream = NULL;
1224
1225
3.54M
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE);
1226
3.54M
  g_return_val_if_fail(fw != NULL, FALSE);
1227
3.54M
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1228
1229
3.54M
  stream = g_memory_input_stream_new_from_bytes(fw);
1230
3.54M
  return fu_firmware_parse_stream(self, stream, offset, flags, error);
1231
3.54M
}
1232
1233
/**
1234
 * fu_firmware_build:
1235
 * @self: a #FuFirmware
1236
 * @n: a Xmlb node
1237
 * @error: (nullable): optional return location for an error
1238
 *
1239
 * Builds a firmware from an XML manifest. The manifest would typically have the
1240
 * following form:
1241
 *
1242
 * |[<!-- language="XML" -->
1243
 * <?xml version="1.0" encoding="UTF-8"?>
1244
 * <firmware gtype="FuBcm57xxFirmware">
1245
 *   <version>1.2.3</version>
1246
 *   <firmware gtype="FuBcm57xxStage1Image">
1247
 *     <version>7.8.9</version>
1248
 *     <id>stage1</id>
1249
 *     <idx>0x01</idx>
1250
 *     <filename>stage1.bin</filename>
1251
 *   </firmware>
1252
 *   <firmware gtype="FuBcm57xxStage2Image">
1253
 *     <id>stage2</id>
1254
 *     <data/> <!-- empty! -->
1255
 *   </firmware>
1256
 *   <firmware gtype="FuBcm57xxDictImage">
1257
 *     <id>ape</id>
1258
 *     <addr>0x7</addr>
1259
 *     <data>aGVsbG8gd29ybGQ=</data> <!-- base64 -->
1260
 *   </firmware>
1261
 * </firmware>
1262
 * ]|
1263
 *
1264
 * This would be used in a build-system to merge images from generated files:
1265
 * `fwupdtool firmware-build fw.builder.xml test.fw`
1266
 *
1267
 * Static binary content can be specified in the `<firmware>/<data>` section and
1268
 * is encoded as base64 text if not empty.
1269
 *
1270
 * Additionally, extra nodes can be included under nested `<firmware>` objects
1271
 * which can be parsed by the subclassed objects. You should verify the
1272
 * subclassed object `FuFirmware->build` vfunc for the specific additional
1273
 * options supported.
1274
 *
1275
 * Plugins should manually g_type_ensure() subclassed image objects if not
1276
 * constructed as part of the plugin fu_plugin_init() or fu_plugin_setup()
1277
 * functions.
1278
 *
1279
 * Returns: %TRUE for success
1280
 *
1281
 * Since: 1.5.0
1282
 **/
1283
gboolean
1284
fu_firmware_build(FuFirmware *self, XbNode *n, GError **error)
1285
0
{
1286
0
  FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self);
1287
0
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
1288
0
  const gchar *tmp;
1289
0
  guint64 tmpval;
1290
0
  guint64 version_raw;
1291
0
  g_autoptr(GPtrArray) chunks = NULL;
1292
0
  g_autoptr(GPtrArray) xb_images = NULL;
1293
0
  g_autoptr(XbNode) data = NULL;
1294
1295
0
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE);
1296
0
  g_return_val_if_fail(XB_IS_NODE(n), FALSE);
1297
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1298
1299
  /* subclassed */
1300
0
  if (klass->build != NULL) {
1301
0
    if (!klass->build(self, n, error))
1302
0
      return FALSE;
1303
0
  }
1304
1305
  /* set attributes */
1306
0
  tmp = xb_node_query_text(n, "version", NULL);
1307
0
  if (tmp != NULL)
1308
0
    fu_firmware_set_version(self, tmp); /* nocheck:set-version */
1309
0
  tmp = xb_node_query_text(n, "version_format", NULL);
1310
0
  if (tmp != NULL) {
1311
0
    FwupdVersionFormat version_format = fwupd_version_format_from_string(tmp);
1312
0
    if (version_format == FWUPD_VERSION_FORMAT_UNKNOWN) {
1313
0
      g_set_error(error,
1314
0
            FWUPD_ERROR,
1315
0
            FWUPD_ERROR_INVALID_DATA,
1316
0
            "%s is not a valid version format",
1317
0
            tmp);
1318
0
      return FALSE;
1319
0
    }
1320
0
    fu_firmware_set_version_format(self, version_format);
1321
0
  }
1322
0
  version_raw = xb_node_query_text_as_uint(n, "version_raw", NULL);
1323
0
  if (version_raw != G_MAXUINT64)
1324
0
    fu_firmware_set_version_raw(self, version_raw);
1325
0
  tmp = xb_node_query_text(n, "id", NULL);
1326
0
  if (tmp != NULL)
1327
0
    fu_firmware_set_id(self, tmp);
1328
0
  tmpval = xb_node_query_text_as_uint(n, "idx", NULL);
1329
0
  if (tmpval != G_MAXUINT64)
1330
0
    fu_firmware_set_idx(self, tmpval);
1331
0
  tmpval = xb_node_query_text_as_uint(n, "addr", NULL);
1332
0
  if (tmpval != G_MAXUINT64)
1333
0
    fu_firmware_set_addr(self, tmpval);
1334
0
  tmpval = xb_node_query_text_as_uint(n, "offset", NULL);
1335
0
  if (tmpval != G_MAXUINT64)
1336
0
    fu_firmware_set_offset(self, tmpval);
1337
0
  tmpval = xb_node_query_text_as_uint(n, "size", NULL);
1338
0
  if (tmpval != G_MAXUINT64)
1339
0
    fu_firmware_set_size(self, tmpval);
1340
0
  tmpval = xb_node_query_text_as_uint(n, "size_max", NULL);
1341
0
  if (tmpval != G_MAXUINT64)
1342
0
    fu_firmware_set_size_max(self, tmpval);
1343
0
  tmpval = xb_node_query_text_as_uint(n, "alignment", NULL);
1344
0
  if (tmpval != G_MAXUINT64) {
1345
0
    if (tmpval > FU_FIRMWARE_ALIGNMENT_2G) {
1346
0
      g_set_error(error,
1347
0
            FWUPD_ERROR,
1348
0
            FWUPD_ERROR_INVALID_DATA,
1349
0
            "0x%x invalid, maximum is 0x%x",
1350
0
            (guint)tmpval,
1351
0
            (guint)FU_FIRMWARE_ALIGNMENT_2G);
1352
0
      return FALSE;
1353
0
    }
1354
0
    fu_firmware_set_alignment(self, (guint8)tmpval);
1355
0
  }
1356
0
  data = xb_node_query_first(n, "data", NULL);
1357
0
  if (data != NULL) {
1358
0
    guint64 sz = xb_node_get_attr_as_uint(data, "size");
1359
0
    g_autoptr(GBytes) blob = NULL;
1360
1361
    /* base64 encoded data */
1362
0
    if (xb_node_get_text(data) != NULL) {
1363
0
      gsize bufsz = 0;
1364
0
      g_autofree guchar *buf = NULL;
1365
0
      buf = g_base64_decode(xb_node_get_text(data), &bufsz);
1366
0
      blob = g_bytes_new(buf, bufsz);
1367
0
    } else {
1368
0
      blob = g_bytes_new(NULL, 0);
1369
0
    }
1370
1371
    /* padding is optional */
1372
0
    if (sz == 0 || sz == G_MAXUINT64) {
1373
0
      fu_firmware_set_bytes(self, blob);
1374
0
    } else {
1375
0
      g_autoptr(GBytes) blob_padded = fu_bytes_pad(blob, (gsize)sz, 0xFF);
1376
0
      fu_firmware_set_bytes(self, blob_padded);
1377
0
    }
1378
0
  }
1379
0
  tmp = xb_node_query_text(n, "filename", NULL);
1380
0
  if (tmp != NULL) {
1381
0
    if (priv->bytes == NULL) {
1382
0
      g_autoptr(GBytes) blob = NULL;
1383
0
      blob = fu_bytes_get_contents(tmp, error);
1384
0
      if (blob == NULL)
1385
0
        return FALSE;
1386
0
      fu_firmware_set_bytes(self, blob);
1387
0
    }
1388
0
    fu_firmware_set_filename(self, tmp);
1389
0
  }
1390
1391
  /* optional chunks */
1392
0
  chunks = xb_node_query(n, "chunks/chunk", 0, NULL);
1393
0
  if (chunks != NULL) {
1394
0
    for (guint i = 0; i < chunks->len; i++) {
1395
0
      XbNode *c = g_ptr_array_index(chunks, i);
1396
0
      g_autoptr(FuChunk) chk = fu_chunk_bytes_new(NULL);
1397
0
      fu_chunk_set_idx(chk, i);
1398
0
      if (!fu_chunk_build(chk, c, error))
1399
0
        return FALSE;
1400
0
      fu_firmware_add_chunk(self, chk);
1401
0
    }
1402
0
  }
1403
1404
  /* parse images */
1405
0
  xb_images = xb_node_query(n, "firmware", 0, NULL);
1406
0
  if (xb_images != NULL) {
1407
0
    for (guint i = 0; i < xb_images->len; i++) {
1408
0
      XbNode *xb_image = g_ptr_array_index(xb_images, i);
1409
0
      g_autoptr(FuFirmware) img = NULL;
1410
0
      tmp = xb_node_get_attr(xb_image, "gtype");
1411
0
      if (tmp != NULL) {
1412
0
        GType gtype = g_type_from_name(tmp);
1413
0
        if (gtype == G_TYPE_INVALID) {
1414
0
          g_set_error(error,
1415
0
                FWUPD_ERROR,
1416
0
                FWUPD_ERROR_NOT_FOUND,
1417
0
                "GType %s not registered",
1418
0
                tmp);
1419
0
          return FALSE;
1420
0
        }
1421
0
        img = g_object_new(gtype, NULL);
1422
0
      } else {
1423
0
        img = fu_firmware_new();
1424
0
      }
1425
0
      if (!fu_firmware_add_image(self, img, error))
1426
0
        return FALSE;
1427
0
      if (!fu_firmware_build(img, xb_image, error))
1428
0
        return FALSE;
1429
0
    }
1430
0
  }
1431
1432
  /* success */
1433
0
  return TRUE;
1434
0
}
1435
1436
/**
1437
 * fu_firmware_new_from_xml:
1438
 * @xml: XML text
1439
 * @error: (nullable): optional return location for an error
1440
 *
1441
 * Builds a firmware from an XML manifest. The manifest would typically have the
1442
 * following form:
1443
 *
1444
 * |[<!-- language="XML" -->
1445
 * <?xml version="1.0" encoding="UTF-8"?>
1446
 * <firmware gtype="FuBcm57xxFirmware">
1447
 *   <version>1.2.3</version>
1448
 *   <firmware gtype="FuBcm57xxStage1Image">
1449
 *     <version>7.8.9</version>
1450
 *     <id>stage1</id>
1451
 *     <idx>0x01</idx>
1452
 *     <filename>stage1.bin</filename>
1453
 *   </firmware>
1454
 *   <firmware gtype="FuBcm57xxStage2Image">
1455
 *     <id>stage2</id>
1456
 *     <data/> <!-- empty! -->
1457
 *   </firmware>
1458
 *   <firmware gtype="FuBcm57xxDictImage">
1459
 *     <id>ape</id>
1460
 *     <addr>0x7</addr>
1461
 *     <data>aGVsbG8gd29ybGQ=</data> <!-- base64 -->
1462
 *   </firmware>
1463
 * </firmware>
1464
 * ]|
1465
 *
1466
 * This would be used in a build-system to merge images from generated files:
1467
 * `fwupdtool firmware-build fw.builder.xml test.fw`
1468
 *
1469
 * Static binary content can be specified in the `<firmware>/<data>` section and
1470
 * is encoded as base64 text if not empty.
1471
 *
1472
 * Additionally, extra nodes can be included under nested `<firmware>` objects
1473
 * which can be parsed by the subclassed objects. You should verify the
1474
 * subclassed object `FuFirmware->build` vfunc for the specific additional
1475
 * options supported.
1476
 *
1477
 * Firmware subclasses should use fu_firmware_add_image_gtype() for subclassed image objects.
1478
 *
1479
 * Returns: %TRUE for success
1480
 *
1481
 * Since: 2.1.1
1482
 **/
1483
FuFirmware *
1484
fu_firmware_new_from_xml(const gchar *xml, GError **error)
1485
0
{
1486
0
  GType gtype;
1487
0
  const gchar *gtypestr = NULL;
1488
0
  g_autoptr(FuFirmware) self = NULL;
1489
0
  g_autoptr(XbBuilder) builder = xb_builder_new();
1490
0
  g_autoptr(XbBuilderSource) source = xb_builder_source_new();
1491
0
  g_autoptr(XbNode) n = NULL;
1492
0
  g_autoptr(XbSilo) silo = NULL;
1493
1494
0
  g_return_val_if_fail(xml != NULL, NULL);
1495
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1496
1497
  /* parse XML */
1498
0
  if (!xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, error)) {
1499
0
    g_prefix_error_literal(error, "could not parse XML: ");
1500
0
    fwupd_error_convert(error);
1501
0
    return NULL;
1502
0
  }
1503
0
  xb_builder_import_source(builder, source);
1504
0
  silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
1505
0
  if (silo == NULL) {
1506
0
    fwupd_error_convert(error);
1507
0
    return NULL;
1508
0
  }
1509
1510
  /* create FuFirmware of specific GType */
1511
0
  n = xb_silo_query_first(silo, "firmware", error);
1512
0
  if (n == NULL) {
1513
0
    fwupd_error_convert(error);
1514
0
    return NULL;
1515
0
  }
1516
0
  gtypestr = xb_node_get_attr(n, "gtype");
1517
0
  if (gtypestr == NULL) {
1518
0
    g_set_error_literal(error,
1519
0
            FWUPD_ERROR,
1520
0
            FWUPD_ERROR_INVALID_DATA,
1521
0
            "expected gtype for <firmware>");
1522
0
    return NULL;
1523
0
  }
1524
0
  gtype = g_type_from_name(gtypestr);
1525
0
  if (gtype == G_TYPE_INVALID) {
1526
0
    g_set_error(error,
1527
0
          FWUPD_ERROR,
1528
0
          FWUPD_ERROR_NOT_FOUND,
1529
0
          "GType %s not registered",
1530
0
          gtypestr);
1531
0
    return NULL;
1532
0
  }
1533
0
  self = g_object_new(gtype, NULL);
1534
0
  if (!fu_firmware_build(self, n, error))
1535
0
    return NULL;
1536
0
  return g_steal_pointer(&self);
1537
0
}
1538
1539
/**
1540
 * fu_firmware_new_from_filename:
1541
 * @filename: filename of XML builder
1542
 * @error: (nullable): optional return location for an error
1543
 *
1544
 * Builds a firmware from an XML manifest.
1545
 *
1546
 * Returns: a #FuFirmware on success, else %NULL
1547
 *
1548
 * Since: 2.1.1
1549
 **/
1550
FuFirmware *
1551
fu_firmware_new_from_filename(const gchar *filename, GError **error)
1552
0
{
1553
0
  g_autofree gchar *xml = NULL;
1554
1555
0
  g_return_val_if_fail(filename != NULL, NULL);
1556
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1557
1558
0
  if (!g_file_get_contents(filename, &xml, NULL, error))
1559
0
    return NULL;
1560
0
  return fu_firmware_new_from_xml(xml, error);
1561
0
}
1562
1563
/**
1564
 * fu_firmware_parse_file:
1565
 * @self: a #FuFirmware
1566
 * @file: a file
1567
 * @flags: #FuFirmwareParseFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE
1568
 * @error: (nullable): optional return location for an error
1569
 *
1570
 * Parses a firmware file, typically breaking the firmware into images.
1571
 *
1572
 * Returns: %TRUE for success
1573
 *
1574
 * Since: 1.3.3
1575
 **/
1576
gboolean
1577
fu_firmware_parse_file(FuFirmware *self, GFile *file, FuFirmwareParseFlags flags, GError **error)
1578
0
{
1579
0
  g_autoptr(GFileInputStream) stream = NULL;
1580
1581
0
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE);
1582
0
  g_return_val_if_fail(G_IS_FILE(file), FALSE);
1583
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1584
1585
0
  stream = g_file_read(file, NULL, error);
1586
0
  if (stream == NULL) {
1587
0
    fwupd_error_convert(error);
1588
0
    return FALSE;
1589
0
  }
1590
0
  return fu_firmware_parse_stream(self, G_INPUT_STREAM(stream), 0, flags, error);
1591
0
}
1592
1593
/**
1594
 * fu_firmware_write:
1595
 * @self: a #FuFirmware
1596
 * @error: (nullable): optional return location for an error
1597
 *
1598
 * Writes a firmware, typically packing the images into a binary blob.
1599
 *
1600
 * Returns: (transfer full): a data blob
1601
 *
1602
 * Since: 1.3.1
1603
 **/
1604
GBytes *
1605
fu_firmware_write(FuFirmware *self, GError **error)
1606
235k
{
1607
235k
  FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self);
1608
1609
235k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
1610
235k
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1611
1612
  /* subclassed */
1613
235k
  if (klass->write != NULL) {
1614
233k
    g_autoptr(GByteArray) buf = klass->write(self, error);
1615
233k
    if (buf == NULL)
1616
4.35k
      return NULL;
1617
228k
    return g_bytes_new(buf->data, buf->len);
1618
233k
  }
1619
1620
  /* just add default blob */
1621
2.54k
  return fu_firmware_get_bytes_with_patches(self, error);
1622
235k
}
1623
1624
/**
1625
 * fu_firmware_add_patch:
1626
 * @self: a #FuFirmware
1627
 * @offset: an address smaller than fu_firmware_get_size()
1628
 * @blob: (not nullable): bytes to replace
1629
 *
1630
 * Adds a byte patch at a specific offset. If a patch already exists at the specified address then
1631
 * it is replaced.
1632
 *
1633
 * If the @address is larger than the size of the image then an error is returned.
1634
 *
1635
 * Since: 1.7.4
1636
 **/
1637
void
1638
fu_firmware_add_patch(FuFirmware *self, gsize offset, GBytes *blob)
1639
0
{
1640
0
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
1641
0
  FuFirmwarePatch *ptch;
1642
1643
0
  g_return_if_fail(FU_IS_FIRMWARE(self));
1644
0
  g_return_if_fail(blob != NULL);
1645
1646
  /* ensure exists */
1647
0
  if (priv->patches == NULL) {
1648
0
    priv->patches =
1649
0
        g_ptr_array_new_with_free_func((GDestroyNotify)fu_firmware_patch_free);
1650
0
  }
1651
1652
  /* find existing of exact same size */
1653
0
  for (guint i = 0; i < priv->patches->len; i++) {
1654
0
    ptch = g_ptr_array_index(priv->patches, i);
1655
0
    if (ptch->offset == offset &&
1656
0
        g_bytes_get_size(ptch->blob) == g_bytes_get_size(blob)) {
1657
0
      g_bytes_unref(ptch->blob);
1658
0
      ptch->blob = g_bytes_ref(blob);
1659
0
      return;
1660
0
    }
1661
0
  }
1662
1663
  /* add new */
1664
0
  ptch = g_new0(FuFirmwarePatch, 1);
1665
0
  ptch->offset = offset;
1666
0
  ptch->blob = g_bytes_ref(blob);
1667
0
  g_ptr_array_add(priv->patches, ptch);
1668
0
}
1669
1670
/**
1671
 * fu_firmware_write_chunk:
1672
 * @self: a #FuFirmware
1673
 * @address: an address smaller than fu_firmware_get_addr()
1674
 * @chunk_sz_max: the size of the new chunk
1675
 * @error: (nullable): optional return location for an error
1676
 *
1677
 * Gets a block of data from the image. If the contents of the image is
1678
 * smaller than the requested chunk size then the #GBytes will be smaller
1679
 * than @chunk_sz_max. Use fu_bytes_pad() if padding is required.
1680
 *
1681
 * If the @address is larger than the size of the image then an error is returned.
1682
 *
1683
 * Returns: (transfer full): a #GBytes, or %NULL
1684
 *
1685
 * Since: 1.6.0
1686
 **/
1687
GBytes *
1688
fu_firmware_write_chunk(FuFirmware *self, guint64 address, guint64 chunk_sz_max, GError **error)
1689
0
{
1690
0
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
1691
0
  gsize chunk_left;
1692
0
  guint64 offset;
1693
1694
0
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
1695
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1696
1697
  /* check address requested is larger than base address */
1698
0
  if (address < priv->addr) {
1699
0
    g_set_error(error,
1700
0
          FWUPD_ERROR,
1701
0
          FWUPD_ERROR_INTERNAL,
1702
0
          "requested address 0x%x less than base address 0x%x",
1703
0
          (guint)address,
1704
0
          (guint)priv->addr);
1705
0
    return NULL;
1706
0
  }
1707
1708
  /* offset into data */
1709
0
  offset = address - priv->addr;
1710
0
  if (offset > g_bytes_get_size(priv->bytes)) {
1711
0
    g_set_error(error,
1712
0
          FWUPD_ERROR,
1713
0
          FWUPD_ERROR_NOT_FOUND,
1714
0
          "offset 0x%x larger than data size 0x%x",
1715
0
          (guint)offset,
1716
0
          (guint)g_bytes_get_size(priv->bytes));
1717
0
    return NULL;
1718
0
  }
1719
1720
  /* if we have less data than requested */
1721
0
  chunk_left = g_bytes_get_size(priv->bytes) - offset;
1722
0
  if (chunk_sz_max > chunk_left)
1723
0
    return fu_bytes_new_offset(priv->bytes, offset, chunk_left, error);
1724
1725
  /* check chunk */
1726
0
  return fu_bytes_new_offset(priv->bytes, offset, chunk_sz_max, error);
1727
0
}
1728
1729
/**
1730
 * fu_firmware_write_file:
1731
 * @self: a #FuFirmware
1732
 * @file: a file
1733
 * @error: (nullable): optional return location for an error
1734
 *
1735
 * Writes a firmware, typically packing the images into a binary blob.
1736
 *
1737
 * Returns: %TRUE for success
1738
 *
1739
 * Since: 1.3.3
1740
 **/
1741
gboolean
1742
fu_firmware_write_file(FuFirmware *self, GFile *file, GError **error)
1743
0
{
1744
0
  g_autoptr(GBytes) blob = NULL;
1745
0
  g_autoptr(GFile) parent = NULL;
1746
1747
0
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE);
1748
0
  g_return_val_if_fail(G_IS_FILE(file), FALSE);
1749
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1750
1751
0
  blob = fu_firmware_write(self, error);
1752
0
  if (blob == NULL)
1753
0
    return FALSE;
1754
0
  parent = g_file_get_parent(file);
1755
0
  if (!g_file_query_exists(parent, NULL)) {
1756
0
    if (!g_file_make_directory_with_parents(parent, NULL, error))
1757
0
      return FALSE;
1758
0
  }
1759
0
  return g_file_replace_contents(file,
1760
0
               g_bytes_get_data(blob, NULL),
1761
0
               g_bytes_get_size(blob),
1762
0
               NULL,
1763
0
               FALSE,
1764
0
               G_FILE_CREATE_NONE,
1765
0
               NULL,
1766
0
               NULL,
1767
0
               error);
1768
0
}
1769
1770
static void
1771
fu_firmware_set_depth(FuFirmware *self, guint depth)
1772
1.07M
{
1773
1.07M
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
1774
1.07M
  g_return_if_fail(FU_IS_FIRMWARE(self));
1775
1.07M
  priv->depth = depth;
1776
1.07M
}
1777
1778
/**
1779
 * fu_firmware_get_depth:
1780
 * @self: a #FuPlugin
1781
 *
1782
 * Gets the depth of this child image relative to the root.
1783
 *
1784
 * Returns: integer, or 0 for the root.
1785
 *
1786
 * Since: 1.9.14
1787
 **/
1788
guint
1789
fu_firmware_get_depth(FuFirmware *self)
1790
0
{
1791
0
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
1792
0
  g_return_val_if_fail(FU_IS_FIRMWARE(self), G_MAXUINT);
1793
0
  return priv->depth;
1794
0
}
1795
1796
/**
1797
 * fu_firmware_add_image_gtype:
1798
 * @self: a #FuFirmware
1799
 * @type: a #GType, e.g. %FU_TYPE_ELF_FIRMWARE
1800
 *
1801
 * Adds a possible image GType.
1802
 *
1803
 * Since: 2.1.1
1804
 **/
1805
void
1806
fu_firmware_add_image_gtype(FuFirmware *self, GType type)
1807
3.33M
{
1808
3.33M
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
1809
1810
3.33M
  g_return_if_fail(FU_IS_FIRMWARE(self));
1811
3.33M
  g_return_if_fail(type != G_TYPE_INVALID);
1812
1813
3.33M
  g_type_ensure(type);
1814
3.33M
  if (priv->image_gtypes == NULL)
1815
3.10M
    priv->image_gtypes = g_array_new(FALSE, FALSE, sizeof(GType));
1816
3.33M
  g_array_append_val(priv->image_gtypes, type);
1817
3.33M
}
1818
1819
/**
1820
 * fu_firmware_get_image_gtypes:
1821
 * @self: a #FuFirmware
1822
 *
1823
 * Returns all the possible image GTypes.
1824
 *
1825
 * Returns: (element-type GType) (transfer none) (nullable): array of #GType.
1826
 *
1827
 * Since: 2.1.1
1828
 **/
1829
GArray *
1830
fu_firmware_get_image_gtypes(FuFirmware *self)
1831
0
{
1832
0
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
1833
1834
0
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
1835
0
  return priv->image_gtypes;
1836
0
}
1837
1838
static gboolean
1839
fu_firmware_check_image_gtype(FuFirmware *self, GType gtype, GError **error)
1840
1.07M
{
1841
1.07M
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
1842
1.07M
  if (priv->image_gtypes == NULL) {
1843
0
#ifndef SUPPORTED_BUILD
1844
0
    g_critical("%s did not add image GType %s with fu_firmware_add_image_gtype()",
1845
0
         G_OBJECT_TYPE_NAME(self),
1846
0
         g_type_name(gtype));
1847
0
#endif
1848
0
    return TRUE;
1849
0
  }
1850
1.19M
  for (guint i = 0; i < priv->image_gtypes->len; i++) {
1851
1.19M
    GType gtype_tmp = g_array_index(priv->image_gtypes, GType, i);
1852
1.19M
    if (gtype_tmp == gtype)
1853
1.07M
      return TRUE;
1854
1.19M
  }
1855
45
  g_set_error(error,
1856
45
        FWUPD_ERROR,
1857
45
        FWUPD_ERROR_NOT_SUPPORTED,
1858
45
        "cannot add %s to %s",
1859
45
        g_type_name(gtype),
1860
45
        G_OBJECT_TYPE_NAME(self));
1861
45
  return FALSE;
1862
1.07M
}
1863
1864
/**
1865
 * fu_firmware_add_image:
1866
 * @self: a #FuPlugin
1867
 * @img: a child firmware image
1868
 * @error: (nullable): optional return location for an error
1869
 *
1870
 * Adds an image to the firmware. This method will fail if the number of images would be
1871
 * above the limit set by fu_firmware_set_images_max().
1872
 *
1873
 * If %FU_FIRMWARE_FLAG_DEDUPE_ID is set, an image with the same ID is already
1874
 * present it is replaced.
1875
 *
1876
 * Returns: %TRUE if the image was added
1877
 *
1878
 * Since: 1.9.3, but @error was added in 2.0.17
1879
 **/
1880
gboolean
1881
fu_firmware_add_image(FuFirmware *self, FuFirmware *img, GError **error)
1882
1.07M
{
1883
1.07M
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
1884
1885
1.07M
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE);
1886
1.07M
  g_return_val_if_fail(FU_IS_FIRMWARE(img), FALSE);
1887
1.07M
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1888
1889
  /* check depth */
1890
1.07M
  if (priv->depth > FU_FIRMWARE_IMAGE_DEPTH_MAX) {
1891
20
    g_set_error(error,
1892
20
          FWUPD_ERROR,
1893
20
          FWUPD_ERROR_INVALID_DATA,
1894
20
          "images are nested too deep, limit is %u",
1895
20
          (guint)FU_FIRMWARE_IMAGE_DEPTH_MAX);
1896
20
    return FALSE;
1897
20
  }
1898
1899
  /* check image is a permissible GType */
1900
1.07M
  if (!fu_firmware_check_image_gtype(self, G_OBJECT_TYPE(img), error))
1901
45
    return FALSE;
1902
1903
  /* dedupe */
1904
1.07M
  if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_ID) {
1905
430k
    for (guint i = 0; i < priv->images->len; i++) {
1906
416k
      FuFirmware *img_tmp = g_ptr_array_index(priv->images, i);
1907
416k
      if (g_strcmp0(fu_firmware_get_id(img_tmp), fu_firmware_get_id(img)) == 0) {
1908
10.2k
        g_ptr_array_remove_index(priv->images, i);
1909
10.2k
        break;
1910
10.2k
      }
1911
416k
    }
1912
24.2k
  }
1913
1.07M
  if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_IDX) {
1914
1.37M
    for (guint i = 0; i < priv->images->len; i++) {
1915
1.35M
      FuFirmware *img_tmp = g_ptr_array_index(priv->images, i);
1916
1.35M
      if (fu_firmware_get_idx(img_tmp) == fu_firmware_get_idx(img)) {
1917
147k
        g_ptr_array_remove_index(priv->images, i);
1918
147k
        break;
1919
147k
      }
1920
1.35M
    }
1921
167k
  }
1922
1923
  /* sanity check */
1924
1.07M
  if (priv->images_max > 0 && priv->images->len >= priv->images_max) {
1925
245
    g_set_error(error,
1926
245
          FWUPD_ERROR,
1927
245
          FWUPD_ERROR_INVALID_DATA,
1928
245
          "too many images, limit is %u",
1929
245
          priv->images_max);
1930
245
    return FALSE;
1931
245
  }
1932
1933
1.07M
  g_ptr_array_add(priv->images, g_object_ref(img));
1934
1935
  /* set the other way around */
1936
1.07M
  fu_firmware_set_parent(img, self);
1937
1.07M
  fu_firmware_set_depth(img, priv->depth + 1);
1938
1939
  /* success */
1940
1.07M
  return TRUE;
1941
1.07M
}
1942
1943
/**
1944
 * fu_firmware_set_images_max:
1945
 * @self: a #FuPlugin
1946
 * @images_max: integer, or 0 for unlimited
1947
 *
1948
 * Sets the maximum number of images this container can hold.
1949
 *
1950
 * Since: 1.9.3
1951
 **/
1952
void
1953
fu_firmware_set_images_max(FuFirmware *self, guint images_max)
1954
400k
{
1955
400k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
1956
400k
  g_return_if_fail(FU_IS_FIRMWARE(self));
1957
400k
  priv->images_max = images_max;
1958
400k
}
1959
1960
/**
1961
 * fu_firmware_get_images_max:
1962
 * @self: a #FuPlugin
1963
 *
1964
 * Gets the maximum number of images this container can hold.
1965
 *
1966
 * Returns: integer, or 0 for unlimited.
1967
 *
1968
 * Since: 1.9.3
1969
 **/
1970
guint
1971
fu_firmware_get_images_max(FuFirmware *self)
1972
0
{
1973
0
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
1974
0
  g_return_val_if_fail(FU_IS_FIRMWARE(self), G_MAXUINT);
1975
0
  return priv->images_max;
1976
0
}
1977
1978
/**
1979
 * fu_firmware_remove_image:
1980
 * @self: a #FuPlugin
1981
 * @img: a child firmware image
1982
 * @error: (nullable): optional return location for an error
1983
 *
1984
 * Remove an image from the firmware.
1985
 *
1986
 * Returns: %TRUE if the image was removed
1987
 *
1988
 * Since: 1.5.0
1989
 **/
1990
gboolean
1991
fu_firmware_remove_image(FuFirmware *self, FuFirmware *img, GError **error)
1992
0
{
1993
0
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
1994
1995
0
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE);
1996
0
  g_return_val_if_fail(FU_IS_FIRMWARE(img), FALSE);
1997
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1998
1999
0
  if (g_ptr_array_remove(priv->images, img))
2000
0
    return TRUE;
2001
2002
  /* did not exist */
2003
0
  g_set_error(error,
2004
0
        FWUPD_ERROR,
2005
0
        FWUPD_ERROR_NOT_FOUND,
2006
0
        "image %s not found in firmware",
2007
0
        fu_firmware_get_id(img));
2008
0
  return FALSE;
2009
0
}
2010
2011
/**
2012
 * fu_firmware_remove_image_by_idx:
2013
 * @self: a #FuPlugin
2014
 * @idx: index
2015
 * @error: (nullable): optional return location for an error
2016
 *
2017
 * Removes the first image from the firmware matching the index.
2018
 *
2019
 * Returns: %TRUE if an image was removed
2020
 *
2021
 * Since: 1.5.0
2022
 **/
2023
gboolean
2024
fu_firmware_remove_image_by_idx(FuFirmware *self, guint64 idx, GError **error)
2025
0
{
2026
0
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
2027
0
  g_autoptr(FuFirmware) img = NULL;
2028
2029
0
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE);
2030
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
2031
2032
0
  img = fu_firmware_get_image_by_idx(self, idx, error);
2033
0
  if (img == NULL)
2034
0
    return FALSE;
2035
0
  g_ptr_array_remove(priv->images, img);
2036
0
  return TRUE;
2037
0
}
2038
2039
/**
2040
 * fu_firmware_remove_image_by_id:
2041
 * @self: a #FuPlugin
2042
 * @id: (nullable): image ID, e.g. `config`
2043
 * @error: (nullable): optional return location for an error
2044
 *
2045
 * Removes the first image from the firmware matching the ID.
2046
 *
2047
 * Returns: %TRUE if an image was removed
2048
 *
2049
 * Since: 1.5.0
2050
 **/
2051
gboolean
2052
fu_firmware_remove_image_by_id(FuFirmware *self, const gchar *id, GError **error)
2053
0
{
2054
0
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
2055
0
  g_autoptr(FuFirmware) img = NULL;
2056
2057
0
  g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE);
2058
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
2059
2060
0
  img = fu_firmware_get_image_by_id(self, id, error);
2061
0
  if (img == NULL)
2062
0
    return FALSE;
2063
0
  g_ptr_array_remove(priv->images, img);
2064
0
  return TRUE;
2065
0
}
2066
2067
/**
2068
 * fu_firmware_get_images:
2069
 * @self: a #FuFirmware
2070
 *
2071
 * Returns all the images in the firmware.
2072
 *
2073
 * Returns: (transfer container) (element-type FuFirmware): images
2074
 *
2075
 * Since: 1.3.1
2076
 **/
2077
GPtrArray *
2078
fu_firmware_get_images(FuFirmware *self)
2079
39.6k
{
2080
39.6k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
2081
39.6k
  g_autoptr(GPtrArray) imgs = NULL;
2082
2083
39.6k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
2084
2085
39.6k
  imgs = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
2086
308k
  for (guint i = 0; i < priv->images->len; i++) {
2087
268k
    FuFirmware *img = g_ptr_array_index(priv->images, i);
2088
268k
    g_ptr_array_add(imgs, g_object_ref(img));
2089
268k
  }
2090
39.6k
  return g_steal_pointer(&imgs);
2091
39.6k
}
2092
2093
/**
2094
 * fu_firmware_get_image_by_id:
2095
 * @self: a #FuPlugin
2096
 * @id: (nullable): image ID, e.g. `config` or `*.mfg|*.elf`
2097
 * @error: (nullable): optional return location for an error
2098
 *
2099
 * Gets the firmware image using the image ID.
2100
 *
2101
 * Returns: (transfer full): a #FuFirmware, or %NULL if the image is not found
2102
 *
2103
 * Since: 1.3.1
2104
 **/
2105
FuFirmware *
2106
fu_firmware_get_image_by_id(FuFirmware *self, const gchar *id, GError **error)
2107
5.04k
{
2108
5.04k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
2109
5.04k
  g_autofree gchar *id_str = NULL;
2110
5.04k
  g_autoptr(GPtrArray) id_errmsg = g_ptr_array_new();
2111
2112
5.04k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
2113
5.04k
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
2114
2115
  /* sanity check */
2116
5.04k
  if (priv->images->len == 0) {
2117
1.35k
    g_set_error_literal(error,
2118
1.35k
            FWUPD_ERROR,
2119
1.35k
            FWUPD_ERROR_NOT_FOUND,
2120
1.35k
            "no images in firmware");
2121
1.35k
    return NULL;
2122
1.35k
  }
2123
2124
  /* non-NULL */
2125
3.69k
  if (id != NULL) {
2126
2.70k
    g_auto(GStrv) split = g_strsplit(id, "|", 0);
2127
12.5k
    for (guint i = 0; i < priv->images->len; i++) {
2128
12.3k
      FuFirmware *img = g_ptr_array_index(priv->images, i);
2129
22.1k
      for (guint j = 0; split[j] != NULL; j++) {
2130
12.3k
        if (fu_firmware_get_id(img) == NULL)
2131
96
          continue;
2132
12.2k
        if (g_pattern_match_simple(split[j], fu_firmware_get_id(img)))
2133
2.41k
          return g_object_ref(img);
2134
12.2k
      }
2135
12.3k
    }
2136
2.70k
  } else {
2137
4.19k
    for (guint i = 0; i < priv->images->len; i++) {
2138
4.12k
      FuFirmware *img = g_ptr_array_index(priv->images, i);
2139
4.12k
      if (fu_firmware_get_id(img) == NULL)
2140
926
        return g_object_ref(img);
2141
4.12k
    }
2142
992
  }
2143
2144
  /* build a useful error */
2145
8.84k
  for (guint i = 0; i < priv->images->len; i++) {
2146
8.49k
    FuFirmware *img = g_ptr_array_index(priv->images, i);
2147
8.49k
    g_ptr_array_add(id_errmsg, (gpointer)fu_firmware_get_id(img));
2148
8.49k
  }
2149
352
  id_str = fu_strjoin(",", id_errmsg);
2150
352
  g_set_error(error,
2151
352
        FWUPD_ERROR,
2152
352
        FWUPD_ERROR_NOT_FOUND,
2153
352
        "no image id %s found in firmware, only have %s",
2154
352
        id,
2155
352
        id_str);
2156
352
  return NULL;
2157
3.69k
}
2158
2159
/**
2160
 * fu_firmware_get_image_by_id_bytes:
2161
 * @self: a #FuPlugin
2162
 * @id: (nullable): image ID, e.g. `config`
2163
 * @error: (nullable): optional return location for an error
2164
 *
2165
 * Gets the firmware image bytes using the image ID.
2166
 *
2167
 * Returns: (transfer full): a #GBytes of a #FuFirmware, or %NULL if the image is not found
2168
 *
2169
 * Since: 1.3.1
2170
 **/
2171
GBytes *
2172
fu_firmware_get_image_by_id_bytes(FuFirmware *self, const gchar *id, GError **error)
2173
1.82k
{
2174
1.82k
  g_autoptr(FuFirmware) img = fu_firmware_get_image_by_id(self, id, error);
2175
1.82k
  if (img == NULL)
2176
1.13k
    return NULL;
2177
694
  return fu_firmware_write(img, error);
2178
1.82k
}
2179
2180
/**
2181
 * fu_firmware_get_image_by_id_stream:
2182
 * @self: a #FuPlugin
2183
 * @id: (nullable): image ID, e.g. `config`
2184
 * @error: (nullable): optional return location for an error
2185
 *
2186
 * Gets the firmware image stream using the image ID.
2187
 *
2188
 * Returns: (transfer full): a #GInputStream of a #FuFirmware, or %NULL if the image is not found
2189
 *
2190
 * Since: 2.0.0
2191
 **/
2192
GInputStream *
2193
fu_firmware_get_image_by_id_stream(FuFirmware *self, const gchar *id, GError **error)
2194
0
{
2195
0
  g_autoptr(FuFirmware) img = fu_firmware_get_image_by_id(self, id, error);
2196
0
  if (img == NULL)
2197
0
    return NULL;
2198
0
  return fu_firmware_get_stream(img, error);
2199
0
}
2200
2201
/**
2202
 * fu_firmware_get_image_by_idx:
2203
 * @self: a #FuPlugin
2204
 * @idx: image index
2205
 * @error: (nullable): optional return location for an error
2206
 *
2207
 * Gets the firmware image using the image index.
2208
 *
2209
 * Returns: (transfer full): a #FuFirmware, or %NULL if the image is not found
2210
 *
2211
 * Since: 1.3.1
2212
 **/
2213
FuFirmware *
2214
fu_firmware_get_image_by_idx(FuFirmware *self, guint64 idx, GError **error)
2215
8.55k
{
2216
8.55k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
2217
2218
8.55k
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
2219
8.55k
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
2220
2221
261k
  for (guint i = 0; i < priv->images->len; i++) {
2222
255k
    FuFirmware *img = g_ptr_array_index(priv->images, i);
2223
255k
    if (fu_firmware_get_idx(img) == idx)
2224
2.83k
      return g_object_ref(img);
2225
255k
  }
2226
5.72k
  g_set_error(error,
2227
5.72k
        FWUPD_ERROR,
2228
5.72k
        FWUPD_ERROR_NOT_FOUND,
2229
5.72k
        "no image idx %" G_GUINT64_FORMAT " found in firmware",
2230
5.72k
        idx);
2231
5.72k
  return NULL;
2232
8.55k
}
2233
2234
/**
2235
 * fu_firmware_get_image_by_checksum:
2236
 * @self: a #FuPlugin
2237
 * @checksum: checksum string of any format
2238
 * @error: (nullable): optional return location for an error
2239
 *
2240
 * Gets the firmware image using the image checksum. The checksum type is guessed
2241
 * based on the length of the input string.
2242
 *
2243
 * Returns: (transfer full): a #FuFirmware, or %NULL if the image is not found
2244
 *
2245
 * Since: 1.5.5
2246
 **/
2247
FuFirmware *
2248
fu_firmware_get_image_by_checksum(FuFirmware *self, const gchar *checksum, GError **error)
2249
0
{
2250
0
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
2251
0
  GChecksumType csum_kind;
2252
2253
0
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
2254
0
  g_return_val_if_fail(checksum != NULL, NULL);
2255
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
2256
2257
0
  csum_kind = fwupd_checksum_guess_kind(checksum);
2258
0
  for (guint i = 0; i < priv->images->len; i++) {
2259
0
    FuFirmware *img = g_ptr_array_index(priv->images, i);
2260
0
    g_autofree gchar *checksum_tmp = NULL;
2261
2262
    /* if this expensive then the subclassed FuFirmware can
2263
     * cache the result as required */
2264
0
    checksum_tmp = fu_firmware_get_checksum(img, csum_kind, error);
2265
0
    if (checksum_tmp == NULL)
2266
0
      return NULL;
2267
0
    if (g_strcmp0(checksum_tmp, checksum) == 0)
2268
0
      return g_object_ref(img);
2269
0
  }
2270
0
  g_set_error(error,
2271
0
        FWUPD_ERROR,
2272
0
        FWUPD_ERROR_NOT_FOUND,
2273
0
        "no image with checksum %s found in firmware",
2274
0
        checksum);
2275
0
  return NULL;
2276
0
}
2277
2278
/**
2279
 * fu_firmware_get_image_by_idx_bytes:
2280
 * @self: a #FuPlugin
2281
 * @idx: image index
2282
 * @error: (nullable): optional return location for an error
2283
 *
2284
 * Gets the firmware image bytes using the image index.
2285
 *
2286
 * Returns: (transfer full): a #GBytes of a #FuFirmware, or %NULL if the image is not found
2287
 *
2288
 * Since: 1.3.1
2289
 **/
2290
GBytes *
2291
fu_firmware_get_image_by_idx_bytes(FuFirmware *self, guint64 idx, GError **error)
2292
0
{
2293
0
  g_autoptr(FuFirmware) img = fu_firmware_get_image_by_idx(self, idx, error);
2294
0
  if (img == NULL)
2295
0
    return NULL;
2296
0
  return fu_firmware_write(img, error);
2297
0
}
2298
2299
/**
2300
 * fu_firmware_get_image_by_idx_stream:
2301
 * @self: a #FuPlugin
2302
 * @idx: image index
2303
 * @error: (nullable): optional return location for an error
2304
 *
2305
 * Gets the firmware image stream using the image index.
2306
 *
2307
 * Returns: (transfer full): a #GInputStream of a #FuFirmware, or %NULL if the image is not found
2308
 *
2309
 * Since: 2.0.0
2310
 **/
2311
GInputStream *
2312
fu_firmware_get_image_by_idx_stream(FuFirmware *self, guint64 idx, GError **error)
2313
0
{
2314
0
  g_autoptr(FuFirmware) img = fu_firmware_get_image_by_idx(self, idx, error);
2315
0
  if (img == NULL)
2316
0
    return NULL;
2317
0
  return fu_firmware_get_stream(img, error);
2318
0
}
2319
2320
/**
2321
 * fu_firmware_get_image_by_gtype_bytes:
2322
 * @self: a #FuPlugin
2323
 * @gtype: an image #GType
2324
 * @error: (nullable): optional return location for an error
2325
 *
2326
 * Gets the firmware image bytes using the image #GType.
2327
 *
2328
 * Returns: (transfer full): a #GBytes of a #FuFirmware, or %NULL if the image is not found
2329
 *
2330
 * Since: 1.9.3
2331
 **/
2332
GBytes *
2333
fu_firmware_get_image_by_gtype_bytes(FuFirmware *self, GType gtype, GError **error)
2334
982
{
2335
982
  g_autoptr(FuFirmware) img = fu_firmware_get_image_by_gtype(self, gtype, error);
2336
982
  if (img == NULL)
2337
0
    return NULL;
2338
982
  return fu_firmware_write(img, error);
2339
982
}
2340
2341
/**
2342
 * fu_firmware_get_image_by_gtype:
2343
 * @self: a #FuPlugin
2344
 * @gtype: an image #GType
2345
 * @error: (nullable): optional return location for an error
2346
 *
2347
 * Gets the firmware image using the image #GType.
2348
 *
2349
 * Returns: (transfer full): a #FuFirmware, or %NULL if the image is not found
2350
 *
2351
 * Since: 1.9.3
2352
 **/
2353
FuFirmware *
2354
fu_firmware_get_image_by_gtype(FuFirmware *self, GType gtype, GError **error)
2355
982
{
2356
982
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
2357
2358
982
  g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL);
2359
982
  g_return_val_if_fail(gtype != G_TYPE_INVALID, NULL);
2360
982
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
2361
2362
982
  for (guint i = 0; i < priv->images->len; i++) {
2363
982
    FuFirmware *img = g_ptr_array_index(priv->images, i);
2364
982
    if (g_type_is_a(G_OBJECT_TYPE(img), gtype))
2365
982
      return g_object_ref(img);
2366
982
  }
2367
0
  g_set_error(error,
2368
0
        FWUPD_ERROR,
2369
0
        FWUPD_ERROR_NOT_FOUND,
2370
0
        "no image GType %s found in firmware",
2371
0
        g_type_name(gtype));
2372
0
  return NULL;
2373
982
}
2374
2375
static gint
2376
fu_firmware_sort_by_id_cb(gconstpointer a, gconstpointer b)
2377
0
{
2378
0
  FuFirmware *firmware1 = *((FuFirmware **)a);
2379
0
  FuFirmware *firmware2 = *((FuFirmware **)b);
2380
0
  return g_strcmp0(fu_firmware_get_id(firmware1), fu_firmware_get_id(firmware2));
2381
0
}
2382
2383
static gint
2384
fu_firmware_sort_by_idx_cb(gconstpointer a, gconstpointer b)
2385
0
{
2386
0
  FuFirmware *firmware1 = *((FuFirmware **)a);
2387
0
  FuFirmware *firmware2 = *((FuFirmware **)b);
2388
0
  guint64 idx1 = fu_firmware_get_idx(firmware1);
2389
0
  guint64 idx2 = fu_firmware_get_idx(firmware2);
2390
0
  if (idx1 < idx2)
2391
0
    return -1;
2392
0
  if (idx1 > idx2)
2393
0
    return 1;
2394
0
  return 0;
2395
0
}
2396
2397
static GPtrArray *
2398
fu_firmware_get_images_sorted(FuFirmware *self)
2399
0
{
2400
0
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
2401
0
  g_autoptr(GPtrArray) images = g_ptr_array_copy(priv->images, (GCopyFunc)g_object_ref, NULL);
2402
0
  if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_IDX)
2403
0
    g_ptr_array_sort(images, fu_firmware_sort_by_idx_cb);
2404
0
  if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_ID)
2405
0
    g_ptr_array_sort(images, fu_firmware_sort_by_id_cb);
2406
0
  return g_steal_pointer(&images);
2407
0
}
2408
2409
/**
2410
 * fu_firmware_export:
2411
 * @self: a #FuFirmware
2412
 * @flags: firmware export flags, e.g. %FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG
2413
 * @bn: a Xmlb builder node
2414
 *
2415
 * This allows us to build an XML object for the nested firmware.
2416
 *
2417
 * The image children will have a predictable order if the firmware has
2418
 * %FU_FIRMWARE_FLAG_DEDUPE_ID or %FU_FIRMWARE_FLAG_DEDUPE_IDX and
2419
 * %FU_FIRMWARE_EXPORT_FLAG_SORTED is also used in @flags.
2420
 *
2421
 * Since: 1.6.0
2422
 **/
2423
void
2424
fu_firmware_export(FuFirmware *self, FuFirmwareExportFlags flags, XbBuilderNode *bn)
2425
160k
{
2426
160k
  FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self);
2427
160k
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
2428
160k
  const gchar *gtypestr = G_OBJECT_TYPE_NAME(self);
2429
2430
  /* object */
2431
160k
  if (g_strcmp0(gtypestr, "FuFirmware") != 0)
2432
160k
    xb_builder_node_set_attr(bn, "gtype", gtypestr);
2433
2434
  /* subclassed type */
2435
160k
  if (priv->flags != FU_FIRMWARE_FLAG_NONE) {
2436
160k
    g_autofree gchar *str = fu_firmware_flags_to_string(priv->flags);
2437
160k
    fu_xmlb_builder_insert_kv(bn, "flags", str);
2438
160k
  }
2439
160k
  fu_xmlb_builder_insert_kv(bn, "id", priv->id);
2440
160k
  fu_xmlb_builder_insert_kx(bn, "idx", priv->idx);
2441
160k
  fu_xmlb_builder_insert_kv(bn, "version", priv->version);
2442
160k
  fu_xmlb_builder_insert_kx(bn, "version_raw", priv->version_raw);
2443
160k
  if (priv->version_format != FWUPD_VERSION_FORMAT_UNKNOWN) {
2444
0
    fu_xmlb_builder_insert_kv(bn,
2445
0
            "version_format",
2446
0
            fwupd_version_format_to_string(priv->version_format));
2447
0
  }
2448
160k
  fu_xmlb_builder_insert_kx(bn, "addr", priv->addr);
2449
160k
  fu_xmlb_builder_insert_kx(bn, "offset", priv->offset);
2450
160k
  fu_xmlb_builder_insert_kx(bn, "alignment", priv->alignment);
2451
160k
  fu_xmlb_builder_insert_kx(bn, "size", priv->size);
2452
160k
  fu_xmlb_builder_insert_kx(bn, "size_max", priv->size_max);
2453
160k
  fu_xmlb_builder_insert_kv(bn, "filename", priv->filename);
2454
160k
  if (priv->stream != NULL) {
2455
152k
    g_autofree gchar *dataszstr = g_strdup_printf("0x%x", (guint)priv->streamsz);
2456
152k
    g_autofree gchar *datastr = NULL;
2457
152k
    if (priv->streamsz <= 0x100) {
2458
152k
      g_autoptr(GByteArray) buf = fu_input_stream_read_byte_array(priv->stream,
2459
152k
                        0x0,
2460
152k
                        priv->streamsz,
2461
152k
                        NULL,
2462
152k
                        NULL);
2463
152k
      if (buf != NULL) {
2464
32.7k
        if (flags & FU_FIRMWARE_EXPORT_FLAG_ASCII_DATA) {
2465
32.7k
          datastr = fu_memstrsafe(buf->data,
2466
32.7k
                buf->len,
2467
32.7k
                0x0,
2468
32.7k
                MIN(buf->len, 0x100),
2469
32.7k
                NULL);
2470
32.7k
        } else {
2471
0
          datastr = g_base64_encode(buf->data, buf->len);
2472
0
        }
2473
32.7k
      }
2474
152k
    }
2475
152k
    xb_builder_node_insert_text(bn,
2476
152k
              "data",
2477
152k
              datastr,
2478
152k
              "type",
2479
152k
              "GInputStream",
2480
152k
              "size",
2481
152k
              dataszstr,
2482
152k
              NULL);
2483
152k
  } else if (priv->bytes != NULL && g_bytes_get_size(priv->bytes) == 0) {
2484
0
    xb_builder_node_insert_text(bn, "data", NULL, "type", "GBytes", NULL);
2485
8.44k
  } else if (priv->bytes != NULL) {
2486
0
    gsize bufsz = 0;
2487
0
    const guint8 *buf = g_bytes_get_data(priv->bytes, &bufsz);
2488
0
    g_autofree gchar *datastr = NULL;
2489
0
    g_autofree gchar *dataszstr = g_strdup_printf("0x%x", (guint)bufsz);
2490
0
    if (flags & FU_FIRMWARE_EXPORT_FLAG_ASCII_DATA) {
2491
0
      datastr = fu_memstrsafe(buf, bufsz, 0x0, MIN(bufsz, 0x100), NULL);
2492
0
    } else {
2493
0
      datastr = g_base64_encode(buf, bufsz);
2494
0
    }
2495
0
    xb_builder_node_insert_text(bn,
2496
0
              "data",
2497
0
              datastr,
2498
0
              "type",
2499
0
              "GBytes",
2500
0
              "size",
2501
0
              dataszstr,
2502
0
              NULL);
2503
0
  }
2504
2505
  /* chunks */
2506
160k
  if (priv->chunks != NULL && priv->chunks->len > 0) {
2507
0
    g_autoptr(XbBuilderNode) bp = xb_builder_node_insert(bn, "chunks", NULL);
2508
0
    for (guint i = 0; i < priv->chunks->len; i++) {
2509
0
      FuChunk *chk = g_ptr_array_index(priv->chunks, i);
2510
0
      g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bp, "chunk", NULL);
2511
0
      fu_chunk_export(chk, flags, bc);
2512
0
    }
2513
0
  }
2514
2515
  /* magic */
2516
160k
  if (priv->magic != NULL && priv->magic->len > 0) {
2517
0
    g_autoptr(XbBuilderNode) bp = xb_builder_node_insert(bn, "magic", NULL);
2518
0
    for (guint i = 0; i < priv->magic->len; i++) {
2519
0
      FuFirmwarePatch *patch = g_ptr_array_index(priv->magic, i);
2520
0
      g_autofree gchar *str = fu_bytes_to_string(patch->blob);
2521
0
      g_autofree gchar *offset = g_strdup_printf("0x%x", (guint)patch->offset);
2522
0
      xb_builder_node_insert_text(bp, "data", str, "offset", offset, NULL);
2523
0
    }
2524
0
  }
2525
2526
  /* image gtypes */
2527
160k
  if ((flags & FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG) > 0 && priv->image_gtypes != NULL &&
2528
0
      priv->image_gtypes->len > 0) {
2529
0
    g_autoptr(XbBuilderNode) bp = xb_builder_node_insert(bn, "image_gtypes", NULL);
2530
0
    for (guint i = 0; i < priv->image_gtypes->len; i++) {
2531
0
      GType gtype_tmp = g_array_index(priv->image_gtypes, GType, i);
2532
0
      xb_builder_node_insert_text(bp, "gtype", g_type_name(gtype_tmp), NULL);
2533
0
    }
2534
0
  }
2535
2536
  /* vfunc */
2537
160k
  if (klass->export != NULL)
2538
160k
    klass->export(self, flags, bn);
2539
2540
  /* children */
2541
160k
  if (priv->images->len > 0) {
2542
0
    g_autoptr(GPtrArray) images = flags & FU_FIRMWARE_EXPORT_FLAG_SORTED
2543
0
              ? fu_firmware_get_images_sorted(self)
2544
0
              : g_ptr_array_ref(priv->images);
2545
0
    for (guint i = 0; i < images->len; i++) {
2546
0
      FuFirmware *img = g_ptr_array_index(images, i);
2547
0
      g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "firmware", NULL);
2548
0
      fu_firmware_export(img, flags, bc);
2549
0
    }
2550
0
  }
2551
160k
}
2552
2553
/**
2554
 * fu_firmware_export_to_xml:
2555
 * @self: a #FuFirmware
2556
 * @flags: firmware export flags, e.g. %FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG
2557
 * @error: (nullable): optional return location for an error
2558
 *
2559
 * This allows us to build an XML object for the nested firmware.
2560
 *
2561
 * Returns: a string value, or %NULL for invalid.
2562
 *
2563
 * Since: 1.6.0
2564
 **/
2565
gchar *
2566
fu_firmware_export_to_xml(FuFirmware *self, FuFirmwareExportFlags flags, GError **error)
2567
0
{
2568
0
  g_autoptr(XbBuilderNode) bn = xb_builder_node_new("firmware");
2569
0
  fu_firmware_export(self, flags, bn);
2570
0
  return xb_builder_node_export(bn,
2571
0
              XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE |
2572
0
            XB_NODE_EXPORT_FLAG_COLLAPSE_EMPTY |
2573
0
            XB_NODE_EXPORT_FLAG_FORMAT_INDENT,
2574
0
              error);
2575
0
}
2576
2577
/**
2578
 * fu_firmware_to_string:
2579
 * @self: a #FuFirmware
2580
 *
2581
 * This allows us to easily print the object.
2582
 *
2583
 * Returns: a string value, or %NULL for invalid.
2584
 *
2585
 * Since: 1.3.1
2586
 **/
2587
gchar *
2588
fu_firmware_to_string(FuFirmware *self)
2589
160k
{
2590
160k
  g_autoptr(XbBuilderNode) bn = xb_builder_node_new("firmware");
2591
160k
  fu_firmware_export(self,
2592
160k
         FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG |
2593
160k
             FU_FIRMWARE_EXPORT_FLAG_ASCII_DATA,
2594
160k
         bn);
2595
160k
  return xb_builder_node_export(bn,
2596
160k
              XB_NODE_EXPORT_FLAG_FORMAT_MULTILINE |
2597
160k
            XB_NODE_EXPORT_FLAG_COLLAPSE_EMPTY |
2598
160k
            XB_NODE_EXPORT_FLAG_FORMAT_INDENT,
2599
160k
              NULL);
2600
160k
}
2601
2602
static void
2603
fu_firmware_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
2604
0
{
2605
0
  FuFirmware *self = FU_FIRMWARE(object);
2606
0
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
2607
0
  switch (prop_id) {
2608
0
  case PROP_PARENT:
2609
0
    g_value_set_object(value, priv->parent);
2610
0
    break;
2611
0
  default:
2612
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
2613
0
    break;
2614
0
  }
2615
0
}
2616
2617
static void
2618
fu_firmware_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
2619
16.3M
{
2620
16.3M
  FuFirmware *self = FU_FIRMWARE(object);
2621
16.3M
  switch (prop_id) {
2622
16.3M
  case PROP_PARENT:
2623
16.3M
    fu_firmware_set_parent(self, g_value_get_object(value));
2624
16.3M
    break;
2625
0
  default:
2626
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
2627
0
    break;
2628
16.3M
  }
2629
16.3M
}
2630
2631
static void
2632
fu_firmware_init(FuFirmware *self)
2633
16.3M
{
2634
16.3M
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
2635
16.3M
  priv->images = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
2636
16.3M
}
2637
2638
static void
2639
fu_firmware_constructed(GObject *obj)
2640
16.3M
{
2641
16.3M
  FuFirmware *self = FU_FIRMWARE(obj);
2642
16.3M
  FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self);
2643
16.3M
  if (klass->add_magic != NULL)
2644
61.7k
    klass->add_magic(self);
2645
16.3M
  if (G_TYPE_FROM_CLASS(klass) != FU_TYPE_FIRMWARE && klass->parse_full == NULL &&
2646
10.2M
      klass->parse == NULL)
2647
142k
    fu_firmware_add_flag(self, FU_FIRMWARE_FLAG_IS_ABSTRACT);
2648
16.3M
}
2649
2650
static void
2651
fu_firmware_finalize(GObject *object)
2652
16.3M
{
2653
16.3M
  FuFirmware *self = FU_FIRMWARE(object);
2654
16.3M
  FuFirmwarePrivate *priv = GET_PRIVATE(self);
2655
16.3M
  g_free(priv->version);
2656
16.3M
  g_free(priv->id);
2657
16.3M
  g_free(priv->filename);
2658
16.3M
  if (priv->bytes != NULL)
2659
235k
    g_bytes_unref(priv->bytes);
2660
16.3M
  if (priv->stream != NULL)
2661
219k
    g_object_unref(priv->stream);
2662
16.3M
  if (priv->chunks != NULL)
2663
735
    g_ptr_array_unref(priv->chunks);
2664
16.3M
  if (priv->image_gtypes != NULL)
2665
3.10M
    g_array_unref(priv->image_gtypes);
2666
16.3M
  if (priv->patches != NULL)
2667
0
    g_ptr_array_unref(priv->patches);
2668
16.3M
  if (priv->magic != NULL)
2669
61.7k
    g_ptr_array_unref(priv->magic);
2670
16.3M
  if (priv->parent != NULL)
2671
49.9k
    g_object_remove_weak_pointer(G_OBJECT(priv->parent), (gpointer *)&priv->parent);
2672
16.3M
  g_ptr_array_unref(priv->images);
2673
16.3M
  G_OBJECT_CLASS(fu_firmware_parent_class)->finalize(object);
2674
16.3M
}
2675
2676
static void
2677
fu_firmware_class_init(FuFirmwareClass *klass)
2678
42
{
2679
42
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
2680
42
  GParamSpec *pspec;
2681
2682
42
  object_class->finalize = fu_firmware_finalize;
2683
42
  object_class->get_property = fu_firmware_get_property;
2684
42
  object_class->set_property = fu_firmware_set_property;
2685
42
  object_class->constructed = fu_firmware_constructed;
2686
2687
  /**
2688
   * FuFirmware:parent:
2689
   *
2690
   * The firmware parent.
2691
   *
2692
   * Since: 1.8.2
2693
   */
2694
42
  pspec = g_param_spec_object("parent",
2695
42
            NULL,
2696
42
            NULL,
2697
42
            FU_TYPE_FIRMWARE,
2698
42
            G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME);
2699
42
  g_object_class_install_property(object_class, PROP_PARENT, pspec);
2700
42
}
2701
2702
/**
2703
 * fu_firmware_new:
2704
 *
2705
 * Creates an empty firmware object.
2706
 *
2707
 * Returns: a #FuFirmware
2708
 *
2709
 * Since: 1.3.1
2710
 **/
2711
FuFirmware *
2712
fu_firmware_new(void)
2713
6.05M
{
2714
6.05M
  FuFirmware *self = g_object_new(FU_TYPE_FIRMWARE, NULL);
2715
6.05M
  return FU_FIRMWARE(self);
2716
6.05M
}
2717
2718
/**
2719
 * fu_firmware_new_from_bytes:
2720
 * @fw: firmware blob image
2721
 *
2722
 * Creates a firmware object with the provided image set as default.
2723
 *
2724
 * Returns: a #FuFirmware
2725
 *
2726
 * Since: 1.3.1
2727
 **/
2728
FuFirmware *
2729
fu_firmware_new_from_bytes(GBytes *fw)
2730
496
{
2731
496
  FuFirmware *self = fu_firmware_new();
2732
496
  fu_firmware_set_bytes(self, fw);
2733
496
  return self;
2734
496
}
2735
2736
/**
2737
 * fu_firmware_new_from_gtypes:
2738
 * @stream: a #GInputStream
2739
 * @offset: start offset, useful for ignoring a bootloader
2740
 * @flags: install flags, e.g. %FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM
2741
 * @error: (nullable): optional return location for an error
2742
 * @...: an array of #GTypes, ending with %G_TYPE_INVALID
2743
 *
2744
 * Tries to parse the firmware with each #GType in order.
2745
 *
2746
 * Returns: (transfer full) (nullable): a #FuFirmware, or %NULL
2747
 *
2748
 * Since: 1.5.6
2749
 **/
2750
FuFirmware *
2751
fu_firmware_new_from_gtypes(GInputStream *stream,
2752
          gsize offset,
2753
          FuFirmwareParseFlags flags,
2754
          GError **error,
2755
          ...)
2756
152k
{
2757
152k
  va_list args;
2758
152k
  g_autoptr(GArray) gtypes = g_array_new(FALSE, FALSE, sizeof(GType));
2759
152k
  g_autoptr(GError) error_all = NULL;
2760
2761
152k
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL);
2762
152k
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
2763
2764
  /* create array of GTypes */
2765
152k
  va_start(args, error);
2766
479k
  while (TRUE) {
2767
479k
    GType gtype = va_arg(args, GType);
2768
479k
    if (gtype == G_TYPE_INVALID)
2769
152k
      break;
2770
326k
    g_array_append_val(gtypes, gtype);
2771
326k
  }
2772
152k
  va_end(args);
2773
2774
  /* invalid */
2775
152k
  if (gtypes->len == 0) {
2776
0
    g_set_error_literal(error,
2777
0
            FWUPD_ERROR,
2778
0
            FWUPD_ERROR_NOTHING_TO_DO,
2779
0
            "no GTypes specified");
2780
0
    return NULL;
2781
0
  }
2782
2783
  /* try each GType in turn */
2784
379k
  for (guint i = 0; i < gtypes->len; i++) {
2785
293k
    GType gtype = g_array_index(gtypes, GType, i);
2786
293k
    g_autoptr(FuFirmware) firmware = g_object_new(gtype, NULL);
2787
293k
    g_autoptr(GError) error_local = NULL;
2788
293k
    if (!fu_firmware_parse_stream(firmware, stream, offset, flags, &error_local)) {
2789
226k
      g_debug("@0x%x %s", (guint)offset, error_local->message);
2790
226k
      if (error_all == NULL) {
2791
139k
        g_propagate_error(&error_all, g_steal_pointer(&error_local));
2792
139k
      } else {
2793
        /* nocheck:error */
2794
86.6k
        g_prefix_error(&error_all, "%s: ", error_local->message);
2795
86.6k
      }
2796
226k
      continue;
2797
226k
    }
2798
66.7k
    return g_steal_pointer(&firmware);
2799
293k
  }
2800
2801
  /* failed */
2802
86.0k
  g_propagate_error(error, g_steal_pointer(&error_all));
2803
86.0k
  return NULL;
2804
152k
}
2805
2806
/**
2807
 * fu_firmware_roundtrip_from_xml:
2808
 * @builder_xml: (not nullable): XML describing the firmware
2809
 * @checksum_expected: (nullable): Expected SHA1 hash of the built binary blob
2810
 * @flags: #FuFirmwareBuilderFlags, e.g. %FU_FIRMWARE_BUILDER_FLAG_NO_BINARY_COMPARE
2811
 * @error: (nullable): optional return location for an error
2812
 *
2813
 * Roundtrip a firmware from builder XML to binary and back to XML.
2814
 *
2815
 * Returns: %TRUE for success
2816
 *
2817
 * Since: 2.1.1
2818
 **/
2819
gboolean
2820
fu_firmware_roundtrip_from_xml(const gchar *builder_xml,
2821
             const gchar *checksum_expected,
2822
             FuFirmwareBuilderFlags flags,
2823
             GError **error)
2824
0
{
2825
0
  g_autofree gchar *csum1 = NULL;
2826
0
  g_autofree gchar *csum2 = NULL;
2827
0
  g_autofree gchar *xml_out = NULL;
2828
0
  g_autoptr(FuFirmware) firmware1 = NULL;
2829
0
  g_autoptr(FuFirmware) firmware2 = NULL;
2830
2831
0
  g_return_val_if_fail(builder_xml != NULL, FALSE);
2832
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
2833
2834
  /* build and write */
2835
0
  firmware1 = fu_firmware_new_from_xml(builder_xml, error);
2836
0
  if (firmware1 == NULL) {
2837
0
    g_prefix_error(error, "failed to build %s: ", builder_xml);
2838
0
    return FALSE;
2839
0
  }
2840
0
  csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, error);
2841
0
  if (csum1 == NULL)
2842
0
    return FALSE;
2843
0
  if (checksum_expected != NULL && g_strcmp0(csum1, checksum_expected) != 0) {
2844
0
    g_set_error(error,
2845
0
          FWUPD_ERROR,
2846
0
          FWUPD_ERROR_INVALID_DATA,
2847
0
          "got checksum %s and expected %s",
2848
0
          csum1,
2849
0
          checksum_expected);
2850
0
    return FALSE;
2851
0
  }
2852
2853
  /* ensure we can write and then parse what we just wrote */
2854
0
  if ((flags & FU_FIRMWARE_BUILDER_FLAG_NO_WRITE) == 0) {
2855
0
    g_autoptr(FuFirmware) firmware3 =
2856
0
        g_object_new(G_TYPE_FROM_INSTANCE(firmware1), NULL);
2857
0
    g_autoptr(GBytes) fw = NULL;
2858
2859
0
    fw = fu_firmware_write(firmware1, error);
2860
0
    if (fw == NULL) {
2861
0
      g_prefix_error(error, "failed to write %s: ", builder_xml);
2862
0
      return FALSE;
2863
0
    }
2864
0
    if (!fu_firmware_parse_bytes(firmware3,
2865
0
               fw,
2866
0
               0x0,
2867
0
               FU_FIRMWARE_PARSE_FLAG_NO_SEARCH |
2868
0
             FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM,
2869
0
               error)) {
2870
0
      g_prefix_error(error, "failed to parse %s: ", builder_xml);
2871
0
      return FALSE;
2872
0
    }
2873
0
    if ((flags & FU_FIRMWARE_BUILDER_FLAG_NO_BINARY_COMPARE) == 0) {
2874
0
      g_autoptr(GBytes) fw2 = NULL;
2875
0
      fw2 = fu_firmware_write(firmware3, error);
2876
0
      if (fw2 == NULL) {
2877
0
        g_prefix_error(error, "failed to write %s: ", builder_xml);
2878
0
        return FALSE;
2879
0
      }
2880
0
      if (!fu_bytes_compare(fw2, fw, error)) {
2881
0
        g_prefix_error(error, "failed to compare %s: ", builder_xml);
2882
0
        return FALSE;
2883
0
      }
2884
0
    }
2885
0
  }
2886
2887
  /* ensure we can round-trip */
2888
0
  xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, error);
2889
0
  if (xml_out == NULL) {
2890
0
    g_prefix_error(error, "cannot export %s: ", builder_xml);
2891
0
    return FALSE;
2892
0
  }
2893
0
  firmware2 = fu_firmware_new_from_xml(xml_out, error);
2894
0
  if (firmware2 == NULL)
2895
0
    return FALSE;
2896
0
  csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, error);
2897
0
  if (csum2 == NULL)
2898
0
    return FALSE;
2899
0
  if (checksum_expected != NULL && g_strcmp0(csum2, checksum_expected) != 0) {
2900
0
    g_set_error(error,
2901
0
          FWUPD_ERROR,
2902
0
          FWUPD_ERROR_INVALID_DATA,
2903
0
          "got round-trip checksum %s and expected %s",
2904
0
          csum2,
2905
0
          checksum_expected);
2906
0
    return FALSE;
2907
0
  }
2908
2909
  /* success */
2910
0
  return TRUE;
2911
0
}
2912
2913
/**
2914
 * fu_firmware_roundtrip_from_filename:
2915
 * @builder_fn: (not nullable): XML filename describing the firmware
2916
 * @checksum_expected: (nullable): Expected SHA1 hash of the built binary blob
2917
 * @flags: #FuFirmwareBuilderFlags, e.g. %FU_FIRMWARE_BUILDER_FLAG_NO_BINARY_COMPARE
2918
 * @error: (nullable): optional return location for an error
2919
 *
2920
 * Roundtrip a firmware from builder XML filename to binary and back to XML.
2921
 *
2922
 * Returns: %TRUE for success
2923
 *
2924
 * Since: 2.1.1
2925
 **/
2926
gboolean
2927
fu_firmware_roundtrip_from_filename(const gchar *builder_fn,
2928
            const gchar *checksum_expected,
2929
            FuFirmwareBuilderFlags flags,
2930
            GError **error)
2931
0
{
2932
0
  g_autofree gchar *xml = NULL;
2933
2934
0
  g_return_val_if_fail(builder_fn != NULL, FALSE);
2935
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
2936
2937
0
  if (!g_file_get_contents(builder_fn, &xml, NULL, error)) {
2938
0
    fwupd_error_convert(error);
2939
0
    return FALSE;
2940
0
  }
2941
0
  return fu_firmware_roundtrip_from_xml(xml, checksum_expected, flags, error);
2942
0
}
2943
2944
static gboolean
2945
fu_firmware_fuzzer_test_input(FuFuzzer *fuzzer, GBytes *blob, GError **error)
2946
63.4k
{
2947
63.4k
  FuFirmware *self = FU_FIRMWARE(fuzzer);
2948
63.4k
  g_autoptr(GBytes) fw = NULL;
2949
2950
63.4k
  if (!fu_firmware_parse_bytes(self,
2951
63.4k
             blob,
2952
63.4k
             0x0,
2953
63.4k
             FU_FIRMWARE_PARSE_FLAG_NO_SEARCH |
2954
63.4k
           FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID |
2955
63.4k
           FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM,
2956
63.4k
             error))
2957
47.7k
    return FALSE;
2958
15.6k
  fw = fu_firmware_write(self, error);
2959
15.6k
  if (fw == NULL)
2960
3.14k
    return FALSE;
2961
2962
  /* success */
2963
12.4k
  return TRUE;
2964
15.6k
}
2965
2966
static GBytes *
2967
fu_firmware_fuzzer_build_example(FuFuzzer *fuzzer, GBytes *blob, GError **error)
2968
0
{
2969
0
  FuFirmware *self = FU_FIRMWARE(fuzzer);
2970
0
  g_autoptr(XbBuilder) builder = xb_builder_new();
2971
0
  g_autoptr(XbBuilderSource) source = xb_builder_source_new();
2972
0
  g_autoptr(XbNode) n = NULL;
2973
0
  g_autoptr(XbSilo) silo = NULL;
2974
2975
  /* sanity check */
2976
0
  if (blob == NULL) {
2977
0
    g_set_error_literal(error,
2978
0
            FWUPD_ERROR,
2979
0
            FWUPD_ERROR_INVALID_DATA,
2980
0
            "builder XML required");
2981
0
    return NULL;
2982
0
  }
2983
2984
  /* parse XML */
2985
0
  if (!xb_builder_source_load_bytes(source, blob, XB_BUILDER_SOURCE_FLAG_NONE, error)) {
2986
0
    g_prefix_error_literal(error, "could not parse XML: ");
2987
0
    fwupd_error_convert(error);
2988
0
    return NULL;
2989
0
  }
2990
0
  xb_builder_import_source(builder, source);
2991
0
  silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error);
2992
0
  if (silo == NULL) {
2993
0
    fwupd_error_convert(error);
2994
0
    return NULL;
2995
0
  }
2996
0
  n = xb_silo_query_first(silo, "firmware", error);
2997
0
  if (n == NULL) {
2998
0
    fwupd_error_convert(error);
2999
0
    return NULL;
3000
0
  }
3001
0
  if (!fu_firmware_build(self, n, error))
3002
0
    return NULL;
3003
0
  return fu_firmware_write(self, error);
3004
0
}
3005
3006
static void
3007
fu_firmware_fuzzer_iface_init(FuFuzzerInterface *iface)
3008
42
{
3009
42
  iface->test_input = fu_firmware_fuzzer_test_input;
3010
42
  iface->build_example = fu_firmware_fuzzer_build_example;
3011
42
}