Coverage Report

Created: 2025-11-25 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/libblkid/src/partitions/gpt.c
Line
Count
Source
1
/*
2
 * EFI GPT partition parsing code
3
 *
4
 * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
5
 *
6
 * This file may be redistributed under the terms of the
7
 * GNU Lesser General Public License.
8
 *
9
 * This code is not copy & past from any other implementation.
10
 *
11
 * For more information about GPT start your study at:
12
 * http://en.wikipedia.org/wiki/GUID_Partition_Table
13
 * http://technet.microsoft.com/en-us/library/cc739412(WS.10).aspx
14
 */
15
#include <stdio.h>
16
#include <string.h>
17
#include <stdlib.h>
18
#include <stdint.h>
19
#include <stddef.h>
20
#include <limits.h>
21
#include <inttypes.h>
22
23
#include "partitions.h"
24
#include "crc32.h"
25
26
76
#define GPT_PRIMARY_LBA 1
27
28
/* Signature - “EFI PART” */
29
152
#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL
30
0
#define GPT_HEADER_SIGNATURE_STR "EFI PART"
31
32
/* basic types */
33
typedef uint16_t efi_char16_t;
34
35
/* UUID */
36
typedef struct {
37
  uint32_t time_low;
38
  uint16_t time_mid;
39
  uint16_t time_hi_and_version;
40
  uint8_t clock_seq_hi;
41
  uint8_t clock_seq_low;
42
  uint8_t node[6];
43
} efi_guid_t;
44
45
46
#define GPT_UNUSED_ENTRY_GUID \
47
0
      ((efi_guid_t) { 0x00000000, 0x0000, 0x0000, 0x00, 0x00, \
48
0
                      { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }})
49
struct gpt_header {
50
  uint64_t  signature;    /* "EFI PART" */
51
  uint32_t  revision;
52
  uint32_t  header_size;    /* usually 92 bytes */
53
  uint32_t  header_crc32;   /* checksum of header with this
54
             * field zeroed during calculation */
55
  uint32_t  reserved1;
56
57
  uint64_t  my_lba;     /* location of this header copy */
58
  uint64_t  alternate_lba;    /* location of the other header copy */
59
  uint64_t  first_usable_lba; /* first usable LBA for partitions */
60
  uint64_t  last_usable_lba;  /* last usable LBA for partitions */
61
62
  efi_guid_t  disk_guid;    /* disk UUID */
63
64
  uint64_t  partition_entries_lba;  /* always 2 in primary header copy */
65
  uint32_t  num_partition_entries;
66
  uint32_t  sizeof_partition_entry;
67
  uint32_t  partition_entry_array_crc32;
68
69
  /*
70
   * The rest of the block is reserved by UEFI and must be zero. EFI
71
   * standard handles this by:
72
   *
73
   * uint8_t    reserved2[ BLKSSZGET - 92 ];
74
   *
75
   * This definition is useless in practice. It is necessary to read
76
   * whole block from the device rather than sizeof(struct gpt_header)
77
   * only.
78
   */
79
} __attribute__ ((packed));
80
81
/*** not used
82
struct gpt_entry_attributes {
83
  uint64_t  required_to_function:1;
84
  uint64_t  reserved:47;
85
        uint64_t  type_guid_specific:16;
86
} __attribute__ ((packed));
87
***/
88
89
struct gpt_entry {
90
  efi_guid_t  partition_type_guid;  /* type UUID */
91
  efi_guid_t  unique_partition_guid;  /* partition UUID */
92
  uint64_t  starting_lba;
93
  uint64_t  ending_lba;
94
95
  /*struct gpt_entry_attributes attributes;*/
96
97
  uint64_t  attributes;
98
99
  efi_char16_t  partition_name[72 / sizeof(efi_char16_t)]; /* UTF-16LE string*/
100
} __attribute__ ((packed));
101
102
103
/*
104
 * EFI uses crc32 with ~0 seed and xor's with ~0 at the end.
105
 */
