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