Coverage Report

Created: 2025-07-18 06:26

/src/fwupd/libfwupdplugin/fu-efi-load-option.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2023 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
1.30k
#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
G_DEFINE_TYPE_EXTENDED(FuEfiLoadOption,
30
           fu_efi_load_option,
31
           FU_TYPE_FIRMWARE,
32
           0,
33
           G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC, fu_efi_load_option_codec_iface_init))
34
35
55.7k
#define FU_EFI_LOAD_OPTION_DESCRIPTION_SIZE_MAX 0x1000u /* bytes */
36
37
935
#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
626
{
42
626
  g_return_if_fail(FU_IS_EFI_LOAD_OPTION(self));
43
626
  if (self->optional_data != NULL) {
44
0
    g_bytes_unref(self->optional_data);
45
0
    self->optional_data = NULL;
46
0
  }
47
626
  if (optional_data != NULL)
48
626
    self->optional_data = g_bytes_ref(optional_data);
49
626
}
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.26k
{
135
4.26k
  g_return_if_fail(FU_IS_EFI_LOAD_OPTION(self));
136
4.26k
  g_return_if_fail(key != NULL);
137
138
4.26k
  if (value == NULL) {
139
0
    g_hash_table_remove(self->metadata, key);
140
0
    return;
141
0
  }
142
  /* auto-set something sensible */
143
4.26k
  if (self->kind == FU_EFI_LOAD_OPTION_KIND_UNKNOWN &&
144
4.26k
      g_strcmp0(key, FU_EFI_LOAD_OPTION_METADATA_PATH) == 0) {
145
133
    self->kind = FU_EFI_LOAD_OPTION_KIND_PATH;
146
4.13k
  } else {
147
4.13k
    self->kind = FU_EFI_LOAD_OPTION_KIND_HIVE;
148
4.13k
  }
149
4.26k
  if (g_strcmp0(key, FU_EFI_LOAD_OPTION_METADATA_PATH) == 0 && value != NULL &&
150
4.26k
      g_str_has_prefix(value, "\\")) {
151
69
    value++;
152
69
  }
153
4.26k
  g_hash_table_insert(self->metadata, g_strdup(key), g_strdup(value));
154
4.26k
}
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.42k
{
162
1.42k
  g_autoptr(FuStructShimHive) st = NULL;
163
1.42k
  guint8 items_count;
164
165
1.42k
  st = fu_struct_shim_hive_parse_stream(stream, offset, error);
166
1.42k
  if (st == NULL)
167
490
    return FALSE;
168
935
  if (fu_struct_shim_hive_get_header_version(st) <
169
935
      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
934
  offset += fu_struct_shim_hive_get_items_offset(st);
178
179
  /* items */
180
934
  items_count = fu_struct_shim_hive_get_items_count(st);
181
5.14k
  for (guint i = 0; i < items_count; i++) {
182
4.79k
    guint8 keysz;
183
4.79k
    guint32 valuesz;
184
4.79k
    g_autofree gchar *key = NULL;
185
4.79k
    g_autofree gchar *value = NULL;
186
4.79k
    g_autoptr(FuStructShimHiveItem) st_item = NULL;
187
188
4.79k
    st_item = fu_struct_shim_hive_item_parse_stream(stream, offset, error);
189
4.79k
    if (st_item == NULL)
190
351
      return FALSE;
191
4.44k
    offset += st_item->len;
192
193
    /* key */
194
4.44k
    keysz = fu_struct_shim_hive_item_get_key_length(st_item);
195
4.44k
    if (keysz == 0) {
196
5
      g_set_error_literal(error,
197
5
              FWUPD_ERROR,
198
5
              FWUPD_ERROR_NOT_SUPPORTED,
199
5
              "zero key size is not supported");
200
5
      return FALSE;
201
5
    }
202
4.43k
    key = fu_input_stream_read_string(stream, offset, keysz, error);
203
4.43k
    if (key == NULL)
204
108
      return FALSE;
205
4.32k
    offset += keysz;
206
207
    /* value */
208
4.32k
    valuesz = fu_struct_shim_hive_item_get_value_length(st_item);
209
4.32k
    if (valuesz > 0) {
210
1.78k
      value = fu_input_stream_read_string(stream, offset, valuesz, error);
211
1.78k
      if (value == NULL)
212
117
        return FALSE;
213
1.66k
      offset += valuesz;
214
1.66k
    }
215
4.21k
    fu_efi_load_option_set_metadata(self, key, value != NULL ? value : "");
216
4.21k
  }
217
218
  /* success */
219
353
  return TRUE;
220
934
}
221
222
static gboolean
223
fu_efi_load_option_parse_optional_path(FuEfiLoadOption *self, GBytes *opt_blob, GError **error)
224
681
{
225
681
  g_autofree gchar *optional_path = NULL;
226
227
  /* convert to UTF-8 */
228
681
  optional_path = fu_utf16_to_utf8_bytes(opt_blob, G_LITTLE_ENDIAN, error);
229
681
  if (optional_path == NULL)
230
273
    return FALSE;
231
232
  /* check is ASCII */
233
408
  if (optional_path[0] == '\0' || !g_str_is_ascii(optional_path)) {
234
353
    g_set_error(error,
235
353
          FWUPD_ERROR,
236
353
          FWUPD_ERROR_NOT_SUPPORTED,
237
353
          "not ASCII data: %s",
238
353
          optional_path);
239
353
    return FALSE;
240
353
  }
241
55
  fu_efi_load_option_set_metadata(self, FU_EFI_LOAD_OPTION_METADATA_PATH, optional_path);
242
243
  /* success */
244
55
  return TRUE;
245
408
}
246
247
static gboolean
248
fu_efi_load_option_parse_optional(FuEfiLoadOption *self,
249
          GInputStream *stream,
250
          gsize offset,
251
          GError **error)
252
1.42k
{
253
1.42k
  gsize streamsz = 0;
254
1.42k
  g_autoptr(GBytes) opt_blob = NULL;
255
1.42k
  g_autoptr(GError) error_hive = NULL;
256
1.42k
  g_autoptr(GError) error_path = NULL;
257
258
  /* try hive structure first */
259
1.42k
  if (!fu_efi_load_option_parse_optional_hive(self, stream, offset, &error_hive)) {
260
1.07k
    if (!g_error_matches(error_hive, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA)) {
261
391
      g_propagate_error(error, g_steal_pointer(&error_hive));
262
391
      return FALSE;
263
391
    }
264
681
    g_debug("not a shim hive, ignoring: %s", error_hive->message);
265
681
  } else {
266
353
    self->kind = FU_EFI_LOAD_OPTION_KIND_HIVE;
267
353
    return TRUE;
268
353
  }
269
270
  /* then UCS-2 path, and on ASCII failure just treat as a raw data blob */
271
681
  if (!fu_input_stream_size(stream, &streamsz, error))
272
0
    return FALSE;
273
681
  opt_blob = fu_input_stream_read_bytes(stream, offset, streamsz - offset, NULL, error);
274
681
  if (opt_blob == NULL)
275
0
    return FALSE;
276
681
  if (!fu_efi_load_option_parse_optional_path(self, opt_blob, &error_path)) {
277
626
    g_debug("not a path, saving as raw blob: %s", error_path->message);
278
626
    fu_efi_load_option_set_optional_data(self, opt_blob);
279
626
  } else {
280
55
    self->kind = FU_EFI_LOAD_OPTION_KIND_PATH;
281
55
    return TRUE;
282
55
  }
283
284
  /* success */
285
626
  self->kind = FU_EFI_LOAD_OPTION_KIND_DATA;
286
626
  return TRUE;
287
681
}
288
289
static gboolean
290
fu_efi_load_option_parse(FuFirmware *firmware,
291
       GInputStream *stream,
292
       FuFirmwareParseFlags flags,
293
       GError **error)
294
2.06k
{
295
2.06k
  FuEfiLoadOption *self = FU_EFI_LOAD_OPTION(firmware);
296
2.06k
  gsize offset = 0;
297
2.06k
  gsize streamsz = 0;
298
2.06k
  g_autofree gchar *id = NULL;
299
2.06k
  g_autoptr(FuEfiDevicePathList) device_path_list = fu_efi_device_path_list_new();
300
2.06k
  g_autoptr(GByteArray) buf_utf16 = g_byte_array_new();
301
2.06k
  g_autoptr(GByteArray) st = NULL;
302
303
  /* parse header */
304
2.06k
  st = fu_struct_efi_load_option_parse_stream(stream, offset, error);
305
2.06k
  if (st == NULL)
306
9
    return FALSE;
307
2.05k
  self->attrs = fu_struct_efi_load_option_get_attrs(st);
308
2.05k
  offset += st->len;
309
310
  /* parse UTF-16 description */
311
2.05k
  if (!fu_input_stream_size(stream, &streamsz, error))
312
0
    return FALSE;
313
55.9k
  for (; offset < streamsz; offset += 2) {
314
55.7k
    guint16 tmp = 0;
315
55.7k
    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
55.7k
    if (!fu_input_stream_read_u16(stream, offset, &tmp, G_LITTLE_ENDIAN, error))
324
30
      return FALSE;
325
55.7k
    if (tmp == 0)
326
1.83k
      break;
327
53.9k
    fu_byte_array_append_uint16(buf_utf16, tmp, G_LITTLE_ENDIAN);
328
53.9k
  }
329
2.02k
  id = fu_utf16_to_utf8_byte_array(buf_utf16, G_LITTLE_ENDIAN, error);
330
2.02k
  if (id == NULL)
331
36
    return FALSE;
332
1.98k
  fu_firmware_set_id(firmware, id);
333
1.98k
  offset += 2;
334
335
  /* parse dp blob */
336
1.98k
  if (!fu_firmware_parse_stream(FU_FIRMWARE(device_path_list), stream, offset, flags, error))
337
382
    return FALSE;
338
1.60k
  if (!fu_firmware_add_image_full(firmware, FU_FIRMWARE(device_path_list), error))
339
0
    return FALSE;
340
1.60k
  offset += fu_struct_efi_load_option_get_dp_size(st);
341
342
  /* optional data */
343
1.60k
  if (offset < streamsz) {
344
1.42k
    if (!fu_efi_load_option_parse_optional(self, stream, offset, error))
345
391
      return FALSE;
346
1.42k
  }
347
348
  /* success */
349
1.21k
  return TRUE;
350
1.60k
}
351
352
static GByteArray *
353
fu_efi_load_option_write_hive(FuEfiLoadOption *self, GError **error)
354
352
{
355
352
  GHashTableIter iter;
356
352
  guint items_count = g_hash_table_size(self->metadata);
357
352
  const gchar *key;
358
352
  const gchar *value;
359
352
  g_autoptr(FuStructShimHive) st = fu_struct_shim_hive_new();
360
361
352
  fu_struct_shim_hive_set_items_count(st, items_count);
362
352
  fu_struct_shim_hive_set_items_offset(st, FU_STRUCT_SHIM_HIVE_SIZE);
363
352
  g_hash_table_iter_init(&iter, self->metadata);
364
1.19k
  while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value)) {
365
844
    guint32 keysz = strlen(key);
366
844
    g_autoptr(FuStructShimHiveItem) st_item = fu_struct_shim_hive_item_new();
367
844
    g_autoptr(GString) value_safe = g_string_new(value);
368
369
    /* required prefix for a path */
370
844
    if (g_strcmp0(key, FU_EFI_LOAD_OPTION_METADATA_PATH) == 0 && value_safe->len > 0 &&
371
844
        !g_str_has_prefix(value_safe->str, "\\")) {
372
66
      g_string_prepend(value_safe, "\\");
373
66
    }
374
844
    fu_struct_shim_hive_item_set_key_length(st_item, keysz);
375
844
    fu_struct_shim_hive_item_set_value_length(st_item, value_safe->len);
376
844
    if (keysz > 0)
377
844
      g_byte_array_append(st_item, (const guint8 *)key, keysz);
378
844
    if (value_safe->len > 0) {
379
576
      g_byte_array_append(st_item,
380
576
              (const guint8 *)value_safe->str,
381
576
              value_safe->len);
382
576
    }
383
384
    /* add to hive */
385
844
    g_byte_array_append(st, st_item->data, st_item->len);
386
844
  }
