Coverage Report

Created: 2025-12-14 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-efi-load-option.c
Line
Count
Source
1
/*
2
 * Copyright 2023 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
1.14k
#define G_LOG_DOMAIN "FuEfiLoadOption"
8
9
#include "config.h"
10
11
#include "fu-byte-array.h"
12
#include "fu-common.h"
13
#include "fu-efi-device-path-list.h"
14
#include "fu-efi-load-option.h"
15
#include "fu-input-stream.h"
16
#include "fu-string.h"
17
18
struct _FuEfiLoadOption {
19
  FuFirmware parent_instance;
20
  guint32 attrs;
21
  FuEfiLoadOptionKind kind;
22
  GBytes *optional_data; /* only used when not a hive or path */
23
  GHashTable *metadata;  /* element-type: utf8:utf8 */
24
};
25
26
static void
27
fu_efi_load_option_codec_iface_init(FwupdCodecInterface *iface);
28
29
12.4k
G_DEFINE_TYPE_EXTENDED(FuEfiLoadOption,
30
12.4k
           fu_efi_load_option,
31
12.4k
           FU_TYPE_FIRMWARE,
32
12.4k
           0,
33
12.4k
           G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC, fu_efi_load_option_codec_iface_init))
34
12.4k
35
34.3k
#define FU_EFI_LOAD_OPTION_DESCRIPTION_SIZE_MAX 0x1000u /* bytes */
36
37
888
#define FU_EFI_LOAD_OPTION_HIVE_HEADER_VERSION_MIN 1
38
39
static void
40
fu_efi_load_option_set_optional_data(FuEfiLoadOption *self, GBytes *optional_data)
41
539
{
42
539
  g_return_if_fail(FU_IS_EFI_LOAD_OPTION(self));
43
539
  if (self->optional_data != NULL) {
44
0
    g_bytes_unref(self->optional_data);
45
0
    self->optional_data = NULL;
46
0
  }
47
539
  if (optional_data != NULL)
48
539
    self->optional_data = g_bytes_ref(optional_data);
49
539
}
50
51
/**
52
 * fu_efi_load_option_get_metadata:
53
 * @self: a #FuEfiLoadOption
54
 * @key: (not nullable): UTF-8 string
55
 * @error: (nullable): optional return location for an error
56
 *
57
 * Gets an optional attribute.
58
 *
59
 * Returns: UTF-8 string, or %NULL
60
 *
61
 * Since: 2.0.0
62
 **/
63
const gchar *
64
fu_efi_load_option_get_metadata(FuEfiLoadOption *self, const gchar *key, GError **error)
65
0
{
66
0
  const gchar *value;
67
68
0
  g_return_val_if_fail(FU_IS_EFI_LOAD_OPTION(self), NULL);
69
0
  g_return_val_if_fail(key != NULL, NULL);
70
71
0
  value = g_hash_table_lookup(self->metadata, key);
72
0
  if (value == NULL) {
73
0
    g_set_error(error,
74
0
          FWUPD_ERROR,
75
0
          FWUPD_ERROR_NOT_SUPPORTED,
76
0
          "no attribute value for %s",
77
0
          key);
78
0
    return NULL;
79
0
  }
80
81
  /* success */
82
0
  return value;
83
0
}
84
85
/**
86
 * fu_efi_load_option_get_kind:
87
 * @self: a #FuEfiLoadOption
88
 *
89
 * Gets the loadopt kind.
90
 *
91
 * Returns: a #FuEfiLoadOptionKind, e.g. %FU_EFI_LOAD_OPTION_KIND_HIVE
92
 *
93
 * Since: 2.0.6
94
 **/
95
FuEfiLoadOptionKind
96
fu_efi_load_option_get_kind(FuEfiLoadOption *self)
97
0
{
98
0
  g_return_val_if_fail(FU_IS_EFI_LOAD_OPTION(self), FU_EFI_LOAD_OPTION_KIND_UNKNOWN);
99
0
  return self->kind;
100
0
}
101
102
/**
103
 * fu_efi_load_option_set_kind:
104
 * @self: a #FuEfiLoadOption
105
 * @kind: a #FuEfiLoadOptionKind, e.g. %FU_EFI_LOAD_OPTION_KIND_HIVE
106
 *
107
 * Sets the loadopt kind.
108
 *
109
 * Since: 2.0.6
110
 **/
