Coverage Report

Created: 2026-01-09 07:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-efi-hard-drive-device-path.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
0
#define G_LOG_DOMAIN "FuEfiDevicePath"
8
9
#include "config.h"
10
11
#include "fu-common.h"
12
#include "fu-efi-hard-drive-device-path.h"
13
#include "fu-efi-struct.h"
14
#include "fu-firmware-common.h"
15
#include "fu-mem.h"
16
#include "fu-string.h"
17
18
/**
19
 * FuEfiHardDriveDevicePath:
20
 *
21
 * See also: [class@FuEfiDevicePath]
22
 */
23
24
struct _FuEfiHardDriveDevicePath {
25
  FuEfiDevicePath parent_instance;
26
  guint32 partition_number;
27
  guint64 partition_start; /* blocks */
28
  guint64 partition_size;  /* blocks */
29
  fwupd_guid_t partition_signature;
30
  FuEfiHardDriveDevicePathPartitionFormat partition_format;
31
  FuEfiHardDriveDevicePathSignatureType signature_type;
32
};
33
34
static void
35
fu_efi_hard_drive_device_path_codec_iface_init(FwupdCodecInterface *iface);
36
37
4.12k
G_DEFINE_TYPE_EXTENDED(FuEfiHardDriveDevicePath,
38
4.12k
           fu_efi_hard_drive_device_path,
39
4.12k
           FU_TYPE_EFI_DEVICE_PATH,
40
4.12k
           0,
41
4.12k
           G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC,
42
4.12k
               fu_efi_hard_drive_device_path_codec_iface_init))
43
4.12k
44
4.12k
#define BLOCK_SIZE_FALLBACK 0x200
45
46
static void
47
fu_efi_hard_drive_device_path_export(FuFirmware *firmware,
48
             FuFirmwareExportFlags flags,
49
             XbBuilderNode *bn)
50
0
{
51
0
  FuEfiHardDriveDevicePath *self = FU_EFI_HARD_DRIVE_DEVICE_PATH(firmware);
52
0
  g_autofree gchar *partition_signature =
53
0
      fwupd_guid_to_string(&self->partition_signature, FWUPD_GUID_FLAG_MIXED_ENDIAN);
54
0
  fu_xmlb_builder_insert_kx(bn, "partition_number", self->partition_number);
55
0
  fu_xmlb_builder_insert_kx(bn, "partition_start", self->partition_start);
56
0
  fu_xmlb_builder_insert_kx(bn, "partition_size", self->partition_size);
57
0
  fu_xmlb_builder_insert_kv(bn, "partition_signature", partition_signature);
58
0
  fu_xmlb_builder_insert_kv(
59
0
      bn,
60
0
      "partition_format",
61
0
      fu_efi_hard_drive_device_path_partition_format_to_string(self->partition_format));
62
0
  fu_xmlb_builder_insert_kv(
63
0
      bn,
64
0
      "signature_type",
65
0
      fu_efi_hard_drive_device_path_signature_type_to_string(self->signature_type));
66
0
}
67
68
static void
69
fu_efi_hard_drive_device_path_add_json(FwupdCodec *codec,
70
               FwupdJsonObject *json_obj,
71
               FwupdCodecFlags flags)
72
0
{
73
0
  FuEfiHardDriveDevicePath *self = FU_EFI_HARD_DRIVE_DEVICE_PATH(codec);
74
0
  g_autofree gchar *partition_signature =
75
0
      fwupd_guid_to_string(&self->partition_signature, FWUPD_GUID_FLAG_MIXED_ENDIAN);
76
77
0
  fwupd_json_object_add_integer(json_obj, "PartitionNumber", self->partition_number);
78
0
  fwupd_json_object_add_integer(json_obj, "PartitionStart", self->partition_start);
79
0
  fwupd_json_object_add_integer(json_obj, "PartitionSize", self->partition_size);
80
0
  if (partition_signature != NULL)
81
0
    fwupd_json_object_add_string(json_obj, "PartitionSignature", partition_signature);
82
0
  fwupd_json_object_add_string(
83
0
      json_obj,
84
0
      "PartitionFormat",
85
0
      fu_efi_hard_drive_device_path_partition_format_to_string(self->partition_format));
86
0
  fwupd_json_object_add_string(
87
0
      json_obj,
88
0
      "SignatureType",
89
0
      fu_efi_hard_drive_device_path_signature_type_to_string(self->signature_type));
90
0
}
91
92
static gboolean
93
fu_efi_hard_drive_device_path_parse(FuFirmware *firmware,
94
            GInputStream *stream,
95
            FuFirmwareParseFlags flags,
96
            GError **error)