387
388
  /* this covers all items, and so has to be done last */
389
352
  fu_struct_shim_hive_set_crc32(st, fu_crc32(FU_CRC_KIND_B32_STANDARD, st->data, st->len));
390
391
  /* success */
392
352
  return g_steal_pointer(&st);
393
352
}
394
395
static GByteArray *
396
fu_efi_load_option_write_path(FuEfiLoadOption *self, GError **error)
397
46
{
398
46
  g_autoptr(GByteArray) buf = NULL;
399
46
  const gchar *path = g_hash_table_lookup(self->metadata, FU_EFI_LOAD_OPTION_METADATA_PATH);
400
46
  g_autoptr(GString) str = g_string_new(path);
401
402
  /* is required if a path */
403
46
  if (!g_str_has_prefix(str->str, "\\"))
404
45
    g_string_prepend(str, "\\");
405
46
  buf = fu_utf8_to_utf16_byte_array(str->str,
406
46
            G_LITTLE_ENDIAN,
407
46
            FU_UTF_CONVERT_FLAG_APPEND_NUL,
408
46
            error);
409
46
  if (buf == NULL)
410
0
    return NULL;
411
46
  return g_steal_pointer(&buf);
412
46
}
413
414
static GByteArray *
415
fu_efi_load_option_write(FuFirmware *firmware, GError **error)
416
1.21k
{
417
1.21k
  FuEfiLoadOption *self = FU_EFI_LOAD_OPTION(firmware);
418
1.21k
  g_autoptr(GByteArray) buf_utf16 = NULL;
419
1.21k
  g_autoptr(GByteArray) st = fu_struct_efi_load_option_new();
420
1.21k
  g_autoptr(GBytes) dpbuf = NULL;
421
422
  /* header */
423
1.21k
  fu_struct_efi_load_option_set_attrs(st, self->attrs);
424
425
  /* label */
426
1.21k
  if (fu_firmware_get_id(firmware) == NULL) {
427
0
    g_set_error_literal(error,
428
0
            FWUPD_ERROR,
429
0
            FWUPD_ERROR_INVALID_DATA,
430
0
            "firmware ID required");
431
0
    return NULL;
432
0
  }
433
1.21k
  buf_utf16 = fu_utf8_to_utf16_byte_array(fu_firmware_get_id(firmware),
434
1.21k
            G_LITTLE_ENDIAN,
435
1.21k
            FU_UTF_CONVERT_FLAG_APPEND_NUL,
436
1.21k
            error);
437
1.21k
  if (buf_utf16 == NULL)
438
0
    return NULL;
439
1.21k
  g_byte_array_append(st, buf_utf16->data, buf_utf16->len);
440
441
  /* dpbuf */
442
1.21k
  dpbuf = fu_firmware_get_image_by_gtype_bytes(firmware, FU_TYPE_EFI_DEVICE_PATH_LIST, error);
443
1.21k
  if (dpbuf == NULL)
444
444
    return NULL;
445
770
  fu_struct_efi_load_option_set_dp_size(st, g_bytes_get_size(dpbuf));
446
770
  fu_byte_array_append_bytes(st, dpbuf);
447
448
  /* hive, path or data */
449
770
  if (self->kind == FU_EFI_LOAD_OPTION_KIND_HIVE) {
450
352
    g_autoptr(GByteArray) buf_hive = NULL;
451
352
    buf_hive = fu_efi_load_option_write_hive(self, error);
452
352
    if (buf_hive == NULL)
453
0
      return NULL;
454
352
    g_byte_array_append(st, buf_hive->data, buf_hive->len);
455
352
    fu_byte_array_align_up(st, FU_FIRMWARE_ALIGNMENT_512, 0x0); /* make atomic */
456
418
  } else if (self->kind == FU_EFI_LOAD_OPTION_KIND_PATH) {
457
46
    g_autoptr(GByteArray) buf_path = NULL;
458
46
    buf_path = fu_efi_load_option_write_path(self, error);
459
46
    if (buf_path == NULL)
460
0
      return NULL;
461
46
    g_byte_array_append(st, buf_path->data, buf_path->len);
462
372
  } else if (self->kind == FU_EFI_LOAD_OPTION_KIND_DATA && self->optional_data != NULL) {
463
291
    fu_byte_array_append_bytes(st, self->optional_data);
464
291
  }
465
466
  /* success */
467
770
  return g_steal_pointer(&st);
468
770
}
469
470
static gboolean
471
fu_efi_load_option_build(FuFirmware *firmware, XbNode *n, GError **error)
472
0
{
473
0
  FuEfiLoadOption *self = FU_EFI_LOAD_OPTION(firmware);
474
0
  const gchar *str;
475
0
  guint64 tmp;
476
0
  g_autoptr(GPtrArray) metadata = NULL;
477
0
  g_autoptr(XbNode) optional_data = NULL;
478
479
  /* simple properties */
480
0
  tmp = xb_node_query_text_as_uint(n, "attrs", NULL);
481
0
  if (tmp != G_MAXUINT64 && tmp <= G_MAXUINT32)
482
0
    self->attrs = tmp;
483
484
  /* simple properties */
485
0
  str = xb_node_query_text(n, "kind", NULL);
486
0
  if (str != NULL) {
487
0
    self->kind = fu_efi_load_option_kind_from_string(str);
488
0
    if (self->kind == FU_EFI_LOAD_OPTION_KIND_UNKNOWN) {
489
0
      g_set_error(error,
490
0
            FWUPD_ERROR,
491
0
            FWUPD_ERROR_INVALID_DATA,
492
0
            "invalid option kind type %s",
493
0
            str);
494
0
      return FALSE;
495
0
    }
496
0
  }
497
498
  /* optional data */
499
0
  optional_data = xb_node_query_first(n, "optional_data", NULL);
500
0
  if (optional_data != NULL) {
501
0
    g_autoptr(GBytes) blob = NULL;
502
0
    if (xb_node_get_text(optional_data) != NULL) {
503
0
      gsize bufsz = 0;
504
0
      g_autofree guchar *buf = NULL;
505
0
      buf = g_base64_decode(xb_node_get_text(optional_data), &bufsz);
506
0
      blob = g_bytes_new(buf, bufsz);
507
0
    } else {
508
0
      blob = g_bytes_new(NULL, 0);
509
0
    }
510
0
    fu_efi_load_option_set_optional_data(self, blob);
511
0
    self->kind = FU_EFI_LOAD_OPTION_KIND_DATA;
512
0
  }
513
0
  metadata = xb_node_query(n, "metadata/*", 0, NULL);
514
0
  if (metadata != NULL) {
515
0
    for (guint i = 0; i < metadata->len; i++) {
516
0
      XbNode *c = g_ptr_array_index(metadata, i);
517
0
      const gchar *value = xb_node_get_text(c);
518
0
      if (xb_node_get_element(c) == NULL)
519
0
        continue;
520
0
      fu_efi_load_option_set_metadata(self,
521
0
              xb_node_get_element(c),
522
0
              value != NULL ? value : "");
523
0
    }
524
0
  }
525
526
  /* success */
527
0
  return TRUE;
528
0
}
529
530
static void
531
fu_efi_load_option_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
532
0
{
533
0
  FuEfiLoadOption *self = FU_EFI_LOAD_OPTION(firmware);
534
535
0
  fu_xmlb_builder_insert_kx(bn, "attrs", self->attrs);
536
0
  if (self->kind != FU_EFI_LOAD_OPTION_KIND_UNKNOWN) {
537
0
    fu_xmlb_builder_insert_kv(bn,
538
0
            "kind",
539
0
            fu_efi_load_option_kind_to_string(self->kind));
540
0
  }
541
0
  if (g_hash_table_size(self->metadata) > 0) {
542
0
    GHashTableIter iter;
543
0
    const gchar *key;
544
0
    const gchar *value;
545
0
    g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "metadata", NULL);
546
0
    g_hash_table_iter_init(&iter, self->metadata);
547
0
    while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value))