111
void
112
fu_efi_load_option_set_kind(FuEfiLoadOption *self, FuEfiLoadOptionKind kind)
113
0
{
114
0
  g_return_if_fail(FU_IS_EFI_LOAD_OPTION(self));
115
0
  g_return_if_fail(kind < FU_EFI_LOAD_OPTION_KIND_LAST);
116
0
  self->kind = kind;
117
0
}
118
119
/**
120
 * fu_efi_load_option_set_metadata:
121
 * @self: a #FuEfiLoadOption
122
 * @key: (not nullable): UTF-8 string
123
 * @value: (nullable): UTF-8 string, or %NULL
124
 *
125
 * Sets an optional attribute. If @value is %NULL then the key will be removed.
126
 *
127
 * NOTE: When the key is `Path`, any leading backslash will be stripped automatically and added
128
 * back as-required on export.
129
 *
130
 * Since: 2.0.0
131
 **/
132
void
133
fu_efi_load_option_set_metadata(FuEfiLoadOption *self, const gchar *key, const gchar *value)
134
4.64k
{
135
4.64k
  g_return_if_fail(FU_IS_EFI_LOAD_OPTION(self));
136
4.64k
  g_return_if_fail(key != NULL);
137
138
4.64k
  if (value == NULL) {
139
0
    g_hash_table_remove(self->metadata, key);
140
0
    return;
141
0
  }
142
  /* auto-set something sensible */
143
4.64k
  if (self->kind == FU_EFI_LOAD_OPTION_KIND_UNKNOWN &&
144
725
      g_strcmp0(key, FU_EFI_LOAD_OPTION_METADATA_PATH) == 0) {
145
122
    self->kind = FU_EFI_LOAD_OPTION_KIND_PATH;
146
4.52k
  } else {
147
4.52k
    self->kind = FU_EFI_LOAD_OPTION_KIND_HIVE;
148
4.52k
  }
149
4.64k
  if (g_strcmp0(key, FU_EFI_LOAD_OPTION_METADATA_PATH) == 0 && value != NULL &&
150
440
      g_str_has_prefix(value, "\\")) {
151
200
    value++;
152
200
  }
153
4.64k
  g_hash_table_insert(self->metadata, g_strdup(key), g_strdup(value));
154
4.64k
}
155
156
static gboolean
157
fu_efi_load_option_parse_optional_hive(FuEfiLoadOption *self,
158
               GInputStream *stream,
159
               gsize offset,
160
               GError **error)