97
830
{
98
830
  FuEfiHardDriveDevicePath *self = FU_EFI_HARD_DRIVE_DEVICE_PATH(firmware);
99
830
  g_autoptr(FuStructEfiHardDriveDevicePath) st = NULL;
100
101
  /* re-parse */
102
830
  st = fu_struct_efi_hard_drive_device_path_parse_stream(stream, 0x0, error);
103
830
  if (st == NULL)
104
40
    return FALSE;
105
790
  self->partition_number = fu_struct_efi_hard_drive_device_path_get_partition_number(st);
106
790
  self->partition_start = fu_struct_efi_hard_drive_device_path_get_partition_start(st);
107
790
  self->partition_size = fu_struct_efi_hard_drive_device_path_get_partition_size(st);
108
790
  memcpy(self->partition_signature, /* nocheck:blocked */
109
790
         fu_struct_efi_hard_drive_device_path_get_partition_signature(st),
110
790
         sizeof(self->partition_signature));
111
790
  self->partition_format = fu_struct_efi_hard_drive_device_path_get_partition_format(st);
112
790
  self->signature_type = fu_struct_efi_hard_drive_device_path_get_signature_type(st);
113
114
  /* success */
115
790
  fu_firmware_set_size(firmware, st->buf->len);
116
790
  return TRUE;
117
830
}
118
119
static GByteArray *
120
fu_efi_hard_drive_device_path_write(FuFirmware *firmware, GError **error)
121
389
{
122
389
  FuEfiHardDriveDevicePath *self = FU_EFI_HARD_DRIVE_DEVICE_PATH(firmware);
123
389
  g_autoptr(FuStructEfiHardDriveDevicePath) st = fu_struct_efi_hard_drive_device_path_new();
124
125
  /* required */
126
389
  fu_struct_efi_hard_drive_device_path_set_partition_number(st, self->partition_number);
127
389
  fu_struct_efi_hard_drive_device_path_set_partition_start(st, self->partition_start);
128
389
  fu_struct_efi_hard_drive_device_path_set_partition_size(st, self->partition_size);
129
389
  fu_struct_efi_hard_drive_device_path_set_partition_signature(st,
130
389
                     &self->partition_signature);
131
389
  fu_struct_efi_hard_drive_device_path_set_partition_format(st, self->partition_format);
132
389
  fu_struct_efi_hard_drive_device_path_set_signature_type(st, self->signature_type);
133
134
  /* success */
135
389
  return g_steal_pointer(&st->buf);
136
389
}
137
138
static gboolean
139
fu_efi_hard_drive_device_path_build(FuFirmware *firmware, XbNode *n, GError **error)
140
0
{
141
0
  FuEfiHardDriveDevicePath *self = FU_EFI_HARD_DRIVE_DEVICE_PATH(firmware);
142
0
  const gchar *tmp;
143
0
  guint64 value = 0;
144
145
  /* optional data */
146
0
  tmp = xb_node_query_text(n, "partition_number", NULL);
147
0
  if (tmp != NULL) {
148
0
    if (!fu_strtoull(tmp, &value, 0x0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error))
149
0
      return FALSE;
150
0
    self->partition_number = value;
151
0
  }
152
0
  tmp = xb_node_query_text(n, "partition_start", NULL);
153
0
  if (tmp != NULL) {
154
0
    if (!fu_strtoull(tmp, &value, 0x0, G_MAXUINT64, FU_INTEGER_BASE_AUTO, error))
155
0
      return FALSE;
156
0
    self->partition_start = value;
157
0
  }
158
0
  tmp = xb_node_query_text(n, "partition_size", NULL);
159
0
  if (tmp != NULL) {
160
0
    if (!fu_strtoull(tmp, &value, 0x0, G_MAXUINT64, FU_INTEGER_BASE_AUTO, error))
161
0
      return FALSE;
162
0
    self->partition_size = value;
163
0
  }
164
0
  tmp = xb_node_query_text(n, "partition_signature", NULL);
165
0
  if (tmp != NULL) {
166
0
    if (!fwupd_guid_from_string(tmp,
167
0
              &self->partition_signature,
168
0
              FWUPD_GUID_FLAG_MIXED_ENDIAN,
169
0
              error))
170
0
      return FALSE;
171
0
  }
172
0
  tmp = xb_node_query_text(n, "partition_format", NULL);
173
0
  if (tmp != NULL) {
174
0
    self->partition_format =
175
0
        fu_efi_hard_drive_device_path_partition_format_from_string(tmp);
176
0
  }
177
0
  tmp = xb_node_query_text(n, "signature_type", NULL);
178
0
  if (tmp != NULL) {
179
0
    self->signature_type =
180
0
        fu_efi_hard_drive_device_path_signature_type_from_string(tmp);
181
0
  }
182
183
  /* success */
184
0
  return TRUE;
185
0
}
186
187
static void
188
fu_efi_hard_drive_device_path_init(FuEfiHardDriveDevicePath *self)
189
830
{
190
830
  fu_firmware_set_idx(FU_FIRMWARE(self), FU_EFI_DEVICE_PATH_TYPE_MEDIA);
191
830
  fu_efi_device_path_set_subtype(FU_EFI_DEVICE_PATH(self),
192
830
               FU_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE_HARD_DRIVE);
193
830
}
194
195
static void
196
fu_efi_hard_drive_device_path_class_init(FuEfiHardDriveDevicePathClass *klass)
197
1
{
198
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
199
1
  firmware_class->export = fu_efi_hard_drive_device_path_export;
200
1
  firmware_class->parse = fu_efi_hard_drive_device_path_parse;
201
1
  firmware_class->write = fu_efi_hard_drive_device_path_write;
202
1
  firmware_class->build = fu_efi_hard_drive_device_path_build;
203
1
}
204
205
/**
206
 * fu_efi_hard_drive_device_path_get_partition_signature:
207
 * @self: a #FuEfiHardDriveDevicePath
208
 *
209
 * Gets the DP partition signature.
210
 *
211
 * Returns: a #fwupd_guid_t
212
 *
213
 * Since: 2.0.0
214
 **/
