Coverage Report

Created: 2025-08-03 06:57

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