161
1.37k
{
162
1.37k
  g_autoptr(FuStructShimHive) st = NULL;
163
1.37k
  guint8 items_count;
164
165
1.37k
  st = fu_struct_shim_hive_parse_stream(stream, offset, error);
166
1.37k
  if (st == NULL)
167
491
    return FALSE;
168
888
  if (fu_struct_shim_hive_get_header_version(st) <
169
888
      FU_EFI_LOAD_OPTION_HIVE_HEADER_VERSION_MIN) {
170
1
    g_set_error(error,
171
1
          FWUPD_ERROR,
172
1
          FWUPD_ERROR_NOT_SUPPORTED,
173
1
          "header version %u is not supported",
174
1
          fu_struct_shim_hive_get_header_version(st));
175
1
    return FALSE;
176
1
  }
177
887
  offset += fu_struct_shim_hive_get_items_offset(st);
178
179
  /* items */
180
887
  items_count = fu_struct_shim_hive_get_items_count(st);
181
5.46k
  for (guint i = 0; i < items_count; i++) {
182
5.09k
    guint8 keysz;
183
5.09k
    guint32 valuesz;
184
5.09k
    g_autofree gchar *key = NULL;
185
5.09k
    g_autofree gchar *value = NULL;
186
5.09k
    g_autoptr(FuStructShimHiveItem) st_item = NULL;
187
188
5.09k
    st_item = fu_struct_shim_hive_item_parse_stream(stream, offset, error);
189
5.09k
    if (st_item == NULL)
190
259
      return FALSE;
191
4.83k
    offset += st_item->buf->len;
192
193
    /* key */
194
4.83k
    keysz = fu_struct_shim_hive_item_get_key_length(st_item);
195
4.83k
    if (keysz == 0) {
196
3
      g_set_error_literal(error,
197
3
              FWUPD_ERROR,
198
3
              FWUPD_ERROR_NOT_SUPPORTED,
199
3
              "zero key size is not supported");
200
3
      return FALSE;
201
3
    }
202
4.83k
    key = fu_input_stream_read_string(stream, offset, keysz, error);
203
4.83k
    if (key == NULL)
204
130
      return FALSE;
205
4.70k
    offset += keysz;
206
207
    /* value */
208
4.70k
    valuesz = fu_struct_shim_hive_item_get_value_length(st_item);
209
4.70k
    if (valuesz > 0) {
210
1.98k
      value = fu_input_stream_read_string(stream, offset, valuesz, error);
211
1.98k
      if (value == NULL)
212
121
        return FALSE;
213
1.86k
      offset += valuesz;
214
1.86k
    }
215
4.58k
    fu_efi_load_option_set_metadata(self, key, value != NULL ? value : "");
216
4.58k
  }
217
218
  /* success */
219
374
  return TRUE;
220
887
}
221
222
static gboolean
223
fu_efi_load_option_parse_optional_path(FuEfiLoadOption *self, GBytes *opt_blob, GError **error)
224
604
{
225
604
  g_autofree gchar *optional_path = NULL;
226
227
  /* convert to UTF-8 */
228
604
  optional_path = fu_utf16_to_utf8_bytes(opt_blob, G_LITTLE_ENDIAN, error);
229
604
  if (optional_path == NULL)
230
210
    return FALSE;
231
232
  /* check is ASCII */
233
394
  if (optional_path[0] == '\0' || !g_str_is_ascii(optional_path)) {
234
329
    g_set_error(error,
235
329
          FWUPD_ERROR,
236
329
          FWUPD_ERROR_NOT_SUPPORTED,
237
329
          "not ASCII data: %s",
238
329
          optional_path);
239
329
    return FALSE;
240
329
  }
241
65
  fu_efi_load_option_set_metadata(self, FU_EFI_LOAD_OPTION_METADATA_PATH, optional_path);
242
243
  /* success */
244
65
  return TRUE;
245
394
}
246
247
static gboolean
248
fu_efi_load_option_parse_optional(FuEfiLoadOption *self,
249
          GInputStream *stream,
250
          gsize offset,
251
          GError **error)
252
1.37k
{
253
1.37k
  gsize streamsz = 0;
254
1.37k
  g_autoptr(GBytes) opt_blob = NULL;
255
1.37k
  g_autoptr(GError) error_hive = NULL;
256
1.37k
  g_autoptr(GError) error_path = NULL;
257
258
  /* try hive structure first */
259
1.37k
  if (!fu_efi_load_option_parse_optional_hive(self, stream, offset, &error_hive)) {
260
1.00k
    if (!g_error_matches(error_hive, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA)) {
261
401
      g_propagate_error(error, g_steal_pointer(&error_hive));
262
401
      return FALSE;
263
401
    }
264
604
    g_debug("not a shim hive, ignoring: %s", error_hive->message);
265
604
  } else {
266
374
    self->kind = FU_EFI_LOAD_OPTION_KIND_HIVE;
267
374
    return TRUE;
268
374
  }
269
270
  /* then UCS-2 path, and on ASCII failure just treat as a raw data blob */
271
604
  if (!fu_input_stream_size(stream, &streamsz, error))
272
0
    return FALSE;
273
604
  opt_blob = fu_input_stream_read_bytes(stream, offset, streamsz - offset, NULL, error);
274
604
  if (opt_blob == NULL)
275
0
    return FALSE;
276
604
  if (!fu_efi_load_option_parse_optional_path(self, opt_blob, &error_path)) {
277
539
    g_debug("not a path, saving as raw blob: %s", error_path->message);
278
539
    fu_efi_load_option_set_optional_data(self, opt_blob);
279
539
  } else {
280
65
    self->kind = FU_EFI_LOAD_OPTION_KIND_PATH;
281
65
    return TRUE;
282
65
  }
283
284
  /* success */
285
539
  self->kind = FU_EFI_LOAD_OPTION_KIND_DATA;
286
539
  return TRUE;
287
604
}
288
289
static gboolean
290
fu_efi_load_option_parse(FuFirmware *firmware,
291
       GInputStream *stream,
292
       FuFirmwareParseFlags flags,
293
       GError **error)