106
static inline uint32_t count_crc32(const unsigned char *buf, size_t len,
107
           size_t exclude_off, size_t exclude_len)
108
4
{
109
4
  return (ul_crc32_exclude_offset(~0L, buf, len, exclude_off, exclude_len, 0) ^ ~0L);
110
4
}
111
112
static inline const unsigned char *get_lba_buffer(blkid_probe pr,
113
          uint64_t lba, size_t bytes)
114
152
{
115
152
  return blkid_probe_get_buffer(pr,
116
152
      blkid_probe_get_sectorsize(pr) * lba, bytes);
117
152
}
118
119
static inline int guidcmp(efi_guid_t left, efi_guid_t right)
120
0
{
121
0
  return memcmp(&left, &right, sizeof (efi_guid_t));
122
0
}
123
124
/*
125
 * UUID is traditionally 16 byte big-endian array, except Intel EFI
126
 * specification where the UUID is a structure of little-endian fields.
127
 */
128
static void swap_efi_guid(efi_guid_t *uid)
129
0
{
130
0
  uid->time_low = swab32(uid->time_low);
131
0
  uid->time_mid = swab16(uid->time_mid);
132
0
  uid->time_hi_and_version = swab16(uid->time_hi_and_version);
133
0
}
134
135
static int last_lba(blkid_probe pr, uint64_t *lba)
136
5.68k
{
137
5.68k
  uint64_t sz = blkid_probe_get_size(pr);
138
5.68k
  unsigned int ssz = blkid_probe_get_sectorsize(pr);
139
140
5.68k
  if (sz < ssz)
141
0
    return -1;
142
143
5.68k
  *lba = (sz / ssz) - 1ULL;
144
5.68k
  return 0;
145
5.68k
}
146
147
/*
148
 * Protective (legacy) MBR.
149
 *
150
 * This MBR contains standard DOS partition table with a single partition, type
151
 * of 0xEE.  The partition usually encompassing the entire GPT drive - or 2TiB
152
 * for large disks.
153
 *
154
 * Note that Apple uses GPT/MBR hybrid disks, where the DOS partition table is
155
 * synchronized with GPT. This synchronization has many restriction of course
156
 * (due DOS PT limitations).
157
 *
158
 * Note that the PMBR detection is optional (enabled by default) and could be
159
 * disabled by BLKID_PARTS_FOPCE_GPT flag (see also blkid_partitions_set_flags()).
160
 */
161
static int is_pmbr_valid(blkid_probe pr, int *has)
162
5.68k
{
163
5.68k
  int flags = blkid_partitions_get_flags(pr);
164
5.68k
  const unsigned char *data;
165
5.68k
  const struct dos_partition *p;
166
5.68k
  int i;
167
168
5.68k
  if (has)
169
956
    *has = 0;
170
4.73k
  else if (flags & BLKID_PARTS_FORCE_GPT)
171
0
    return 1;     /* skip PMBR check */
172
173
5.68k
  data = blkid_probe_get_sector(pr, 0);
174
5.68k
  if (!data) {
175
0
    if (errno)
176
0
      return -errno;
177
0
    goto failed;
178
0
  }
179
180
5.68k
  if (!mbr_is_valid_magic(data))
181
3.77k
    goto failed;
182
183
9.30k
  for (i = 0, p = mbr_get_partition(data, 0); i < 4; i++, p++) {
184
7.46k
    if (p->sys_ind == MBR_GPT_PARTITION) {
185
76
      DBG(LOWPROBE, ul_debug(" #%d valid PMBR partition", i + 1));
186
76
      goto ok;
187
76
    }
188
7.46k
  }
189
5.61k
failed:
190
5.61k
  return 0;
191
76
ok:
192
76
  if (has)
193
38
    *has = 1;
194
76
  return 1;
195
1.91k
}
196
197
/*
198
 * Reads GPT header to @hdr and returns a pointer to @hdr or NULL in case of
199
 * error. The function also returns GPT entries in @ents.
200
 *
201
 * Note, this function does not allocate any memory. The GPT header has fixed
202
 * size so we use stack, and @ents returns memory from libblkid buffer (so the
203
 * next blkid_probe_get_buffer() will overwrite this buffer).
204
 *
205
 * This function checks validity of header and entries array. A corrupted
206
 * header is not returned.
207
 */