548
0
      xb_builder_node_insert_text(bc, key, value, NULL);
549
0
  }
550
0
  if (self->optional_data != NULL) {
551
0
    gsize bufsz = 0;
552
0
    const guint8 *buf = g_bytes_get_data(self->optional_data, &bufsz);
553
0
    g_autofree gchar *datastr = g_base64_encode(buf, bufsz);
554
0
    xb_builder_node_insert_text(bn, "optional_data", datastr, NULL);
555
0
  }
556
0
}
557
558
static void
559
fu_efi_load_option_add_json(FwupdCodec *codec, JsonBuilder *builder, FwupdCodecFlags flags)
560
0
{
561
0
  FuEfiLoadOption *self = FU_EFI_LOAD_OPTION(codec);
562
0
  g_autoptr(FuFirmware) dp_list = NULL;
563
564
0
  fwupd_codec_json_append(builder, "Name", fu_firmware_get_id(FU_FIRMWARE(self)));
565
0
  if (self->kind != FU_EFI_LOAD_OPTION_KIND_UNKNOWN) {
566
0
    fwupd_codec_json_append(builder,
567
0
          "Kind",
568
0
          fu_efi_load_option_kind_to_string(self->kind));
569
0
  }
570
0
  if (g_hash_table_size(self->metadata) > 0) {
571
0
    GHashTableIter iter;
572
0
    const gchar *key;
573
0
    const gchar *value;
574
0
    json_builder_set_member_name(builder, "Metadata");
575
0
    json_builder_begin_object(builder);
576
0
    g_hash_table_iter_init(&iter, self->metadata);
577
0
    while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value))