294
2.02k
{
295
2.02k
  FuEfiLoadOption *self = FU_EFI_LOAD_OPTION(firmware);
296
2.02k
  gsize offset = 0;
297
2.02k
  gsize streamsz = 0;
298
2.02k
  g_autofree gchar *id = NULL;
299
2.02k
  g_autoptr(FuEfiDevicePathList) device_path_list = fu_efi_device_path_list_new();
300
2.02k
  g_autoptr(GByteArray) buf_utf16 = g_byte_array_new();
301
2.02k
  g_autoptr(FuStructEfiLoadOption) st = NULL;
302
303
  /* parse header */
304
2.02k
  st = fu_struct_efi_load_option_parse_stream(stream, offset, error);
305
2.02k
  if (st == NULL)
306
8
    return FALSE;
307
2.01k
  self->attrs = fu_struct_efi_load_option_get_attrs(st);
308
2.01k
  offset += st->buf->len;
309
310
  /* parse UTF-16 description */
311
2.01k
  if (!fu_input_stream_size(stream, &streamsz, error))
312
0
    return FALSE;
313
34.5k
  for (; offset < streamsz; offset += 2) {
314
34.3k
    guint16 tmp = 0;
315
34.3k
    if (buf_utf16->len > FU_EFI_LOAD_OPTION_DESCRIPTION_SIZE_MAX) {
316
2
      g_set_error(error,
317
2
            FWUPD_ERROR,
318
2
            FWUPD_ERROR_INVALID_DATA,
319
2
            "description was too long, limit is 0x%x chars",
320
2
            FU_EFI_LOAD_OPTION_DESCRIPTION_SIZE_MAX / 2);
321
2
      return FALSE;
322
2
    }
323
34.3k
    if (!fu_input_stream_read_u16(stream, offset, &tmp, G_LITTLE_ENDIAN, error))
324
17
      return FALSE;
325
34.2k
    if (tmp == 0)
326
1.80k
      break;
327
32.4k
    fu_byte_array_append_uint16(buf_utf16, tmp, G_LITTLE_ENDIAN);
328
32.4k
  }
329
1.99k
  id = fu_utf16_to_utf8_byte_array(buf_utf16, G_LITTLE_ENDIAN, error);
330
1.99k
  if (id == NULL)
331
43
    return FALSE;
332
1.95k
  fu_firmware_set_id(firmware, id);
333
1.95k
  offset += 2;
334
335
  /* parse dp blob */
336
1.95k
  if (!fu_firmware_parse_stream(FU_FIRMWARE(device_path_list), stream, offset, flags, error))
337
392
    return FALSE;
338
1.56k
  if (!fu_firmware_add_image(firmware, FU_FIRMWARE(device_path_list), error))
339
0
    return FALSE;
340
1.56k
  offset += fu_struct_efi_load_option_get_dp_size(st);
341
342
  /* optional data */
343
1.56k
  if (offset < streamsz) {
344
1.37k
    if (!fu_efi_load_option_parse_optional(self, stream, offset, error))
345
401
      return FALSE;
346
1.37k
  }
347
348
  /* success */
349
1.16k
  return TRUE;
350
1.56k
}
351
352
static GByteArray *
353
fu_efi_load_option_write_hive(FuEfiLoadOption *self, GError **error)
354
374
{
355
374
  GHashTableIter iter;
356
374
  guint items_count = g_hash_table_size(self->metadata);
357
374
  const gchar *key;
358
374
  const gchar *value;
359
374
  g_autoptr(FuStructShimHive) st = fu_struct_shim_hive_new();
360
361
374
  fu_struct_shim_hive_set_items_count(st, items_count);
362
374
  fu_struct_shim_hive_set_items_offset(st, FU_STRUCT_SHIM_HIVE_SIZE);
363
374
  g_hash_table_iter_init(&iter, self->metadata);
364
1.40k
  while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value)) {
365
1.03k
    guint32 keysz = strlen(key);
366
1.03k
    g_autoptr(FuStructShimHiveItem) st_item = fu_struct_shim_hive_item_new();
367
1.03k
    g_autoptr(GString) value_safe = g_string_new(value);
368
369
    /* required prefix for a path */
370
1.03k
    if (g_strcmp0(key, FU_EFI_LOAD_OPTION_METADATA_PATH) == 0 && value_safe->len > 0 &&
371
40
        !g_str_has_prefix(value_safe->str, "\\")) {
372
39
      g_string_prepend(value_safe, "\\");
373
39
    }
374
1.03k
    fu_struct_shim_hive_item_set_key_length(st_item, keysz);
375
1.03k
    fu_struct_shim_hive_item_set_value_length(st_item, value_safe->len);
376
1.03k
    if (keysz > 0)
377
1.03k
      g_byte_array_append(st_item->buf, (const guint8 *)key, keysz);
378
1.03k
    if (value_safe->len > 0) {
379
673
      g_byte_array_append(st_item->buf,
380
673
              (const guint8 *)value_safe->str,
381
673
              value_safe->len);
382
673
    }
383
384
    /* add to hive */
385
1.03k
    fu_byte_array_append_array(st->buf, st_item->buf);
386
1.03k
  }