208
static struct gpt_header *get_gpt_header(
209
        blkid_probe pr, struct gpt_header *hdr,
210
        struct gpt_entry **ents, uint64_t lba,
211
        uint64_t lastlba)
212
152
{
213
152
  struct gpt_header *h;
214
152
  uint32_t crc;
215
152
  uint64_t lu, fu;
216
152
  uint64_t esz;
217
152
  uint32_t hsz, ssz;
218
219
152
  ssz = blkid_probe_get_sectorsize(pr);
220
221
152
  DBG(LOWPROBE, ul_debug(" checking for GPT header at %"PRIu64, lba));
222
223
  /* whole sector is allocated for GPT header */
224
152
  h = (struct gpt_header *) get_lba_buffer(pr, lba, ssz);
225
152
  if (!h)
226
0
    return NULL;
227
228
152
  if (le64_to_cpu(h->signature) != GPT_HEADER_SIGNATURE)
229
140
    return NULL;
230
231
12
  hsz = le32_to_cpu(h->header_size);
232
233
  /* EFI: The HeaderSize must be greater than 92 and must be less
234
   *      than or equal to the logical block size.
235
   */
236
12
  if (hsz > ssz || hsz < sizeof(*h))
237
8
    return NULL;
238
239
  /* Header has to be verified when header_crc32 is zero */
240
4
  crc = count_crc32((unsigned char *) h, hsz,
241
4
      offsetof(struct gpt_header, header_crc32),
242
4
      sizeof(h->header_crc32));
243
244
4
  if (!blkid_probe_verify_csum(pr, crc, le32_to_cpu(h->header_crc32))) {
245
4
    DBG(LOWPROBE, ul_debug("GPT header corrupted"));
246
4
    return NULL;
247
4
  }
248
249
  /* Valid header has to be at MyLBA */
250
0
  if (le64_to_cpu(h->my_lba) != lba) {
251
0
    DBG(LOWPROBE, ul_debug(
252
0
      "GPT->MyLBA mismatch with real position"));
253
0
    return NULL;
254
0
  }
255
256
0
  fu = le64_to_cpu(h->first_usable_lba);
257
0
  lu = le64_to_cpu(h->last_usable_lba);
258
259
  /* Check if First and Last usable LBA makes sense */
260
0
  if (lu < fu || fu > lastlba || lu > lastlba) {
261
0
    DBG(LOWPROBE, ul_debug(
262
0
      "GPT->{First,Last}UsableLBA out of range"));
263
0
    return NULL;
264
0
  }
265
266
  /* The header has to be outside usable range */
267
0
  if (fu < lba && lba < lu) {
268
0
    DBG(LOWPROBE, ul_debug("GPT header is inside usable area"));
269
0
    return NULL;
270
0
  }
271
272
  /* Size of blocks with GPT entries */
273
0
  esz = (uint64_t)le32_to_cpu(h->num_partition_entries) *
274
0
    le32_to_cpu(h->sizeof_partition_entry);
275
276
0
  if (esz == 0 || esz >= UINT32_MAX ||
277
0
      le32_to_cpu(h->sizeof_partition_entry) != sizeof(struct gpt_entry)) {
278
0
    DBG(LOWPROBE, ul_debug("GPT entries undefined"));
279
0
    return NULL;
280
0
  }
281
282
  /* The header seems valid, save it
283
   * (we don't care about zeros in hdr->reserved2 area) */
284
0
  memcpy(hdr, h, sizeof(*h));
285
0
  h = hdr;
286
287
  /* Read GPT entries */
288
0
  *ents = (struct gpt_entry *) get_lba_buffer(pr,
289
0
        le64_to_cpu(h->partition_entries_lba), esz);
290
0
  if (!*ents) {
291
0
    DBG(LOWPROBE, ul_debug("GPT entries unreadable"));
292
0
    return NULL;
293
0
  }
294
295
  /* Validate entries */
296
0
  crc = count_crc32((unsigned char *) *ents, esz, 0, 0);
297
0
  if (!blkid_probe_verify_csum(pr, crc, le32_to_cpu(h->partition_entry_array_crc32))) {
298
0
    DBG(LOWPROBE, ul_debug("GPT entries corrupted"));
299
0
    return NULL;
300
0
  }
301
302
0
  return h;
303
0
}
304
305
static int probe_gpt_pt(blkid_probe pr,
306
    const struct blkid_idmag *mag __attribute__((__unused__)))