215
const fwupd_guid_t *
216
fu_efi_hard_drive_device_path_get_partition_signature(FuEfiHardDriveDevicePath *self)
217
0
{
218
0
  g_return_val_if_fail(FU_IS_EFI_HARD_DRIVE_DEVICE_PATH(self), NULL);
219
0
  return &self->partition_signature;
220
0
}
221
222
/**
223
 * fu_efi_hard_drive_device_path_get_partition_size:
224
 * @self: a #FuEfiHardDriveDevicePath
225
 *
226
 * Gets the DP partition size.
227
 *
228
 * NOTE: This are multiples of the block size, which can be found using fu_volume_get_block_size()
229
 *
230
 * Returns: integer
231
 *
232
 * Since: 2.0.0
233
 **/
234
guint64
235
fu_efi_hard_drive_device_path_get_partition_size(FuEfiHardDriveDevicePath *self)
236
0
{
237
0
  g_return_val_if_fail(FU_IS_EFI_HARD_DRIVE_DEVICE_PATH(self), 0);
238
0
  return self->partition_size;
239
0
}
240
241
/**
242
 * fu_efi_hard_drive_device_path_get_partition_start:
243
 * @self: a #FuEfiHardDriveDevicePath
244
 *
245
 * Gets the DP partition start.
246
 *
247
 * NOTE: This are multiples of the block size, which can be found using fu_volume_get_block_size()
248
 *
249
 * Returns: integer
250
 *
251
 * Since: 2.0.0
252
 **/
