Coverage Report

Created: 2026-01-16 06:48

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