387
388
  /* this covers all items, and so has to be done last */
389
374
  fu_struct_shim_hive_set_crc32(
390
374
      st,
391
374
      fu_crc32(FU_CRC_KIND_B32_STANDARD, st->buf->data, st->buf->len));
392
393
  /* success */
394
374
  return g_steal_pointer(&st->buf);
395
374
}
396
397
static GByteArray *
398
fu_efi_load_option_write_path(FuEfiLoadOption *self, GError **error)
399
52
{
400
52
  g_autoptr(GByteArray) buf = NULL;
401
52
  const gchar *path = g_hash_table_lookup(self->metadata, FU_EFI_LOAD_OPTION_METADATA_PATH);
402
52
  g_autoptr(GString) str = g_string_new(path);
403
404
  /* is required if a path */
405
52
  if (!g_str_has_prefix(str->str, "\\"))
406
51
    g_string_prepend(str, "\\");
407
52
  buf = fu_utf8_to_utf16_byte_array(str->str,
408
52
            G_LITTLE_ENDIAN,
409
52
            FU_UTF_CONVERT_FLAG_APPEND_NUL,
410
52
            error);
411
52
  if (buf == NULL)
412
0
    return NULL;
413
52
  return g_steal_pointer(&buf);
414
52
}
415
416
static GByteArray *
417
fu_efi_load_option_write(FuFirmware *firmware, GError **error)
418
1.16k
{
419
1.16k
  FuEfiLoadOption *self = FU_EFI_LOAD_OPTION(firmware);
420
1.16k
  g_autoptr(GByteArray) buf_utf16 = NULL;
421
1.16k
  g_autoptr(FuStructEfiLoadOption) st = fu_struct_efi_load_option_new();
422
1.16k
  g_autoptr(GBytes) dpbuf = NULL;
423
424
  /* header */
425
1.16k
  fu_struct_efi_load_option_set_attrs(st, self->attrs);
426
427
  /* label */
428
1.16k
  if (fu_firmware_get_id(firmware) == NULL) {
429
0
    g_set_error_literal(error,
430
0
            FWUPD_ERROR,
431
0
            FWUPD_ERROR_INVALID_DATA,
432
0
            "firmware ID required");
433
0
    return NULL;
434
0
  }
435
1.16k
  buf_utf16 = fu_utf8_to_utf16_byte_array(fu_firmware_get_id(firmware),
436
1.16k
            G_LITTLE_ENDIAN,
437
1.16k
            FU_UTF_CONVERT_FLAG_APPEND_NUL,
438
1.16k
            error);
439
1.16k
  if (buf_utf16 == NULL)
440
0
    return NULL;
441
1.16k
  g_byte_array_append(st->buf, buf_utf16->data, buf_utf16->len);
442
443
  /* dpbuf */
444
1.16k
  dpbuf = fu_firmware_get_image_by_gtype_bytes(firmware, FU_TYPE_EFI_DEVICE_PATH_LIST, error);
445
1.16k
  if (dpbuf == NULL)
446
383
    return NULL;
447
778
  fu_struct_efi_load_option_set_dp_size(st, g_bytes_get_size(dpbuf));
448
778
  fu_byte_array_append_bytes(st->buf, dpbuf);
449
450
  /* hive, path or data */
451
778
  if (self->kind == FU_EFI_LOAD_OPTION_KIND_HIVE) {
452
374
    g_autoptr(GByteArray) buf_hive = NULL;
453
374
    buf_hive = fu_efi_load_option_write_hive(self, error);
454
374
    if (buf_hive == NULL)
455
0
      return NULL;
456
374
    g_byte_array_append(st->buf, buf_hive->data, buf_hive->len);
457
374
    fu_byte_array_align_up(st->buf, FU_FIRMWARE_ALIGNMENT_512, 0x0); /* make atomic */
458
404
  } else if (self->kind == FU_EFI_LOAD_OPTION_KIND_PATH) {
459
52
    g_autoptr(GByteArray) buf_path = NULL;
460
52
    buf_path = fu_efi_load_option_write_path(self, error);
461
52
    if (buf_path == NULL)
462
0
      return NULL;
463
52
    g_byte_array_append(st->buf, buf_path->data, buf_path->len);
464
352
  } else if (self->kind == FU_EFI_LOAD_OPTION_KIND_DATA && self->optional_data != NULL) {
465
285
    fu_byte_array_append_bytes(st->buf, self->optional_data);
466
285
  }
467
468
  /* success */
469
778
  return g_steal_pointer(&st->buf);
470
778
}
471
472
static gboolean
473
fu_efi_load_option_build(FuFirmware *firmware, XbNode *n, GError **error)
474
0
{
475
0
  FuEfiLoadOption *self = FU_EFI_LOAD_OPTION(firmware);
476
0
  const gchar *str;
477
0
  guint64 tmp;
478
0
  g_autoptr(GPtrArray) metadata = NULL;
479
0
  g_autoptr(XbNode) optional_data = NULL;
480
481
  /* simple properties */
482
0
  tmp = xb_node_query_text_as_uint(n, "attrs", NULL);
483
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32)
484
0
    self->attrs = tmp;