253
guint64
254
fu_efi_hard_drive_device_path_get_partition_start(FuEfiHardDriveDevicePath *self)
255
0
{
256
0
  g_return_val_if_fail(FU_IS_EFI_HARD_DRIVE_DEVICE_PATH(self), 0);
257
0
  return self->partition_start;
258
0
}
259
260
/**
261
 * fu_efi_hard_drive_device_path_get_partition_number:
262
 * @self: a #FuEfiHardDriveDevicePath
263
 *
264
 * Gets the DP partition number.
265
 *
266
 * Returns: integer
267
 *
268
 * Since: 2.0.0
269
 **/
270
guint32
271
fu_efi_hard_drive_device_path_get_partition_number(FuEfiHardDriveDevicePath *self)
272
0
{
273
0
  g_return_val_if_fail(FU_IS_EFI_HARD_DRIVE_DEVICE_PATH(self), 0);
274
0
  return self->partition_number;
275
0
}
276
277
/**
278
 * fu_efi_hard_drive_device_path_compare:
279
 * @dp1: a #FuEfiHardDriveDevicePath
280
 * @dp2: a #FuEfiHardDriveDevicePath
281
 *
282
 * Compares two EFI HardDrive `DEVICE_PATH`s.
283
 *
284
 * Returns: %TRUE is considered equal
285
 *
286
 * Since: 2.0.0
287
 **/
288
gboolean
289
fu_efi_hard_drive_device_path_compare(FuEfiHardDriveDevicePath *dp1, FuEfiHardDriveDevicePath *dp2)
290
0
{
291
0
  g_return_val_if_fail(FU_IS_EFI_HARD_DRIVE_DEVICE_PATH(dp1), FALSE);
292
0
  g_return_val_if_fail(FU_IS_EFI_HARD_DRIVE_DEVICE_PATH(dp2), FALSE);
293
294
0
  if (dp1->partition_format != dp2->partition_format)
295
0
    return FALSE;
296
0
  if (dp1->signature_type != dp2->signature_type)
297
0
    return FALSE;
298
0
  if (memcmp(dp1->partition_signature, dp2->partition_signature, sizeof(fwupd_guid_t)) != 0)
299
0
    return FALSE;
300
0
  if (dp1->partition_number != dp2->partition_number)
301
0
    return FALSE;
302
0
  if (dp1->partition_start != dp2->partition_start)
303
0
    return FALSE;
304
0
  if (dp1->partition_size != dp2->partition_size)
305
0
    return FALSE;
306
0
  return TRUE;
307
0
}
308
309
static void
310
fu_efi_hard_drive_device_path_codec_iface_init(FwupdCodecInterface *iface)
311
1
{
312
1
  iface->add_json = fu_efi_hard_drive_device_path_add_json;
313
1
}
314
315
/**
316
 * fu_efi_hard_drive_device_path_new:
317
 *
318
 * Creates a new EFI `DEVICE_PATH`.
319
 *
320
 * Returns: (transfer full): a #FuEfiHardDriveDevicePath
321
 *
322
 * Since: 1.9.3
323
 **/
324
FuEfiHardDriveDevicePath *
325
fu_efi_hard_drive_device_path_new(void)
326
830
{
327
830
  return g_object_new(FU_TYPE_EFI_HARD_DRIVE_DEVICE_PATH, NULL);
328
830
}
329
330
/**
331
 * fu_efi_hard_drive_device_path_new_from_volume:
332
 * @volume: a #FuVolume
333
 * @error: (nullable): optional return location for an error
334
 *
335
 * Creates a new EFI `DEVICE_PATH` for a specific volume.
336
 *
337
 * Returns: (transfer full): a #FuEfiHardDriveDevicePath, or %NULL on error
338
 *
339
 * Since: 1.9.3
340
 **/
