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