485
486
  /* simple properties */
487
0
  str = xb_node_query_text(n, "kind", NULL);
488
0
  if (str != NULL) {
489
0
    self->kind = fu_efi_load_option_kind_from_string(str);
490
0
    if (self->kind == FU_EFI_LOAD_OPTION_KIND_UNKNOWN) {
491
0
      g_set_error(error,
492
0
            FWUPD_ERROR,
493
0
            FWUPD_ERROR_INVALID_DATA,
494
0
            "invalid option kind type %s",
495
0
            str);
496
0
      return FALSE;
497
0
    }
498
0
  }
499
500
  /* optional data */
501
0
  optional_data = xb_node_query_first(n, "optional_data", NULL);
502
0
  if (optional_data != NULL) {
503
0
    g_autoptr(GBytes) blob = NULL;
504
0
    if (xb_node_get_text(optional_data) != NULL) {
505
0
      gsize bufsz = 0;
506
0
      g_autofree guchar *buf = NULL;
507
0
      buf = g_base64_decode(xb_node_get_text(optional_data), &bufsz);
508
0
      blob = g_bytes_new(buf, bufsz);
509
0
    } else {
510
0
      blob = g_bytes_new(NULL, 0);
511
0
    }
512
0
    fu_efi_load_option_set_optional_data(self, blob);
513
0
    self->kind = FU_EFI_LOAD_OPTION_KIND_DATA;
514
0
  }
515
0
  metadata = xb_node_query(n, "metadata/*", 0, NULL);
516
0
  if (metadata != NULL) {
517
0
    for (guint i = 0; i < metadata->len; i++) {
518
0
      XbNode *c = g_ptr_array_index(metadata, i);
519
0
      const gchar *value = xb_node_get_text(c);
520
0
      if (xb_node_get_element(c) == NULL)
521
0
        continue;
522
0
      fu_efi_load_option_set_metadata(self,
523
0
              xb_node_get_element(c),
524
0
              value != NULL ? value : "");
525
0
    }
526
0
  }
527
528
  /* success */
529
0
  return TRUE;
530
0
}
531
532
static void
533
fu_efi_load_option_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
534
0
{
535
0
  FuEfiLoadOption *self = FU_EFI_LOAD_OPTION(firmware);
536
537
0
  fu_xmlb_builder_insert_kx(bn, "attrs", self->attrs);
538
0
  if (self->kind != FU_EFI_LOAD_OPTION_KIND_UNKNOWN) {
539
0
    fu_xmlb_builder_insert_kv(bn,
540
0
            "kind",
541
0
            fu_efi_load_option_kind_to_string(self->kind));
542
0
  }
543
0
  if (g_hash_table_size(self->metadata) > 0) {
544
0
    GHashTableIter iter;
545
0
    const gchar *key;
546
0
    const gchar *value;
547
0
    g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "metadata", NULL);
548
0
    g_hash_table_iter_init(&iter, self->metadata);
549
0
    while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value))
