1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <gpt/gpt.h>
6 #include <lib/cksum.h>
7 #include <zircon/syscalls.h> // for zx_cprng_draw
8 #include <zircon/device/block.h>
9 #include <assert.h>
10 #include <errno.h>
11 #include <inttypes.h>
12 #include <stdbool.h>
13 #include <stddef.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/param.h>
18
19 #include "gpt/gpt.h"
20
21 namespace libgpt {
22
23 namespace {
24 #define G_PRINTF(f, ...) \
25 if (debug_out) \
26 printf((f), ##__VA_ARGS__);
27
28 bool debug_out = false;
29
30 struct mbr_partition_t {
31 uint8_t status;
32 uint8_t chs_first[3];
33 uint8_t type;
34 uint8_t chs_last[3];
35 uint32_t lba;
36 uint32_t sectors;
37 };
38
39 // Since the human-readable representation of a GUID is the following format,
40 // ordered little-endian, it is useful to group a GUID into these
41 // appropriately-sized groups.
42 struct guid_t {
43 uint32_t data1;
44 uint16_t data2;
45 uint16_t data3;
46 uint8_t data4[8];
47 };
48
49 static_assert(sizeof(gpt_header_t) == GPT_HEADER_SIZE, "unexpected gpt header size");
50 static_assert(sizeof(gpt_partition_t) == GPT_ENTRY_SIZE, "unexpected gpt entry size");
51
52 struct gpt_priv_t {
53 // device to use
54 int fd;
55
56 // block size in bytes
57 uint64_t blocksize;
58
59 // number of blocks
60 uint64_t blocks;
61
62 // true if valid mbr exists on disk
63 bool mbr;
64
65 // header buffer, should be primary copy
66 gpt_header_t header;
67
68 // partition table buffer
69 gpt_partition_t ptable[PARTITIONS_COUNT];
70
71 // copy of buffer from when last init'd or sync'd.
72 gpt_partition_t backup[PARTITIONS_COUNT];
73
74 gpt_device_t device;
75 };
76
77 #define get_priv(dev) ((gpt_priv_t*)((uintptr_t)(dev)-offsetof(gpt_priv_t, device)))
78
print_array(gpt_partition_t * const a[PARTITIONS_COUNT],int c)79 void print_array(gpt_partition_t* const a[PARTITIONS_COUNT], int c) {
80 char GUID[GPT_GUID_STRLEN];
81 char name[GPT_NAME_LEN / 2 + 1];
82
83 for (int i = 0; i < c; ++i) {
84 uint8_to_guid_string(GUID, a[i]->type);
85 memset(name, 0, GPT_NAME_LEN / 2 + 1);
86 utf16_to_cstring(name, (const uint16_t*)a[i]->name, GPT_NAME_LEN / 2);
87
88 printf("Name: %s \n Start: %lu -- End: %lu \nType: %s\n",
89 name, a[i]->first, a[i]->last, GUID);
90 }
91 }
92
partition_init(gpt_partition_t * part,const char * name,const uint8_t * type,const uint8_t * guid,uint64_t first,uint64_t last,uint64_t flags)93 void partition_init(gpt_partition_t* part, const char* name, const uint8_t* type,
94 const uint8_t* guid, uint64_t first, uint64_t last, uint64_t flags) {
95 memcpy(part->type, type, sizeof(part->type));
96 memcpy(part->guid, guid, sizeof(part->guid));
97 part->first = first;
98 part->last = last;
99 part->flags = flags;
100 cstring_to_utf16((uint16_t*)part->name, name, sizeof(part->name) / sizeof(uint16_t));
101 }
102
gpt_sync_current(int fd,uint64_t blocksize,gpt_header_t * header,gpt_partition_t * ptable)103 int gpt_sync_current(int fd, uint64_t blocksize, gpt_header_t* header,
104 gpt_partition_t* ptable) {
105 // write partition table first
106 off_t rc = lseek(fd, header->entries * blocksize, SEEK_SET);
107 if (rc < 0) {
108 return -1;
109 }
110 size_t ptable_size = header->entries_count * header->entries_size;
111 rc = write(fd, ptable, ptable_size);
112 if (rc < 0 || (size_t)rc != ptable_size) {
113 return -1;
114 }
115 // then write the header
116 rc = lseek(fd, header->current * blocksize, SEEK_SET);
117 if (rc < 0) {
118 return -1;
119 }
120
121 uint8_t block[blocksize];
122 memset(block, 0, sizeof(blocksize));
123 memcpy(block, header, sizeof(*header));
124 rc = write(fd, block, blocksize);
125 if (rc != (ssize_t)blocksize) {
126 return -1;
127 }
128 return 0;
129 }
130
gpt_device_finalize_and_sync(gpt_device_t * dev,bool persist)131 int gpt_device_finalize_and_sync(gpt_device_t* dev, bool persist) {
132 gpt_priv_t* priv = get_priv(dev);
133
134 // write fake mbr if needed
135 uint8_t mbr[priv->blocksize];
136 off_t rc;
137 if (!priv->mbr) {
138 memset(mbr, 0, priv->blocksize);
139 mbr[0x1fe] = 0x55;
140 mbr[0x1ff] = 0xaa;
141 mbr_partition_t* mpart = (mbr_partition_t*)(mbr + 0x1be);
142 mpart->chs_first[1] = 0x1;
143 mpart->type = 0xee; // gpt protective mbr
144 mpart->chs_last[0] = 0xfe;
145 mpart->chs_last[1] = 0xff;
146 mpart->chs_last[2] = 0xff;
147 mpart->lba = 1;
148 mpart->sectors = priv->blocks & 0xffffffff;
149 rc = lseek(priv->fd, 0, SEEK_SET);
150 if (rc < 0) {
151 return -1;
152 }
153 rc = write(priv->fd, mbr, priv->blocksize);
154 if (rc < 0 || (size_t)rc != priv->blocksize) {
155 return -1;
156 }
157 priv->mbr = true;
158 }
159
160 // fill in the new header fields
161 gpt_header_t header;
162 memset(&header, 0, sizeof(header));
163 header.magic = GPT_MAGIC;
164 header.revision = 0x00010000; // gpt version 1.0
165 header.size = GPT_HEADER_SIZE;
166 if (dev->valid) {
167 header.current = priv->header.current;
168 header.backup = priv->header.backup;
169 memcpy(header.guid, priv->header.guid, 16);
170 } else {
171 header.current = 1;
172 // backup gpt is in the last block
173 header.backup = priv->blocks - 1;
174 // generate a guid
175 zx_cprng_draw(header.guid, GPT_GUID_LEN);
176 }
177
178 // always write 128 entries in partition table
179 size_t ptable_size = PARTITIONS_COUNT * sizeof(gpt_partition_t);
180 gpt_partition_t* buf = static_cast<gpt_partition_t*>(malloc(ptable_size));
181 if (!buf) {
182 return -1;
183 }
184 memset(buf, 0, ptable_size);
185
186 // generate partition table
187 uint8_t* ptr = reinterpret_cast<uint8_t*>(buf);
188 int i;
189 gpt_partition_t** p;
190 for (i = 0, p = dev->partitions; i < PARTITIONS_COUNT && *p; i++, p++) {
191 memcpy(ptr, *p, GPT_ENTRY_SIZE);
192 ptr += GPT_ENTRY_SIZE;
193 }
194
195 // fill in partition table fields in header
196 header.entries = dev->valid ? priv->header.entries : 2;
197 header.entries_count = PARTITIONS_COUNT;
198 header.entries_size = GPT_ENTRY_SIZE;
199 header.entries_crc = crc32(0, reinterpret_cast<uint8_t*>(buf), ptable_size);
200
201 uint64_t ptable_blocks = ptable_size / priv->blocksize;
202 header.first = header.entries + ptable_blocks;
203 header.last = header.backup - ptable_blocks - 1;
204
205 // calculate header checksum
206 header.crc32 = crc32(0, (const unsigned char*)&header, GPT_HEADER_SIZE);
207
208 // the copy cached in priv is the primary copy
209 memcpy(&priv->header, &header, sizeof(header));
210
211 // the header copy on stack is now the backup copy...
212 header.current = priv->header.backup;
213 header.backup = priv->header.current;
214 header.entries = priv->header.last + 1;
215 header.crc32 = 0;
216 header.crc32 = crc32(0, (const unsigned char*)&header, GPT_HEADER_SIZE);
217
218 if (persist) {
219 // write backup to disk
220 rc = gpt_sync_current(priv->fd, priv->blocksize, &header, buf);
221 if (rc < 0) {
222 goto fail;
223 }
224
225 // write primary copy to disk
226 rc = gpt_sync_current(priv->fd, priv->blocksize, &priv->header, buf);
227 if (rc < 0) {
228 goto fail;
229 }
230 }
231
232 // align backup with new on-disk state
233 memcpy(priv->backup, priv->ptable, sizeof(priv->ptable));
234
235 dev->valid = true;
236
237 free(buf);
238 return 0;
239 fail:
240 free(buf);
241 return -1;
242 }
243
compare(const void * ls,const void * rs)244 int compare(const void* ls, const void* rs) {
245 const auto* l = *static_cast<gpt_partition_t* const*>(ls);
246 const auto* r = *static_cast<gpt_partition_t* const*>(rs);
247 if (l == NULL && r == NULL) {
248 return 0;
249 }
250
251 if (l == NULL) {
252 return 1;
253 }
254
255 if (r == NULL) {
256 return -1;
257 }
258
259 if (l->first == r->first) {
260 return 0;
261 }
262
263 return (l->first < r->first) ? -1 : 1;
264 }
265
266 } // namespace
267
268 __BEGIN_CDECLS
269
gpt_set_debug_output_enabled(bool enabled)270 void gpt_set_debug_output_enabled(bool enabled) {
271 debug_out = enabled;
272 }
273
cstring_to_utf16(uint16_t * dst,const char * src,size_t maxlen)274 void cstring_to_utf16(uint16_t* dst, const char* src, size_t maxlen) {
275 size_t len = strlen(src);
276 if (len > maxlen) len = maxlen;
277 for (size_t i = 0; i < len; i++) {
278 *dst++ = (uint16_t)(*src++ & 0x7f);
279 }
280 }
281
utf16_to_cstring(char * dst,const uint16_t * src,size_t len)282 char* utf16_to_cstring(char* dst, const uint16_t* src, size_t len) {
283 size_t i = 0;
284 char* ptr = dst;
285 while (i < len) {
286 char c = src[i++] & 0x7f;
287 if (!c) continue;
288 *ptr++ = c;
289 }
290 return dst;
291 }
292
print_table(const gpt_device_t * device)293 void print_table(const gpt_device_t* device) {
294 int count = 0;
295 for (; device->partitions[count] != NULL; ++count)
296 ;
297 print_array(device->partitions, count);
298 }
299
gpt_is_sys_guid(uint8_t * guid,ssize_t len)300 bool gpt_is_sys_guid(uint8_t* guid, ssize_t len) {
301 static const uint8_t sys_guid[GPT_GUID_LEN] = GUID_SYSTEM_VALUE;
302 return len == GPT_GUID_LEN && !memcmp(guid, sys_guid, GPT_GUID_LEN);
303 }
304
gpt_is_data_guid(uint8_t * guid,ssize_t len)305 bool gpt_is_data_guid(uint8_t* guid, ssize_t len) {
306 static const uint8_t data_guid[GPT_GUID_LEN] = GUID_DATA_VALUE;
307 return len == GPT_GUID_LEN && !memcmp(guid, data_guid, GPT_GUID_LEN);
308 }
309
gpt_is_install_guid(uint8_t * guid,ssize_t len)310 bool gpt_is_install_guid(uint8_t* guid, ssize_t len) {
311 static const uint8_t install_guid[GPT_GUID_LEN] = GUID_INSTALL_VALUE;
312 return len == GPT_GUID_LEN && !memcmp(guid, install_guid, GPT_GUID_LEN);
313 }
314
gpt_is_efi_guid(uint8_t * guid,ssize_t len)315 bool gpt_is_efi_guid(uint8_t* guid, ssize_t len) {
316 static const uint8_t efi_guid[GPT_GUID_LEN] = GUID_EFI_VALUE;
317 return len == GPT_GUID_LEN && !memcmp(guid, efi_guid, GPT_GUID_LEN);
318 }
319
gpt_get_diffs(const gpt_device_t * dev,int idx,unsigned * diffs)320 int gpt_get_diffs(const gpt_device_t* dev, int idx, unsigned* diffs) {
321 gpt_priv_t* priv = get_priv(dev);
322
323 *diffs = 0;
324
325 if (idx >= PARTITIONS_COUNT) {
326 return -1;
327 }
328
329 if (dev->partitions[idx] == NULL) {
330 return -1;
331 }
332
333 gpt_partition_t* a = dev->partitions[idx];
334 gpt_partition_t* b = priv->backup + idx;
335 if (memcmp(a->type, b->type, sizeof(a->type))) {
336 *diffs |= GPT_DIFF_TYPE;
337 }
338 if (memcmp(a->guid, b->guid, sizeof(a->guid))) {
339 *diffs |= GPT_DIFF_GUID;
340 }
341 if (a->first != b->first) {
342 *diffs |= GPT_DIFF_FIRST;
343 }
344 if (a->last != b->last) {
345 *diffs |= GPT_DIFF_LAST;
346 }
347 if (a->flags != b->flags) {
348 *diffs |= GPT_DIFF_FLAGS;
349 }
350 if (memcmp(a->name, b->name, sizeof(a->name))) {
351 *diffs |= GPT_DIFF_NAME;
352 }
353
354 return 0;
355 }
356
gpt_device_init(int fd,uint32_t blocksize,uint64_t blocks,gpt_device_t ** out_dev)357 int gpt_device_init(int fd, uint32_t blocksize, uint64_t blocks, gpt_device_t** out_dev) {
358 ssize_t ptable_size;
359 uint32_t saved_crc, crc;
360 gpt_partition_t* ptable;
361 gpt_header_t* header;
362 off_t rc;
363
364 gpt_priv_t* priv = static_cast<gpt_priv_t*>(calloc(1, sizeof(gpt_priv_t)));
365 if (!priv) return -1;
366
367 priv->fd = fd;
368 priv->blocksize = blocksize;
369 priv->blocks = blocks;
370
371 uint8_t block[blocksize];
372
373 if (priv->blocksize < 512) {
374 G_PRINTF("blocksize < 512 not supported\n");
375 goto fail;
376 }
377
378 // Read protective MBR.
379 rc = lseek(fd, 0, SEEK_SET);
380 if (rc < 0) {
381 goto fail;
382 }
383 rc = read(fd, block, blocksize);
384 if (rc < 0 || (uint64_t)rc != blocksize) {
385 goto fail;
386 }
387 priv->mbr = block[0x1fe] == 0x55 && block[0x1ff] == 0xaa;
388
389 // read the gpt header (lba 1)
390 rc = read(fd, block, blocksize);
391 if (rc < 0 || (uint64_t)rc != blocksize) {
392 goto fail;
393 }
394
395 header = &priv->header;
396 memcpy(header, block, sizeof(*header));
397
398 // is this a valid gpt header?
399 if (header->magic != GPT_MAGIC) {
400 G_PRINTF("invalid header magic!\n");
401 goto out; // ok to have an invalid header
402 }
403
404 // header checksum
405 saved_crc = header->crc32;
406 header->crc32 = 0;
407 crc = crc32(0, (const unsigned char*)header, header->size);
408 if (crc != saved_crc) {
409 G_PRINTF("header crc check failed\n");
410 goto out;
411 }
412
413 if (header->entries_count > PARTITIONS_COUNT) {
414 G_PRINTF("too many partitions!\n");
415 goto out;
416 }
417
418 priv->device.valid = true;
419
420 if (header->entries_count == 0) {
421 goto out;
422 }
423 if (header->entries_count > PARTITIONS_COUNT) {
424 G_PRINTF("too many partitions\n");
425 goto out;
426 }
427
428 ptable = priv->ptable;
429
430 // read the partition table
431 rc = lseek(fd, header->entries * blocksize, SEEK_SET);
432 if (rc < 0) {
433 goto fail;
434 }
435 ptable_size = header->entries_size * header->entries_count;
436 if ((size_t)ptable_size > SIZE_MAX) {
437 G_PRINTF("partition table too big\n");
438 goto out;
439 }
440 rc = read(fd, ptable, ptable_size);
441 if (rc != ptable_size) {
442 goto fail;
443 }
444
445 // partition table checksum
446 crc = crc32(0, (const unsigned char*)ptable, ptable_size);
447 if (crc != header->entries_crc) {
448 G_PRINTF("table crc check failed\n");
449 goto out;
450 }
451
452 // save original state so we can know what we changed
453 memcpy(priv->backup, priv->ptable, sizeof(priv->ptable));
454
455 // fill the table of valid partitions
456 for (unsigned i = 0; i < header->entries_count; i++) {
457 if (ptable[i].first == 0 && ptable[i].last == 0) continue;
458 priv->device.partitions[i] = &ptable[i];
459 }
460 out:
461 *out_dev = &priv->device;
462 return 0;
463 fail:
464 free(priv);
465 return -1;
466 }
467
gpt_device_release(gpt_device_t * dev)468 void gpt_device_release(gpt_device_t* dev) {
469 gpt_priv_t* priv = get_priv(dev);
470 free(priv);
471 }
472
gpt_device_finalize(gpt_device_t * dev)473 int gpt_device_finalize(gpt_device_t* dev) {
474 return gpt_device_finalize_and_sync(dev, false);
475 }
476
gpt_device_sync(gpt_device_t * dev)477 int gpt_device_sync(gpt_device_t* dev) {
478 return gpt_device_finalize_and_sync(dev, true);
479 }
480
gpt_device_range(const gpt_device_t * dev,uint64_t * block_start,uint64_t * block_end)481 int gpt_device_range(const gpt_device_t* dev, uint64_t* block_start, uint64_t* block_end) {
482 gpt_priv_t* priv = get_priv(dev);
483
484 if (!dev->valid) {
485 G_PRINTF("partition header invalid\n");
486 return -1;
487 }
488
489 // check range
490 *block_start = priv->header.first;
491 *block_end = priv->header.last;
492 return 0;
493 }
494
gpt_partition_add(gpt_device_t * dev,const char * name,const uint8_t * type,const uint8_t * guid,uint64_t offset,uint64_t blocks,uint64_t flags)495 int gpt_partition_add(gpt_device_t* dev, const char* name, const uint8_t* type,
496 const uint8_t* guid, uint64_t offset, uint64_t blocks,
497 uint64_t flags) {
498 gpt_priv_t* priv = get_priv(dev);
499
500 if (!dev->valid) {
501 G_PRINTF("partition header invalid, sync to generate a default header\n");
502 return -1;
503 }
504
505 if (blocks == 0) {
506 G_PRINTF("partition must be at least 1 block\n");
507 return -1;
508 }
509
510 uint64_t first = offset;
511 uint64_t last = first + blocks - 1;
512
513 // check range
514 if (last < first || first < priv->header.first || last > priv->header.last) {
515 G_PRINTF("partition must be in range of usable blocks[%" PRIu64 ", %" PRIu64 "]\n",
516 priv->header.first, priv->header.last);
517 return -1;
518 }
519
520 // check for overlap
521 int i;
522 int tail = -1;
523 for (i = 0; i < PARTITIONS_COUNT; i++) {
524 if (!dev->partitions[i]) {
525 tail = i;
526 break;
527 }
528 if (first <= dev->partitions[i]->last && last >= dev->partitions[i]->first) {
529 G_PRINTF("partition range overlaps\n");
530 return -1;
531 }
532 }
533 if (tail == -1) {
534 G_PRINTF("too many partitions\n");
535 return -1;
536 }
537
538 // find a free slot
539 gpt_partition_t* part = NULL;
540 for (i = 0; i < PARTITIONS_COUNT; i++) {
541 if (priv->ptable[i].first == 0 && priv->ptable[i].last == 0) {
542 part = &priv->ptable[i];
543 break;
544 }
545 }
546 assert(part);
547
548 // insert the new element into the list
549 partition_init(part, name, type, guid, first, last, flags);
550 dev->partitions[tail] = part;
551 return 0;
552 }
553
gpt_partition_clear(gpt_device_t * dev,uint64_t offset,uint64_t blocks)554 int gpt_partition_clear(gpt_device_t* dev, uint64_t offset, uint64_t blocks) {
555 gpt_priv_t* priv = get_priv(dev);
556
557 if (!dev->valid) {
558 G_PRINTF("partition header invalid, sync to generate a default header\n");
559 return -1;
560 }
561
562 if (blocks == 0) {
563 G_PRINTF("must clear at least 1 block\n");
564 return -1;
565 }
566 uint64_t first = offset;
567 uint64_t last = offset + blocks - 1;
568
569 if (last < first || first < priv->header.first || last > priv->header.last) {
570 G_PRINTF("must clear in the range of usable blocks[%" PRIu64 ", %" PRIu64 "]\n",
571 priv->header.first, priv->header.last);
572 return -1;
573 }
574
575 char zero[priv->blocksize];
576 memset(zero, 0, sizeof(zero));
577
578 for (size_t i = first; i <= last; i++) {
579 if (pwrite(priv->fd, zero, sizeof(zero), priv->blocksize * i) !=
580 (ssize_t)sizeof(zero)) {
581 G_PRINTF("Failed to write to block %zu; errno: %d\n", i, errno);
582 return -1;
583 }
584 }
585
586 return 0;
587 }
588
gpt_partition_remove(gpt_device_t * dev,const uint8_t * guid)589 int gpt_partition_remove(gpt_device_t* dev, const uint8_t* guid) {
590 // look for the entry in the partition list
591 int i;
592 for (i = 0; i < PARTITIONS_COUNT; i++) {
593 if (!memcmp(dev->partitions[i]->guid, guid, sizeof(dev->partitions[i]->guid))) {
594 break;
595 }
596 }
597 if (i == PARTITIONS_COUNT) {
598 G_PRINTF("partition not found\n");
599 return -1;
600 }
601 // clear the entry
602 memset(dev->partitions[i], 0, GPT_ENTRY_SIZE);
603 // pack the partition list
604 for (i = i + 1; i < PARTITIONS_COUNT; i++) {
605 if (dev->partitions[i] == NULL) {
606 dev->partitions[i - 1] = NULL;
607 } else {
608 dev->partitions[i - 1] = dev->partitions[i];
609 }
610 }
611 return 0;
612 }
613
gpt_partition_remove_all(gpt_device_t * dev)614 int gpt_partition_remove_all(gpt_device_t* dev) {
615 memset(dev->partitions, 0, sizeof(dev->partitions));
616 return 0;
617 }
618
uint8_to_guid_string(char * dst,const uint8_t * src)619 void uint8_to_guid_string(char* dst, const uint8_t* src) {
620 guid_t* guid = (guid_t*)src;
621 sprintf(dst, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", guid->data1, guid->data2, guid->data3, guid->data4[0], guid->data4[1], guid->data4[2], guid->data4[3], guid->data4[4], guid->data4[5], guid->data4[6], guid->data4[7]);
622 }
623
gpt_device_get_header_guid(const gpt_device_t * dev,uint8_t (* disk_guid_out)[GPT_GUID_LEN])624 void gpt_device_get_header_guid(const gpt_device_t* dev,
625 uint8_t (*disk_guid_out)[GPT_GUID_LEN]) {
626 gpt_header_t* header = &get_priv(dev)->header;
627 memcpy(disk_guid_out, header->guid, GPT_GUID_LEN);
628 }
629
gpt_device_read_gpt(int fd,gpt_device_t ** gpt_out)630 int gpt_device_read_gpt(int fd, gpt_device_t** gpt_out) {
631 block_info_t info;
632
633 ssize_t rc = ioctl_block_get_info(fd, &info);
634 if (rc < 0) {
635 return static_cast<int>(rc);
636 }
637
638 if (info.block_size < 1) {
639 return -1;
640 }
641
642 int result = gpt_device_init(fd, info.block_size, info.block_count,
643 gpt_out);
644
645 if (result < 0) {
646 return result;
647 } else if (!(*gpt_out)->valid) {
648 gpt_device_release(*gpt_out);
649 *gpt_out = NULL;
650 return -1;
651 }
652
653 return 0;
654 }
655
gpt_sort_partitions(gpt_partition_t ** base,size_t count)656 void gpt_sort_partitions(gpt_partition_t** base, size_t count) {
657 qsort(base, count, sizeof(gpt_partition_t*), compare);
658 }
659
gpt_guid_to_type(const char * guid)660 const char* gpt_guid_to_type(const char* guid) {
661 if (!strcmp("FE3A2A5D-4F32-41A7-B725-ACCC3285A309", guid)) {
662 return "cros kernel";
663 } else if (!strcmp("3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC", guid)) {
664 return "cros rootfs";
665 } else if (!strcmp("2E0A753D-9E48-43B0-8337-B15192CB1B5E", guid)) {
666 return "cros reserved";
667 } else if (!strcmp("CAB6E88E-ABF3-4102-A07A-D4BB9BE3C1D3", guid)) {
668 return "cros firmware";
669 } else if (!strcmp("C12A7328-F81F-11D2-BA4B-00A0C93EC93B", guid)) {
670 return "efi system";
671 } else if (!strcmp("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7", guid)) {
672 return "data";
673 } else if (!strcmp("21686148-6449-6E6F-744E-656564454649", guid)) {
674 return "bios";
675 } else if (!strcmp(GUID_SYSTEM_STRING, guid)) {
676 return "fuchsia-system";
677 } else if (!strcmp(GUID_DATA_STRING, guid)) {
678 return "fuchsia-data";
679 } else if (!strcmp(GUID_INSTALL_STRING, guid)) {
680 return "fuchsia-install";
681 } else if (!strcmp(GUID_BLOB_STRING, guid)) {
682 return "fuchsia-blob";
683 } else if (!strcmp(GUID_FVM_STRING, guid)) {
684 return "fuchsia-fvm";
685 } else if (!strcmp(GUID_ZIRCON_A_STRING, guid)) {
686 return "zircon-a";
687 } else if (!strcmp(GUID_ZIRCON_B_STRING, guid)) {
688 return "zircon-b";
689 } else if (!strcmp(GUID_ZIRCON_R_STRING, guid)) {
690 return "zircon-r";
691 } else if (!strcmp(GUID_SYS_CONFIG_STRING, guid)) {
692 return "sys-config";
693 } else if (!strcmp(GUID_FACTORY_CONFIG_STRING, guid)) {
694 return "factory";
695 } else if (!strcmp(GUID_BOOTLOADER_STRING, guid)) {
696 return "bootloader";
697 } else if (!strcmp(GUID_VBMETA_A_STRING, guid)) {
698 return "vbmeta_a";
699 } else if (!strcmp(GUID_VBMETA_B_STRING, guid)) {
700 return "vbmeta_b";
701 } else {
702 return "unknown";
703 }
704 }
705
706 __END_CDECLS
707 } // namespace libgpt
708