341
FuEfiHardDriveDevicePath *
342
fu_efi_hard_drive_device_path_new_from_volume(FuVolume *volume, GError **error)
343
0
{
344
0
  guint16 block_size;
345
0
  g_autoptr(FuEfiHardDriveDevicePath) self = fu_efi_hard_drive_device_path_new();
346
0
  g_autofree gchar *partition_kind = NULL;
347
0
  g_autofree gchar *partition_uuid = NULL;
348
0
  g_autoptr(GError) error_local = NULL;
349
350
0
  g_return_val_if_fail(FU_IS_VOLUME(volume), NULL);
351
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
352
353
  /* common to both */
354
0
  block_size = fu_volume_get_block_size(volume, &error_local);
355
0
  if (block_size == 0) {
356
0
    g_debug("failed to get volume block size, falling back to 0x%x: %s",
357
0
      (guint)BLOCK_SIZE_FALLBACK,
358
0
      error_local->message);
359
0
    block_size = BLOCK_SIZE_FALLBACK;
360
0
  }
361
0
  self->partition_number = fu_volume_get_partition_number(volume);
362
0
  self->partition_start = fu_volume_get_partition_offset(volume) / block_size;
363
0
  self->partition_size = fu_volume_get_partition_size(volume) / block_size;
364
365
  /* set up the rest of the struct */
366
0
  partition_kind = fu_volume_get_partition_kind(volume);
367
0
  if (partition_kind == NULL) {
368
0
    g_set_error_literal(error,
369
0
            FWUPD_ERROR,
370
0
            FWUPD_ERROR_NOT_SUPPORTED,
371
0
            "partition kind required");
372
0
    return NULL;
373
0
  }
374
0
  partition_uuid = fu_volume_get_partition_uuid(volume);
375
0
  if (partition_uuid == NULL) {
376
0
    g_set_error_literal(error,
377
0
            FWUPD_ERROR,
378
0
            FWUPD_ERROR_NOT_SUPPORTED,
379
0
            "partition UUID required");
380
0
    return NULL;
381
0
  }
382
0
  if (g_strcmp0(partition_kind, FU_VOLUME_KIND_ESP) == 0 ||
383
0
      g_strcmp0(partition_kind, FU_VOLUME_KIND_BDP) == 0) {
384
0
    self->partition_format =
385
0
        FU_EFI_HARD_DRIVE_DEVICE_PATH_PARTITION_FORMAT_GUID_PARTITION_TABLE;
386
0
    self->signature_type = FU_EFI_HARD_DRIVE_DEVICE_PATH_SIGNATURE_TYPE_GUID;
387
0
    if (!fwupd_guid_from_string(partition_uuid,
388
0
              &self->partition_signature,
389
0
              FWUPD_GUID_FLAG_MIXED_ENDIAN,
390
0
              error))
391
0
      return NULL;
392
0
  } else if (g_strcmp0(partition_kind, "0xef") == 0) {
393
0
    guint32 value = 0;
394
0
    g_auto(GStrv) parts = g_strsplit(partition_uuid, "-", -1);
395
0
    if (!fu_firmware_strparse_uint32_safe(parts[0],
396
0
                  strlen(parts[0]),
397
0
                  0x0,
398
0
                  &value,
399
0
                  error)) {
400
0
      g_prefix_error(error, "failed to parse %s: ", parts[0]);
401
0
      return NULL;
402
0
    }
403
0
    if (!fu_memwrite_uint32_safe(self->partition_signature,
404
0
               sizeof(self->partition_signature),
405
0
               0x0,
406
0
               value,
407
0
               G_LITTLE_ENDIAN,
408
0
               error))
409
0
      return NULL;
410
0
    self->partition_format = FU_EFI_HARD_DRIVE_DEVICE_PATH_PARTITION_FORMAT_LEGACY_MBR;
411
0
    self->signature_type = FU_EFI_HARD_DRIVE_DEVICE_PATH_SIGNATURE_TYPE_ADDR1B8;
412
0
  } else {
413
0
    g_set_error(error,
414
0
          FWUPD_ERROR,
415
0
          FWUPD_ERROR_NOT_SUPPORTED,
416
0
          "partition kind %s not supported",
417
0
          partition_kind);
418
0
    return NULL;
419
0
  }
420
421
  /* success */
422
0
  return g_steal_pointer(&self);
423
0
}