/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 | | |