Coverage Report

Created: 2026-08-13 07:13

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