578
0
      fwupd_codec_json_append(builder, key, value);
579
0
    json_builder_end_object(builder);
580
0
  }
581
0
  dp_list =
582
0
      fu_firmware_get_image_by_gtype(FU_FIRMWARE(self), FU_TYPE_EFI_DEVICE_PATH_LIST, NULL);
583
0
  if (dp_list != NULL)
584
0
    fwupd_codec_to_json(FWUPD_CODEC(dp_list), builder, flags);
585
0
}
586
587
static void
588
fu_efi_load_option_codec_iface_init(FwupdCodecInterface *iface)
589
1
{
590
1
  iface->add_json = fu_efi_load_option_add_json;
591
1
}
592
593
static void
594
fu_efi_load_option_finalize(GObject *obj)
595
2.06k
{
596
2.06k
  FuEfiLoadOption *self = FU_EFI_LOAD_OPTION(obj);
597
2.06k
  if (self->optional_data != NULL)
598
626
    g_bytes_unref(self->optional_data);
599
2.06k
  g_hash_table_unref(self->metadata);
600
2.06k
  G_OBJECT_CLASS(fu_efi_load_option_parent_class)->finalize(obj);
601
2.06k
}
602
603
static void
604
fu_efi_load_option_class_init(FuEfiLoadOptionClass *klass)
605
1
{
606
1
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
607
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
608
1
  object_class->finalize = fu_efi_load_option_finalize;
609
1
  firmware_class->parse = fu_efi_load_option_parse;
610
1
  firmware_class->write = fu_efi_load_option_write;
611
1
  firmware_class->build = fu_efi_load_option_build;
612
1
  firmware_class->export = fu_efi_load_option_export;
613
1
}
614
615
static void
616
fu_efi_load_option_init(FuEfiLoadOption *self)
617
2.06k
{
618
2.06k
  self->attrs = FU_EFI_LOAD_OPTION_ATTRS_ACTIVE;
619
2.06k
  self->metadata = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
620
2.06k
  g_type_ensure(FU_TYPE_EFI_DEVICE_PATH_LIST);
621
2.06k
}
622
623
/**
624
 * fu_efi_load_option_new:
625
 *
626
 * Returns: (transfer full): a #FuEfiLoadOption
627
 *
628
 * Since: 1.9.3
629
 **/
630
FuEfiLoadOption *
631
fu_efi_load_option_new(void)
632
0
{
633
0
  return g_object_new(FU_TYPE_EFI_LOAD_OPTION, NULL);
634
0
}