550
0
      xb_builder_node_insert_text(bc, key, value, NULL);
551
0
  }
552
0
  if (self->optional_data != NULL) {
553
0
    gsize bufsz = 0;
554
0
    const guint8 *buf = g_bytes_get_data(self->optional_data, &bufsz);
555
0
    g_autofree gchar *datastr = g_base64_encode(buf, bufsz);
556
0
    xb_builder_node_insert_text(bn, "optional_data", datastr, NULL);
557
0
  }
558
0
}
559
560
static void
561
fu_efi_load_option_add_json(FwupdCodec *codec, JsonBuilder *builder, FwupdCodecFlags flags)
562
0
{
563
0
  FuEfiLoadOption *self = FU_EFI_LOAD_OPTION(codec);
564
0
  g_autoptr(FuFirmware) dp_list = NULL;
565
566
0
  fwupd_codec_json_append(builder, "Name", fu_firmware_get_id(FU_FIRMWARE(self)));
567
0
  if (self->kind != FU_EFI_LOAD_OPTION_KIND_UNKNOWN) {
568
0
    fwupd_codec_json_append(builder,
569
0
          "Kind",
570
0
          fu_efi_load_option_kind_to_string(self->kind));
571
0
  }
572
0
  if (g_hash_table_size(self->metadata) > 0) {
573
0
    GHashTableIter iter;
574
0
    const gchar *key;
575
0
    const gchar *value;
576
0
    json_builder_set_member_name(builder, "Metadata");
577
0
    json_builder_begin_object(builder);
578
0
    g_hash_table_iter_init(&iter, self->metadata);
579
0
    while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value))
580
0
      fwupd_codec_json_append(builder, key, value);
581
0
    json_builder_end_object(builder);
582
0
  }
583
0
  dp_list =
584
0
      fu_firmware_get_image_by_gtype(FU_FIRMWARE(self), FU_TYPE_EFI_DEVICE_PATH_LIST, NULL);
585
0
  if (dp_list != NULL)
586
0
    fwupd_codec_to_json(FWUPD_CODEC(dp_list), builder, flags);
587
0
}
588
589
static void
590
fu_efi_load_option_codec_iface_init(FwupdCodecInterface *iface)
591
1
{
592
1
  iface->add_json = fu_efi_load_option_add_json;
593
1
}
594
595
static void
596
fu_efi_load_option_finalize(GObject *obj)
597
2.02k
{
598
2.02k
  FuEfiLoadOption *self = FU_EFI_LOAD_OPTION(obj);
599
2.02k
  if (self->optional_data != NULL)
600
539
    g_bytes_unref(self->optional_data);
601
2.02k
  g_hash_table_unref(self->metadata);
602
2.02k
  G_OBJECT_CLASS(fu_efi_load_option_parent_class)->finalize(obj);
603
2.02k
}
604
605
static void
606
fu_efi_load_option_class_init(FuEfiLoadOptionClass *klass)
607
1
{
608
1
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
609
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
610
1
  object_class->finalize = fu_efi_load_option_finalize;
611
1
  firmware_class->parse = fu_efi_load_option_parse;
612
1
  firmware_class->write = fu_efi_load_option_write;
613
1
  firmware_class->build = fu_efi_load_option_build;
614
1
  firmware_class->export = fu_efi_load_option_export;
615
1
}
616
617
static void
618
fu_efi_load_option_init(FuEfiLoadOption *self)
619
2.02k
{
620
2.02k
  self->attrs = FU_EFI_LOAD_OPTION_ATTR_ACTIVE;
621
2.02k
  self->metadata = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
622
2.02k
  g_type_ensure(FU_TYPE_EFI_DEVICE_PATH_LIST);
623
2.02k
}
624
625
/**
626
 * fu_efi_load_option_new:
627
 *
628
 * Returns: (transfer full): a #FuEfiLoadOption
629
 *
630
 * Since: 1.9.3
631
 **/
632
FuEfiLoadOption *
633
fu_efi_load_option_new(void)
634
0
{
635
0
  return g_object_new(FU_TYPE_EFI_LOAD_OPTION, NULL);
636
0
}