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