1 /* $OpenBSD: gpt.c,v 1.72 2022/04/20 23:36:30 krw Exp $ */ 2 /* 3 * Copyright (c) 2015 Markus Muller <mmu@grummel.net> 4 * Copyright (c) 2015 Kenneth R Westerback <krw@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> /* DEV_BSIZE */ 20 #include <sys/disklabel.h> 21 #include <sys/dkio.h> 22 #include <sys/ioctl.h> 23 24 #include <ctype.h> 25 #include <err.h> 26 #include <stdio.h> 27 #include <stdint.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <uuid.h> 31 32 #include "part.h" 33 #include "disk.h" 34 #include "mbr.h" 35 #include "misc.h" 36 #include "gpt.h" 37 38 #ifdef DEBUG 39 #define DPRINTF(x...) printf(x) 40 #else 41 #define DPRINTF(x...) 42 #endif 43 44 struct mbr gmbr; 45 struct gpt_header gh; 46 struct gpt_partition gp[NGPTPARTITIONS]; 47 48 struct gpt_partition **sort_gpt(void); 49 int lba_start_cmp(const void *e1, const void *e2); 50 int lba_free(uint64_t *, uint64_t *); 51 int add_partition(const uint8_t *, const char *, uint64_t); 52 int find_partition(const uint8_t *); 53 int get_header(const uint64_t); 54 int get_partition_table(void); 55 int init_gh(void); 56 int init_gp(const int); 57 uint32_t crc32(const u_char *, const uint32_t); 58 int protective_mbr(const struct mbr *); 59 int gpt_chk_mbr(struct dos_partition *, uint64_t); 60 void string_to_name(const unsigned int, const char *); 61 const char *name_to_string(const unsigned int); 62 63 void 64 string_to_name(const unsigned int pn, const char *ch) 65 { 66 unsigned int i; 67 68 memset(gp[pn].gp_name, 0, sizeof(gp[pn].gp_name)); 69 70 for (i = 0; i < sizeof(gp[pn].gp_name) && ch[i] != '\0'; i++) 71 gp[pn].gp_name[i] = htole16((unsigned int)ch[i]); 72 } 73 74 const char * 75 name_to_string(const unsigned int pn) 76 { 77 static char name[GPTPARTNAMESIZE + 1]; 78 unsigned int i; 79 80 memset(name, 0, sizeof(name)); 81 82 for (i = 0; i < sizeof(name) && gp[pn].gp_name[i] != 0; i++) 83 name[i] = letoh16(gp[pn].gp_name[i]) & 0x7F; 84 85 return name; 86 } 87 88 /* 89 * Return the index into dp[] of the EFI GPT (0xEE) partition, or -1 if no such 90 * partition exists. 91 * 92 * Taken from kern/subr_disk.c. 93 * 94 */ 95 int 96 gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize) 97 { 98 struct dos_partition *dp2; 99 int efi, eficnt, found, i; 100 uint32_t psize; 101 102 found = efi = eficnt = 0; 103 for (dp2 = dp, i = 0; i < NDOSPART; i++, dp2++) { 104 if (dp2->dp_typ == DOSPTYP_UNUSED) 105 continue; 106 found++; 107 if (dp2->dp_typ != DOSPTYP_EFI) 108 continue; 109 if (letoh32(dp2->dp_start) != GPTSECTOR) 110 continue; 111 psize = letoh32(dp2->dp_size); 112 if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX) { 113 efi = i; 114 eficnt++; 115 } 116 } 117 if (found == 1 && eficnt == 1) 118 return efi; 119 120 return -1; 121 } 122 123 int 124 protective_mbr(const struct mbr *mbr) 125 { 126 struct dos_partition dp[NDOSPART], dos_partition; 127 int i; 128 129 if (mbr->mbr_lba_self != 0) 130 return -1; 131 132 for (i = 0; i < NDOSPART; i++) { 133 PRT_make(&mbr->mbr_prt[i], mbr->mbr_lba_self, 134 mbr->mbr_lba_firstembr, &dos_partition); 135 memcpy(&dp[i], &dos_partition, sizeof(dp[i])); 136 } 137 138 return gpt_chk_mbr(dp, DL_GETDSIZE(&dl)); 139 } 140 141 int 142 get_header(const uint64_t sector) 143 { 144 struct gpt_header legh; 145 char *secbuf; 146 uint64_t gpbytes, gpsectors, lba_end; 147 148 secbuf = DISK_readsectors(sector, 1); 149 if (secbuf == NULL) 150 return -1; 151 152 memcpy(&legh, secbuf, sizeof(struct gpt_header)); 153 free(secbuf); 154 155 gh.gh_sig = letoh64(legh.gh_sig); 156 if (gh.gh_sig != GPTSIGNATURE) { 157 DPRINTF("gpt signature: expected 0x%llx, got 0x%llx\n", 158 GPTSIGNATURE, gh.gh_sig); 159 return -1; 160 } 161 162 gh.gh_rev = letoh32(legh.gh_rev); 163 if (gh.gh_rev != GPTREVISION) { 164 DPRINTF("gpt revision: expected 0x%x, got 0x%x\n", 165 GPTREVISION, gh.gh_rev); 166 return -1; 167 } 168 169 gh.gh_lba_self = letoh64(legh.gh_lba_self); 170 if (gh.gh_lba_self != sector) { 171 DPRINTF("gpt self lba: expected %llu, got %llu\n", 172 sector, gh.gh_lba_self); 173 return -1; 174 } 175 176 gh.gh_size = letoh32(legh.gh_size); 177 if (gh.gh_size != GPTMINHDRSIZE) { 178 DPRINTF("gpt header size: expected %u, got %u\n", 179 GPTMINHDRSIZE, gh.gh_size); 180 return -1; 181 } 182 183 gh.gh_part_size = letoh32(legh.gh_part_size); 184 if (gh.gh_part_size != GPTMINPARTSIZE) { 185 DPRINTF("gpt partition size: expected %u, got %u\n", 186 GPTMINPARTSIZE, gh.gh_part_size); 187 return -1; 188 } 189 190 if ((dl.d_secsize % gh.gh_part_size) != 0) { 191 DPRINTF("gpt sector size %% partition size (%u %% %u) != 0\n", 192 dl.d_secsize, gh.gh_part_size); 193 return -1; 194 } 195 196 gh.gh_part_num = letoh32(legh.gh_part_num); 197 if (gh.gh_part_num > NGPTPARTITIONS) { 198 DPRINTF("gpt partition count: expected <= %u, got %u\n", 199 NGPTPARTITIONS, gh.gh_part_num); 200 return -1; 201 } 202 203 gh.gh_csum = letoh32(legh.gh_csum); 204 legh.gh_csum = 0; 205 legh.gh_csum = crc32((unsigned char *)&legh, gh.gh_size); 206 if (legh.gh_csum != gh.gh_csum) { 207 DPRINTF("gpt header checksum: expected 0x%x, got 0x%x\n", 208 legh.gh_csum, gh.gh_csum); 209 /* Accept wrong-endian checksum. */ 210 if (swap32(legh.gh_csum) != gh.gh_csum) 211 return -1; 212 } 213 214 gpbytes = gh.gh_part_num * gh.gh_part_size; 215 gpsectors = (gpbytes + dl.d_secsize - 1) / dl.d_secsize; 216 lba_end = DL_GETDSIZE(&dl) - gpsectors - 2; 217 218 gh.gh_lba_end = letoh64(legh.gh_lba_end); 219 if (gh.gh_lba_end > lba_end) { 220 DPRINTF("gpt last usable LBA: reduced from %llu to %llu\n", 221 gh.gh_lba_end, lba_end); 222 gh.gh_lba_end = lba_end; 223 } 224 225 gh.gh_lba_start = letoh64(legh.gh_lba_start); 226 if (gh.gh_lba_start >= gh.gh_lba_end) { 227 DPRINTF("gpt first usable LBA: expected < %llu, got %llu\n", 228 gh.gh_lba_end, gh.gh_lba_start); 229 return -1; 230 } 231 232 gh.gh_part_lba = letoh64(legh.gh_part_lba); 233 if (gh.gh_lba_self == GPTSECTOR) { 234 if (gh.gh_part_lba <= GPTSECTOR) { 235 DPRINTF("gpt partition entries start: expected > %u, " 236 "got %llu\n", GPTSECTOR, gh.gh_part_lba); 237 return -1; 238 } 239 if (gh.gh_part_lba + gpsectors > gh.gh_lba_start) { 240 DPRINTF("gpt partition entries end: expected < %llu, " 241 "got %llu\n", gh.gh_lba_start, 242 gh.gh_part_lba + gpsectors); 243 return -1; 244 } 245 } else { 246 if (gh.gh_part_lba <= gh.gh_lba_end) { 247 DPRINTF("gpt partition entries start: expected > %llu, " 248 "got %llu\n", gh.gh_lba_end, gh.gh_part_lba); 249 return -1; 250 } 251 if (gh.gh_part_lba + gpsectors > gh.gh_lba_self) { 252 DPRINTF("gpt partition entries end: expected < %llu, " 253 "got %llu\n", gh.gh_lba_self, 254 gh.gh_part_lba + gpsectors); 255 return -1; 256 } 257 } 258 259 gh.gh_lba_alt = letoh32(legh.gh_lba_alt); 260 gh.gh_part_csum = letoh32(legh.gh_part_csum); 261 gh.gh_rsvd = letoh32(legh.gh_rsvd); /* Should always be 0. */ 262 uuid_dec_le(&legh.gh_guid, &gh.gh_guid); 263 264 return 0; 265 } 266 267 int 268 get_partition_table(void) 269 { 270 char *secbuf; 271 uint64_t gpbytes, gpsectors; 272 uint32_t gh_part_csum; 273 274 DPRINTF("gpt partition table being read from LBA %llu\n", 275 gh.gh_part_lba); 276 277 gpbytes = gh.gh_part_num * gh.gh_part_size; 278 gpsectors = (gpbytes + dl.d_secsize - 1) / dl.d_secsize; 279 memset(&gp, 0, sizeof(gp)); 280 281 secbuf = DISK_readsectors(gh.gh_part_lba, gpsectors); 282 if (secbuf == NULL) 283 return -1; 284 285 memcpy(&gp, secbuf, gpbytes); 286 free(secbuf); 287 288 gh_part_csum = gh.gh_part_csum; 289 gh.gh_part_csum = crc32((unsigned char *)&gp, gpbytes); 290 if (gh_part_csum != gh.gh_part_csum) { 291 DPRINTF("gpt partition table checksum: expected 0x%x, " 292 "got 0x%x\n", gh.gh_part_csum, gh_part_csum); 293 /* Accept wrong-endian checksum. */ 294 if (swap32(gh_part_csum) != gh.gh_part_csum) 295 return -1; 296 } 297 298 return 0; 299 } 300 301 int 302 GPT_read(const int which) 303 { 304 int error; 305 306 error = MBR_read(0, 0, &gmbr); 307 if (error == 0) 308 error = protective_mbr(&gmbr); 309 if (error) 310 goto done; 311 312 switch (which) { 313 case PRIMARYGPT: 314 error = get_header(GPTSECTOR); 315 break; 316 case SECONDARYGPT: 317 error = get_header(DL_GETDSIZE(&dl) - 1); 318 break; 319 case ANYGPT: 320 error = get_header(GPTSECTOR); 321 if (error != 0 || get_partition_table() != 0) 322 error = get_header(DL_GETDSIZE(&dl) - 1); 323 break; 324 default: 325 return -1; 326 } 327 328 if (error == 0) 329 error = get_partition_table(); 330 331 done: 332 if (error != 0) { 333 /* No valid GPT found. Zap any artifacts. */ 334 memset(&gmbr, 0, sizeof(gmbr)); 335 memset(&gh, 0, sizeof(gh)); 336 memset(&gp, 0, sizeof(gp)); 337 } 338 339 return error; 340 } 341 342 void 343 GPT_print(const char *units, const int verbosity) 344 { 345 const struct unit_type *ut; 346 const int secsize = dl.d_secsize; 347 char *guidstr = NULL; 348 double size; 349 int i; 350 uint32_t status; 351 352 #ifdef DEBUG 353 char *p; 354 uint64_t sig; 355 356 sig = htole64(gh.gh_sig); 357 p = (char *)&sig; 358 359 printf("gh_sig : "); 360 for (i = 0; i < sizeof(sig); i++) 361 printf("%c", isprint((unsigned char)p[i]) ? p[i] : '?'); 362 printf(" ("); 363 for (i = 0; i < sizeof(sig); i++) { 364 printf("%02x", p[i]); 365 if ((i + 1) < sizeof(sig)) 366 printf(":"); 367 } 368 printf(")\n"); 369 printf("gh_rev : %u\n", gh.gh_rev); 370 printf("gh_size : %u (%zd)\n", gh.gh_size, sizeof(gh)); 371 printf("gh_csum : 0x%x\n", gh.gh_csum); 372 printf("gh_rsvd : %u\n", gh.gh_rsvd); 373 printf("gh_lba_self : %llu\n", gh.gh_lba_self); 374 printf("gh_lba_alt : %llu\n", gh.gh_lba_alt); 375 printf("gh_lba_start : %llu\n", gh.gh_lba_start); 376 printf("gh_lba_end : %llu\n", gh.gh_lba_end); 377 p = NULL; 378 uuid_to_string(&gh.gh_guid, &p, &status); 379 printf("gh_gh_guid : %s\n", (status == uuid_s_ok) ? p : "<invalid>"); 380 free(p); 381 printf("gh_gh_part_lba : %llu\n", gh.gh_part_lba); 382 printf("gh_gh_part_num : %u (%zu)\n", gh.gh_part_num, nitems(gp)); 383 printf("gh_gh_part_size: %u (%zu)\n", gh.gh_part_size, sizeof(gp[0])); 384 printf("gh_gh_part_csum: 0x%x\n", gh.gh_part_csum); 385 printf("\n"); 386 #endif /* DEBUG */ 387 388 size = units_size(units, DL_GETDSIZE(&dl), &ut); 389 printf("Disk: %s Usable LBA: %llu to %llu [%.0f ", 390 disk.dk_name, gh.gh_lba_start, gh.gh_lba_end, size); 391 if (ut->ut_conversion == 0 && secsize != DEV_BSIZE) 392 printf("%d-byte ", secsize); 393 printf("%s]\n", ut->ut_lname); 394 395 if (verbosity == VERBOSE) { 396 printf("GUID: "); 397 uuid_to_string(&gh.gh_guid, &guidstr, &status); 398 if (status == uuid_s_ok) 399 printf("%s\n", guidstr); 400 else 401 printf("<invalid header GUID>\n"); 402 free(guidstr); 403 } 404 405 GPT_print_parthdr(verbosity); 406 for (i = 0; i < gh.gh_part_num; i++) { 407 if (uuid_is_nil(&gp[i].gp_type, NULL)) 408 continue; 409 GPT_print_part(i, units, verbosity); 410 } 411 } 412 413 void 414 GPT_print_parthdr(const int verbosity) 415 { 416 printf(" #: type " 417 " [ start: size ]\n"); 418 if (verbosity == VERBOSE) 419 printf(" guid name\n"); 420 printf("--------------------------------------------------------" 421 "----------------\n"); 422 } 423 424 void 425 GPT_print_part(const unsigned int pn, const char *units, const int verbosity) 426 { 427 const struct unit_type *ut; 428 struct uuid guid; 429 char *guidstr = NULL; 430 double size; 431 uint64_t sectors; 432 uint32_t status; 433 434 uuid_dec_le(&gp[pn].gp_type, &guid); 435 sectors = letoh64(gp[pn].gp_lba_end) - letoh64(gp[pn].gp_lba_start) + 1; 436 size = units_size(units, sectors, &ut); 437 printf("%c%3u: %-36s [%12lld: %12.0f%s]\n", 438 (letoh64(gp[pn].gp_attrs) & GPTDOSACTIVE) ? '*' : ' ', pn, 439 PRT_uuid_to_typename(&guid), letoh64(gp[pn].gp_lba_start), 440 size, ut->ut_abbr); 441 442 if (verbosity == VERBOSE) { 443 uuid_dec_le(&gp[pn].gp_guid, &guid); 444 uuid_to_string(&guid, &guidstr, &status); 445 if (status != uuid_s_ok) 446 printf(" <invalid partition guid> "); 447 else 448 printf(" %-36s ", guidstr); 449 printf("%-36s\n", name_to_string(pn)); 450 free(guidstr); 451 } 452 } 453 454 int 455 find_partition(const uint8_t *beuuid) 456 { 457 struct uuid uuid, gp_type; 458 unsigned int pn; 459 460 uuid_dec_be(beuuid, &uuid); 461 uuid_enc_le(&gp_type, &uuid); 462 463 for (pn = 0; pn < gh.gh_part_num; pn++) { 464 if (uuid_compare(&gp[pn].gp_type, &gp_type, NULL) == 0) 465 return pn; 466 } 467 return -1; 468 } 469 470 int 471 add_partition(const uint8_t *beuuid, const char *name, uint64_t sectors) 472 { 473 struct uuid uuid, gp_type; 474 int rslt; 475 uint64_t end, freesectors, gpbytes, start; 476 uint32_t status, pn; 477 478 uuid_dec_be(beuuid, &uuid); 479 uuid_enc_le(&gp_type, &uuid); 480 481 for (pn = 0; pn < gh.gh_part_num; pn++) { 482 if (uuid_is_nil(&gp[pn].gp_type, NULL)) 483 break; 484 } 485 if (pn == gh.gh_part_num) 486 goto done; 487 488 rslt = lba_free(&start, &end); 489 if (rslt == -1) 490 goto done; 491 492 if (start % BLOCKALIGNMENT) 493 start += (BLOCKALIGNMENT - start % BLOCKALIGNMENT); 494 if (start >= end) 495 goto done; 496 497 freesectors = end - start + 1; 498 499 if (sectors == 0) 500 sectors = freesectors; 501 502 if (freesectors < sectors) 503 goto done; 504 else if (freesectors > sectors) 505 end = start + sectors - 1; 506 507 gp[pn].gp_type = gp_type; 508 gp[pn].gp_lba_start = htole64(start); 509 gp[pn].gp_lba_end = htole64(end); 510 string_to_name(pn, name); 511 512 uuid_create(&uuid, &status); 513 if (status != uuid_s_ok) 514 goto done; 515 516 uuid_enc_le(&gp[pn].gp_guid, &uuid); 517 gpbytes = gh.gh_part_num * gh.gh_part_size; 518 gh.gh_part_csum = crc32((unsigned char *)&gp, gpbytes); 519 gh.gh_csum = 0; 520 gh.gh_csum = crc32((unsigned char *)&gh, gh.gh_size); 521 522 return 0; 523 524 done: 525 if (pn != gh.gh_part_num) 526 memset(&gp[pn], 0, sizeof(gp[pn])); 527 printf("unable to add %s\n", name); 528 return -1; 529 } 530 531 int 532 init_gh(void) 533 { 534 struct gpt_header oldgh; 535 const int secsize = dl.d_secsize; 536 int needed; 537 uint32_t status; 538 539 memcpy(&oldgh, &gh, sizeof(oldgh)); 540 memset(&gh, 0, sizeof(gh)); 541 memset(&gmbr, 0, sizeof(gmbr)); 542 543 /* XXX Do we need the boot code? UEFI spec & Apple says no. */ 544 memcpy(gmbr.mbr_code, default_dmbr.dmbr_boot, sizeof(gmbr.mbr_code)); 545 gmbr.mbr_prt[0].prt_id = DOSPTYP_EFI; 546 gmbr.mbr_prt[0].prt_bs = 1; 547 gmbr.mbr_prt[0].prt_ns = UINT32_MAX; 548 gmbr.mbr_signature = DOSMBR_SIGNATURE; 549 550 needed = sizeof(gp) / secsize + 2; 551 552 if (needed % BLOCKALIGNMENT) 553 needed += (needed - (needed % BLOCKALIGNMENT)); 554 555 gh.gh_sig = GPTSIGNATURE; 556 gh.gh_rev = GPTREVISION; 557 gh.gh_size = GPTMINHDRSIZE; 558 gh.gh_csum = 0; 559 gh.gh_rsvd = 0; 560 gh.gh_lba_self = 1; 561 gh.gh_lba_alt = DL_GETDSIZE(&dl) - 1; 562 gh.gh_lba_start = needed; 563 gh.gh_lba_end = DL_GETDSIZE(&dl) - needed; 564 uuid_create(&gh.gh_guid, &status); 565 if (status != uuid_s_ok) { 566 memcpy(&gh, &oldgh, sizeof(gh)); 567 return -1; 568 } 569 gh.gh_part_lba = 2; 570 gh.gh_part_num = NGPTPARTITIONS; 571 gh.gh_part_size = GPTMINPARTSIZE; 572 gh.gh_part_csum = 0; 573 574 return 0; 575 } 576 577 int 578 init_gp(const int how) 579 { 580 struct gpt_partition oldgp[NGPTPARTITIONS]; 581 const uint8_t gpt_uuid_efi_system[] = GPT_UUID_EFI_SYSTEM; 582 const uint8_t gpt_uuid_openbsd[] = GPT_UUID_OPENBSD; 583 uint64_t prt_ns; 584 int pn, rslt; 585 586 memcpy(&oldgp, &gp, sizeof(oldgp)); 587 if (how == GHANDGP) 588 memset(&gp, 0, sizeof(gp)); 589 else { 590 for (pn = 0; pn < gh.gh_part_num; pn++) { 591 if (PRT_protected_guid(&gp[pn].gp_type)) 592 continue; 593 memset(&gp[pn], 0, sizeof(gp[pn])); 594 } 595 } 596 597 rslt = 0; 598 if (disk.dk_bootprt.prt_ns > 0) { 599 pn = find_partition(gpt_uuid_efi_system); 600 if (pn == -1) { 601 rslt = add_partition(gpt_uuid_efi_system, 602 "EFI System Area", disk.dk_bootprt.prt_ns); 603 } else { 604 prt_ns = gp[pn].gp_lba_end - gp[pn].gp_lba_start + 1; 605 if (prt_ns < disk.dk_bootprt.prt_ns) { 606 printf("EFI System Area < %llu sectors\n", 607 disk.dk_bootprt.prt_ns); 608 rslt = -1; 609 } 610 } 611 } 612 if (rslt == 0) 613 rslt = add_partition(gpt_uuid_openbsd, "OpenBSD Area", 0); 614 615 if (rslt != 0) 616 memcpy(&gp, &oldgp, sizeof(gp)); 617 618 return rslt; 619 } 620 621 int 622 GPT_init(const int how) 623 { 624 int rslt = 0; 625 626 if (how == GHANDGP) 627 rslt = init_gh(); 628 if (rslt == 0) 629 rslt = init_gp(how); 630 631 return rslt; 632 } 633 634 void 635 GPT_zap_headers(void) 636 { 637 char *secbuf; 638 uint64_t sig; 639 640 secbuf = DISK_readsectors(GPTSECTOR, 1); 641 if (secbuf == NULL) 642 return; 643 644 memcpy(&sig, secbuf, sizeof(sig)); 645 if (letoh64(sig) == GPTSIGNATURE) { 646 memset(secbuf, 0, dl.d_secsize); 647 if (DISK_writesectors(secbuf, GPTSECTOR, 1)) 648 DPRINTF("Unable to zap GPT header @ sector %d", 649 GPTSECTOR); 650 } 651 free(secbuf); 652 653 secbuf = DISK_readsectors(DL_GETDSIZE(&dl) - 1, 1); 654 if (secbuf == NULL) 655 return; 656 657 memcpy(&sig, secbuf, sizeof(sig)); 658 if (letoh64(sig) == GPTSIGNATURE) { 659 memset(secbuf, 0, dl.d_secsize); 660 if (DISK_writesectors(secbuf, DL_GETDSIZE(&dl) - 1, 1)) 661 DPRINTF("Unable to zap GPT header @ sector %llu", 662 DL_GETDSIZE(&dl) - 1); 663 } 664 free(secbuf); 665 } 666 667 int 668 GPT_write(void) 669 { 670 struct gpt_header legh; 671 char *secbuf; 672 uint64_t altgh, altgp; 673 uint64_t gpbytes, gpsectors; 674 int rslt; 675 676 if (MBR_write(&gmbr)) 677 return -1; 678 679 gpbytes = gh.gh_part_num * gh.gh_part_size; 680 gpsectors = (gpbytes + dl.d_secsize - 1) / dl.d_secsize; 681 682 altgh = DL_GETDSIZE(&dl) - 1; 683 altgp = altgh - gpsectors; 684 685 secbuf = DISK_readsectors(GPTSECTOR, 1); 686 if (secbuf == NULL) 687 return -1; 688 689 legh.gh_sig = htole64(GPTSIGNATURE); 690 legh.gh_rev = htole32(GPTREVISION); 691 legh.gh_size = htole32(GPTMINHDRSIZE); 692 legh.gh_csum = 0; 693 legh.gh_rsvd = 0; 694 legh.gh_lba_self = htole64(GPTSECTOR); 695 legh.gh_lba_alt = htole64(altgh); 696 legh.gh_lba_start = htole64(gh.gh_lba_start); 697 legh.gh_lba_end = htole64(gh.gh_lba_end); 698 uuid_enc_le(&legh.gh_guid, &gh.gh_guid); 699 legh.gh_part_lba = htole64(GPTSECTOR + 1); 700 legh.gh_part_num = htole32(gh.gh_part_num); 701 legh.gh_part_size = htole32(GPTMINPARTSIZE); 702 legh.gh_part_csum = htole32(crc32((unsigned char *)&gp, gpbytes)); 703 704 legh.gh_csum = htole32(crc32((unsigned char *)&legh, gh.gh_size)); 705 memcpy(secbuf, &legh, sizeof(legh)); 706 rslt = DISK_writesectors(secbuf, GPTSECTOR, 1); 707 free(secbuf); 708 if (rslt) 709 return -1; 710 711 legh.gh_lba_self = htole64(altgh); 712 legh.gh_lba_alt = htole64(GPTSECTOR); 713 legh.gh_part_lba = htole64(altgp); 714 legh.gh_csum = 0; 715 legh.gh_csum = htole32(crc32((unsigned char *)&legh, gh.gh_size)); 716 717 secbuf = DISK_readsectors(altgh, 1); 718 if (secbuf == NULL) 719 return -1; 720 721 memcpy(secbuf, &legh, gh.gh_size); 722 rslt = DISK_writesectors(secbuf, altgh, 1); 723 free(secbuf); 724 if (rslt) 725 return -1; 726 727 if (DISK_writesectors((const char *)&gp, GPTSECTOR + 1, gpsectors)) 728 return -1; 729 if (DISK_writesectors((const char *)&gp, altgp, gpsectors)) 730 return -1; 731 732 /* Refresh in-kernel disklabel from the updated disk information. */ 733 if (ioctl(disk.dk_fd, DIOCRLDINFO, 0) == -1) 734 warn("DIOCRLDINFO"); 735 736 return 0; 737 } 738 739 int 740 gp_lba_start_cmp(const void *e1, const void *e2) 741 { 742 struct gpt_partition *p1 = *(struct gpt_partition **)e1; 743 struct gpt_partition *p2 = *(struct gpt_partition **)e2; 744 uint64_t o1; 745 uint64_t o2; 746 747 o1 = letoh64(p1->gp_lba_start); 748 o2 = letoh64(p2->gp_lba_start); 749 750 if (o1 < o2) 751 return -1; 752 else if (o1 > o2) 753 return 1; 754 else 755 return 0; 756 } 757 758 struct gpt_partition ** 759 sort_gpt(void) 760 { 761 static struct gpt_partition *sgp[NGPTPARTITIONS+2]; 762 unsigned int i, j; 763 764 memset(sgp, 0, sizeof(sgp)); 765 766 j = 0; 767 for (i = 0; i < gh.gh_part_num; i++) { 768 if (letoh64(gp[i].gp_lba_start) >= gh.gh_lba_start) 769 sgp[j++] = &gp[i]; 770 } 771 772 if (j > 1) { 773 if (mergesort(sgp, j, sizeof(sgp[0]), gp_lba_start_cmp) == -1) { 774 printf("unable to sort gpt by lba start\n"); 775 return NULL; 776 } 777 } 778 779 return sgp; 780 } 781 782 int 783 lba_free(uint64_t *start, uint64_t *end) 784 { 785 struct gpt_partition **sgp; 786 uint64_t bs, bigbs, nextbs, ns; 787 unsigned int i; 788 789 sgp = sort_gpt(); 790 if (sgp == NULL) 791 return -1; 792 793 bs = gh.gh_lba_start; 794 ns = gh.gh_lba_end - bs + 1; 795 796 if (sgp[0] != NULL) { 797 bigbs = bs; 798 ns = 0; 799 for (i = 0; sgp[i] != NULL; i++) { 800 nextbs = letoh64(sgp[i]->gp_lba_start); 801 if (bs < nextbs && ns < nextbs - bs) { 802 ns = nextbs - bs; 803 bigbs = bs; 804 } 805 bs = letoh64(sgp[i]->gp_lba_end) + 1; 806 } 807 nextbs = gh.gh_lba_end + 1; 808 if (bs < nextbs && ns < nextbs - bs) { 809 ns = nextbs - bs; 810 bigbs = bs; 811 } 812 bs = bigbs; 813 } 814 815 if (ns == 0) 816 return -1; 817 818 if (start != NULL) 819 *start = bs; 820 if (end != NULL) 821 *end = bs + ns - 1; 822 823 return 0; 824 } 825 826 int 827 GPT_get_lba_start(const unsigned int pn) 828 { 829 uint64_t bs; 830 unsigned int i; 831 int rslt; 832 833 bs = gh.gh_lba_start; 834 835 if (letoh64(gp[pn].gp_lba_start) >= bs) { 836 bs = letoh64(gp[pn].gp_lba_start); 837 } else { 838 rslt = lba_free(&bs, NULL); 839 if (rslt == -1) { 840 printf("no space for partition %u\n", pn); 841 return -1; 842 } 843 } 844 845 bs = getuint64("Partition offset", bs, gh.gh_lba_start, gh.gh_lba_end); 846 for (i = 0; i < gh.gh_part_num; i++) { 847 if (i == pn) 848 continue; 849 if (bs >= letoh64(gp[i].gp_lba_start) && 850 bs <= letoh64(gp[i].gp_lba_end)) { 851 printf("partition %u can't start inside partition %u\n", 852 pn, i); 853 return -1; 854 } 855 } 856 857 gp[pn].gp_lba_start = htole64(bs); 858 859 return 0; 860 } 861 862 int 863 GPT_get_lba_end(const unsigned int pn) 864 { 865 struct gpt_partition **sgp; 866 uint64_t bs, nextbs, ns; 867 unsigned int i; 868 869 sgp = sort_gpt(); 870 if (sgp == NULL) 871 return -1; 872 873 bs = letoh64(gp[pn].gp_lba_start); 874 ns = gh.gh_lba_end - bs + 1; 875 for (i = 0; sgp[i] != NULL; i++) { 876 nextbs = letoh64(sgp[i]->gp_lba_start); 877 if (nextbs > bs) { 878 ns = nextbs - bs; 879 break; 880 } 881 } 882 ns = getuint64("Partition size", ns, 1, ns); 883 884 gp[pn].gp_lba_end = htole64(bs + ns - 1); 885 886 return 0; 887 } 888 889 int 890 GPT_get_name(const unsigned int pn) 891 { 892 char name[GPTPARTNAMESIZE + 1]; 893 894 printf("Partition name: [%s] ", name_to_string(pn)); 895 string_from_line(name, sizeof(name), UNTRIMMED); 896 897 switch (strlen(name)) { 898 case 0: 899 break; 900 case GPTPARTNAMESIZE: 901 printf("partition name must be < %d characters\n", 902 GPTPARTNAMESIZE); 903 return -1; 904 default: 905 string_to_name(pn, name); 906 break; 907 } 908 909 return 0; 910 } 911 912 /* 913 * Adapted from Hacker's Delight crc32b(). 914 * 915 * To quote http://www.hackersdelight.org/permissions.htm : 916 * 917 * "You are free to use, copy, and distribute any of the code on 918 * this web site, whether modified by you or not. You need not give 919 * attribution. This includes the algorithms (some of which appear 920 * in Hacker's Delight), the Hacker's Assistant, and any code submitted 921 * by readers. Submitters implicitly agree to this." 922 */ 923 uint32_t 924 crc32(const u_char *buf, const uint32_t size) 925 { 926 int j; 927 uint32_t i, byte, crc, mask; 928 929 crc = 0xFFFFFFFF; 930 931 for (i = 0; i < size; i++) { 932 byte = buf[i]; /* Get next byte. */ 933 crc = crc ^ byte; 934 for (j = 7; j >= 0; j--) { /* Do eight times. */ 935 mask = -(crc & 1); 936 crc = (crc >> 1) ^ (0xEDB88320 & mask); 937 } 938 } 939 940 return ~crc; 941 } 942