307
4.73k
{
308
4.73k
  uint64_t lastlba = 0, lba;
309
4.73k
  struct gpt_header hdr, *h;
310
4.73k
  struct gpt_entry *e;
311
4.73k
  blkid_parttable tab = NULL;
312
4.73k
  blkid_partlist ls;
313
4.73k
  uint64_t fu, lu;
314
4.73k
  uint32_t ssf, i;
315
4.73k
  efi_guid_t guid;
316
4.73k
  int ret;
317
318
4.73k
  if (last_lba(pr, &lastlba))
319
0
    goto nothing;
320
321
4.73k
  ret = is_pmbr_valid(pr, NULL);
322
4.73k
  if (ret < 0)
323
0
    return ret;
324
4.73k
  if (ret == 0)
325
4.69k
    goto nothing;
326
327
4.73k
  errno = 0;
328
38
  h = get_gpt_header(pr, &hdr, &e, (lba = GPT_PRIMARY_LBA), lastlba);
329
38
  if (!h && !errno)
330
38
    h = get_gpt_header(pr, &hdr, &e, (lba = lastlba), lastlba);
331
332
38
  if (!h) {
333
38
    if (errno)
334
0
      return -errno;
335
38
    goto nothing;
336
38
  }
337
338
0
  blkid_probe_use_wiper(pr, lba * blkid_probe_get_size(pr), 8);
339
340
0
  if (blkid_probe_set_magic(pr, blkid_probe_get_sectorsize(pr) * lba,
341
0
            sizeof(GPT_HEADER_SIGNATURE_STR) - 1,
342
0
            (unsigned char *) GPT_HEADER_SIGNATURE_STR))
343
0
    goto err;
344
345
0
  guid = h->disk_guid;
346
0
  swap_efi_guid(&guid);
347
348
0
  if (blkid_partitions_need_typeonly(pr)) {
349
    /* Non-binary interface -- caller does not ask for details
350
     * about partitions, just set generic variables only. */
351
0
    blkid_partitions_set_ptuuid(pr, (unsigned char *) &guid);
352
0
    return BLKID_PROBE_OK;
353
0
  }
354
355
0
  ls = blkid_probe_get_partlist(pr);
356
0
  if (!ls)
357
0
    goto nothing;
358
359
0
  tab = blkid_partlist_new_parttable(ls, "gpt",
360
0
        blkid_probe_get_sectorsize(pr) * lba);
361
0
  if (!tab)
362
0
    goto err;
363
364
0
  blkid_parttable_set_uuid(tab, (const unsigned char *) &guid);
365
366
0
  ssf = blkid_probe_get_sectorsize(pr) / 512;
367
368
0
  fu = le64_to_cpu(h->first_usable_lba);
369
0
  lu = le64_to_cpu(h->last_usable_lba);
370
371
0
  for (i = 0; i < le32_to_cpu(h->num_partition_entries); i++, e++) {
372
373
0
    blkid_partition par;
374
0
    uint64_t start = le64_to_cpu(e->starting_lba);
375
0
    uint64_t size = le64_to_cpu(e->ending_lba) -
376
0
          le64_to_cpu(e->starting_lba) + 1ULL;
377
378
    /* 00000000-0000-0000-0000-000000000000 entry */
379
0
    if (!guidcmp(e->partition_type_guid, GPT_UNUSED_ENTRY_GUID)) {
380
0
      blkid_partlist_increment_partno(ls);
381
0
      continue;
382
0
    }
383
    /* the partition has to inside usable range */
384
0
    if (start < fu || start + size - 1 > lu) {
385
0
      DBG(LOWPROBE, ul_debug(
386
0
        "GPT entry[%d] overflows usable area - ignore",
387
0
        i));
388
0
      blkid_partlist_increment_partno(ls);
389
0
      continue;
390
0
    }
391
392
0
    par = blkid_partlist_add_partition(ls, tab,
393
0
          start * ssf, size * ssf);
394
0
    if (!par)
395
0
      goto err;
396
397
0
    blkid_partition_set_utf8name(par,
398
0
      (unsigned char *) e->partition_name,
399
0
      sizeof(e->partition_name), UL_ENCODE_UTF16LE);
400
401
0
    guid = e->unique_partition_guid;
402
0
    swap_efi_guid(&guid);
403
0
    blkid_partition_set_uuid(par, (const unsigned char *) &guid);
404
405
0
    guid = e->partition_type_guid;
406
0
    swap_efi_guid(&guid);
407
0
    blkid_partition_set_type_uuid(par, (const unsigned char *) &guid);
408
409
0
    blkid_partition_set_flags(par, le64_to_cpu(e->attributes));
410
0
  }
411
412
0
  return BLKID_PROBE_OK;
413
414
4.73k
nothing:
415
4.73k
  return BLKID_PROBE_NONE;
416
417
0
err:
418
0
  return -ENOMEM;
419
0
}
420
421
422
const struct blkid_idinfo gpt_pt_idinfo =
423
{
424
  .name   = "gpt",
425
  .probefunc  = probe_gpt_pt,
426
427
  /*
428
   * It would be possible to check for DOS signature (0xAA55), but
429
   * unfortunately almost all EFI GPT implementations allow to optionally
430
   * skip the legacy MBR. We follows this behavior and MBR is optional.
431
   * See is_valid_pmbr().
432
   *
433
   * It means we have to always call probe_gpt_pt().
434
   */
435
  .magics   = BLKID_NONE_MAGIC
436
};
437
438
439
440
/* probe for *alone* protective MBR */
441
static int probe_pmbr_pt(blkid_probe pr,
442
    const struct blkid_idmag *mag __attribute__((__unused__)))
443
956
{
444
956
  int has = 0;
445
956
  struct gpt_entry *e;
446
956
  uint64_t lastlba = 0;
447
956
  struct gpt_header hdr;
448
449
956
  if (last_lba(pr, &lastlba))
450
0
    goto nothing;
451
452
956
  is_pmbr_valid(pr, &has);
453
956
  if (!has)
454
918
    goto nothing;
455
456
38
  if (!get_gpt_header(pr, &hdr, &e, GPT_PRIMARY_LBA, lastlba) &&
457
38
      !get_gpt_header(pr, &hdr, &e, lastlba, lastlba))
458
38
    return 0;
459
918
nothing:
460
918
  return 1;
461
38
}
462
463
const struct blkid_idinfo pmbr_pt_idinfo =
464
{
465
  .name   = "PMBR",
466
  .probefunc  = probe_pmbr_pt,
467
  .magics   =
468
  {
469
    { .magic = "\x55\xAA", .len = 2, .sboff = 510 },
470
    { NULL }
471
  }
472
};
473