Coverage Report

Created: 2025-07-18 06:26

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