Coverage Report

Created: 2025-07-01 07:09

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