1 /* $OpenBSD: gpt.c,v 1.12 2021/01/30 18:16:36 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 <errno.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <unistd.h> 29 #include <uuid.h> 30 31 #include "disk.h" 32 #include "misc.h" 33 #include "part.h" 34 #include "gpt.h" 35 36 #ifdef DEBUG 37 #define DPRINTF(x...) printf(x) 38 #else 39 #define DPRINTF(x...) 40 #endif 41 42 struct gpt_header gh; 43 struct gpt_partition gp[NGPTPARTITIONS]; 44 45 struct gpt_partition **sort_gpt(void); 46 int lba_start_cmp(const void *e1, const void *e2); 47 48 int 49 GPT_get_header(off_t where) 50 { 51 char *secbuf; 52 uint64_t partlastlba; 53 int partspersec; 54 uint32_t orig_gh_csum, new_gh_csum; 55 56 secbuf = DISK_readsector(where); 57 if (secbuf == 0) 58 return (1); 59 60 memcpy(&gh, secbuf, sizeof(struct gpt_header)); 61 free(secbuf); 62 63 if (letoh64(gh.gh_sig) != GPTSIGNATURE) { 64 DPRINTF("gpt signature: expected 0x%llx, got 0x%llx\n", 65 GPTSIGNATURE, letoh64(gh.gh_sig)); 66 return (1); 67 } 68 69 if (letoh32(gh.gh_rev) != GPTREVISION) { 70 DPRINTF("gpt revision: expected 0x%x, got 0x%x\n", 71 GPTREVISION, letoh32(gh.gh_rev)); 72 return (1); 73 } 74 75 if (letoh64(gh.gh_lba_self) != where) { 76 DPRINTF("gpt self lba: expected %lld, got %llu\n", 77 (long long)where, letoh64(gh.gh_lba_self)); 78 return (1); 79 } 80 81 if (letoh32(gh.gh_size) != GPTMINHDRSIZE) { 82 DPRINTF("gpt header size: expected %u, got %u\n", 83 GPTMINHDRSIZE, letoh32(gh.gh_size)); 84 return (1); 85 } 86 87 if (letoh32(gh.gh_part_size) != GPTMINPARTSIZE) { 88 DPRINTF("gpt partition size: expected %u, got %u\n", 89 GPTMINPARTSIZE, letoh32(gh.gh_part_size)); 90 return (1); 91 } 92 93 if (letoh32(gh.gh_part_num) > NGPTPARTITIONS) { 94 DPRINTF("gpt partition count: expected <= %u, got %u\n", 95 NGPTPARTITIONS, letoh32(gh.gh_part_num)); 96 return (1); 97 } 98 99 orig_gh_csum = gh.gh_csum; 100 gh.gh_csum = 0; 101 new_gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size)); 102 gh.gh_csum = orig_gh_csum; 103 if (letoh32(orig_gh_csum) != new_gh_csum) { 104 DPRINTF("gpt header checksum: expected 0x%x, got 0x%x\n", 105 orig_gh_csum, new_gh_csum); 106 return (1); 107 } 108 109 if (letoh64(gh.gh_lba_end) >= DL_GETDSIZE(&dl)) { 110 DPRINTF("gpt last usable LBA: expected < %lld, got %llu\n", 111 DL_GETDSIZE(&dl), letoh64(gh.gh_lba_end)); 112 return (1); 113 } 114 115 if (letoh64(gh.gh_lba_start) >= letoh64(gh.gh_lba_end)) { 116 DPRINTF("gpt first usable LBA: expected < %llu, got %llu\n", 117 letoh64(gh.gh_lba_start), letoh64(gh.gh_lba_start)); 118 return (1); 119 } 120 121 if (letoh64(gh.gh_part_lba) <= letoh64(gh.gh_lba_end) && 122 letoh64(gh.gh_part_lba) >= letoh64(gh.gh_lba_start)) { 123 DPRINTF("gpt partition table start lba: expected < %llu or " 124 "> %llu, got %llu\n", letoh64(gh.gh_lba_start), 125 letoh64(gh.gh_lba_end), letoh64(gh.gh_part_lba)); 126 return (1); 127 } 128 129 partspersec = dl.d_secsize / letoh32(gh.gh_part_size); 130 partlastlba = letoh64(gh.gh_part_lba) + 131 ((letoh32(gh.gh_part_num) + partspersec - 1) / partspersec) - 1; 132 if (partlastlba <= letoh64(gh.gh_lba_end) && 133 partlastlba >= letoh64(gh.gh_lba_start)) { 134 DPRINTF("gpt partition table last LBA: expected < %llu or " 135 "> %llu, got %llu\n", letoh64(gh.gh_lba_start), 136 letoh64(gh.gh_lba_end), partlastlba); 137 return (1); 138 } 139 140 /* 141 * Other possible paranoia checks: 142 * 1) partition table starts before primary gpt lba. 143 * 2) partition table extends into lowest partition. 144 * 3) alt partition table starts before gh_lba_end. 145 */ 146 return (0); 147 } 148 149 int 150 GPT_get_partition_table(off_t where) 151 { 152 ssize_t len; 153 off_t off; 154 int secs; 155 uint32_t checksum, partspersec; 156 157 DPRINTF("gpt partition table being read from LBA %llu\n", 158 letoh64(gh.gh_part_lba)); 159 160 partspersec = dl.d_secsize / letoh32(gh.gh_part_size); 161 if (partspersec * letoh32(gh.gh_part_size) != dl.d_secsize) { 162 DPRINTF("gpt partition table entry invalid size. %u\n", 163 letoh32(gh.gh_part_size)); 164 return (1); 165 } 166 secs = (letoh32(gh.gh_part_num) + partspersec - 1) / partspersec; 167 168 memset(&gp, 0, sizeof(gp)); 169 170 where *= dl.d_secsize; 171 off = lseek(disk.fd, where, SEEK_SET); 172 if (off == -1) { 173 DPRINTF("seek to gpt partition table @ sector %llu failed\n", 174 (unsigned long long)where / dl.d_secsize); 175 return (1); 176 } 177 len = read(disk.fd, &gp, secs * dl.d_secsize); 178 if (len == -1 || len != secs * dl.d_secsize) { 179 DPRINTF("gpt partition table read failed.\n"); 180 return (1); 181 } 182 183 checksum = crc32((unsigned char *)&gp, letoh32(gh.gh_part_num) * 184 letoh32(gh.gh_part_size)); 185 if (checksum != letoh32(gh.gh_part_csum)) { 186 DPRINTF("gpt partition table checksum: expected %x, got %x\n", 187 checksum, letoh32(gh.gh_part_csum)); 188 return (1); 189 } 190 191 return (0); 192 } 193 194 void 195 GPT_get_gpt(int which) 196 { 197 int privalid, altvalid; 198 199 /* 200 * primary header && primary partition table || 201 * alt header && alt partition table 202 */ 203 privalid = GPT_get_header(GPTSECTOR); 204 if (privalid == 0) 205 privalid = GPT_get_partition_table(gh.gh_part_lba); 206 if (which == 1 || (which == 0 && privalid == 0)) 207 return; 208 209 /* No valid GPT found. Zap any artifacts. */ 210 memset(&gh, 0, sizeof(gh)); 211 memset(&gp, 0, sizeof(gp)); 212 213 altvalid = GPT_get_header(DL_GETDSIZE(&dl) - 1); 214 if (altvalid == 0) 215 altvalid = GPT_get_partition_table(gh.gh_part_lba); 216 if (which == 2 || altvalid == 0) 217 return; 218 219 /* No valid GPT found. Zap any artifacts. */ 220 memset(&gh, 0, sizeof(gh)); 221 memset(&gp, 0, sizeof(gp)); 222 } 223 224 void 225 GPT_print(char *units, int verbosity) 226 { 227 const int secsize = unit_types[SECTORS].conversion; 228 struct uuid guid; 229 char *guidstr = NULL; 230 double size; 231 int i, u, status; 232 233 u = unit_lookup(units); 234 size = ((double)DL_GETDSIZE(&dl) * secsize) / unit_types[u].conversion; 235 printf("Disk: %s Usable LBA: %llu to %llu [%.0f ", 236 disk.name, letoh64(gh.gh_lba_start), letoh64(gh.gh_lba_end), size); 237 238 if (u == SECTORS && secsize != DEV_BSIZE) 239 printf("%d-byte ", secsize); 240 printf("%s]\n", unit_types[u].lname); 241 242 if (verbosity) { 243 printf("GUID: "); 244 uuid_dec_le(&gh.gh_guid, &guid); 245 uuid_to_string(&guid, &guidstr, &status); 246 if (status == uuid_s_ok) 247 printf("%s\n", guidstr); 248 else 249 printf("<invalid header GUID>\n"); 250 free(guidstr); 251 } 252 253 GPT_print_parthdr(verbosity); 254 for (i = 0; i < letoh32(gh.gh_part_num); i++) { 255 if (uuid_is_nil(&gp[i].gp_type, NULL)) 256 continue; 257 GPT_print_part(i, units, verbosity); 258 } 259 260 } 261 262 void 263 GPT_print_parthdr(int verbosity) 264 { 265 printf(" #: type " 266 " [ start: size ]\n"); 267 if (verbosity) 268 printf(" guid name\n"); 269 printf("--------------------------------------------------------" 270 "----------------\n"); 271 } 272 273 void 274 GPT_print_part(int n, char *units, int verbosity) 275 { 276 struct uuid guid; 277 const int secsize = unit_types[SECTORS].conversion; 278 struct gpt_partition *partn = &gp[n]; 279 char *guidstr = NULL; 280 double size; 281 int u, status; 282 283 uuid_dec_le(&partn->gp_type, &guid); 284 u = unit_lookup(units); 285 size = letoh64(partn->gp_lba_end) - letoh64(partn->gp_lba_start) + 1; 286 size = (size * secsize) / unit_types[u].conversion; 287 printf("%c%3d: %-36s [%12lld: %12.0f%s]\n", 288 (letoh64(partn->gp_attrs) & GPTDOSACTIVE)?'*':' ', n, 289 PRT_uuid_to_typename(&guid), letoh64(partn->gp_lba_start), 290 size, unit_types[u].abbr); 291 292 if (verbosity) { 293 uuid_dec_le(&partn->gp_guid, &guid); 294 uuid_to_string(&guid, &guidstr, &status); 295 if (status != uuid_s_ok) 296 printf(" <invalid partition guid> "); 297 else 298 printf(" %-36s ", guidstr); 299 printf("%-36s\n", utf16le_to_string(partn->gp_name)); 300 free(guidstr); 301 } 302 } 303 304 int 305 GPT_init(void) 306 { 307 extern u_int32_t b_arg; 308 const int secsize = unit_types[SECTORS].conversion; 309 struct uuid guid; 310 int needed; 311 uint32_t status; 312 const uint8_t gpt_uuid_efi_system[] = GPT_UUID_EFI_SYSTEM; 313 const uint8_t gpt_uuid_openbsd[] = GPT_UUID_OPENBSD; 314 315 memset(&gh, 0, sizeof(gh)); 316 memset(&gp, 0, sizeof(gp)); 317 318 needed = sizeof(gp) / secsize + 2; 319 /* Start on 64 sector boundary */ 320 if (needed % 64) 321 needed += (64 - (needed % 64)); 322 323 gh.gh_sig = htole64(GPTSIGNATURE); 324 gh.gh_rev = htole32(GPTREVISION); 325 gh.gh_size = htole32(GPTMINHDRSIZE); 326 gh.gh_csum = 0; 327 gh.gh_rsvd = 0; 328 gh.gh_lba_self = htole64(1); 329 gh.gh_lba_alt = htole64(DL_GETDSIZE(&dl) - 1); 330 gh.gh_lba_start = htole64(needed); 331 gh.gh_lba_end = htole64(DL_GETDSIZE(&dl) - needed); 332 gh.gh_part_lba = htole64(2); 333 gh.gh_part_num = htole32(NGPTPARTITIONS); 334 gh.gh_part_size = htole32(GPTMINPARTSIZE); 335 336 uuid_create(&guid, &status); 337 if (status != uuid_s_ok) 338 return (1); 339 uuid_enc_le(&gh.gh_guid, &guid); 340 341 #if defined(__i386__) || defined(__amd64__) 342 if (b_arg > 0) { 343 /* Add an EFI system partition on i386/amd64. */ 344 uuid_dec_be(gpt_uuid_efi_system, &guid); 345 uuid_enc_le(&gp[1].gp_type, &guid); 346 uuid_create(&guid, &status); 347 if (status != uuid_s_ok) 348 return (1); 349 uuid_enc_le(&gp[1].gp_guid, &guid); 350 gp[1].gp_lba_start = gh.gh_lba_start; 351 gp[1].gp_lba_end = htole64(letoh64(gh.gh_lba_start)+b_arg - 1); 352 memcpy(gp[1].gp_name, string_to_utf16le("EFI System Area"), 353 sizeof(gp[1].gp_name)); 354 } 355 #endif 356 uuid_dec_be(gpt_uuid_openbsd, &guid); 357 uuid_enc_le(&gp[3].gp_type, &guid); 358 uuid_create(&guid, &status); 359 if (status != uuid_s_ok) 360 return (1); 361 uuid_enc_le(&gp[3].gp_guid, &guid); 362 gp[3].gp_lba_start = gh.gh_lba_start; 363 #if defined(__i386__) || defined(__amd64__) 364 if (b_arg > 0) { 365 gp[3].gp_lba_start = htole64(letoh64(gp[3].gp_lba_start) + 366 b_arg); 367 if (letoh64(gp[3].gp_lba_start) % 64) 368 gp[3].gp_lba_start = 369 htole64(letoh64(gp[3].gp_lba_start) + 370 (64 - letoh64(gp[3].gp_lba_start) % 64)); 371 } 372 #endif 373 gp[3].gp_lba_end = gh.gh_lba_end; 374 memcpy(gp[3].gp_name, string_to_utf16le("OpenBSD Area"), 375 sizeof(gp[3].gp_name)); 376 377 gh.gh_part_csum = crc32((unsigned char *)&gp, sizeof(gp)); 378 gh.gh_csum = crc32((unsigned char *)&gh, sizeof(gh)); 379 380 return 0; 381 } 382 383 int 384 GPT_write(void) 385 { 386 char *secbuf; 387 const int secsize = unit_types[SECTORS].conversion; 388 ssize_t len; 389 off_t off; 390 u_int64_t altgh, altgp; 391 392 /* Assume we always write full-size partition table. XXX */ 393 altgh = DL_GETDSIZE(&dl) - 1; 394 altgp = DL_GETDSIZE(&dl) - 1 - (sizeof(gp) / secsize); 395 396 /* 397 * Place the new GPT header at the start of sectors 1 and 398 * DL_GETDSIZE(lp)-1 and write the sectors back. 399 */ 400 gh.gh_lba_self = htole64(1); 401 gh.gh_lba_alt = htole64(altgh); 402 gh.gh_part_lba = htole64(2); 403 gh.gh_part_csum = crc32((unsigned char *)&gp, sizeof(gp)); 404 gh.gh_csum = 0; 405 gh.gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size)); 406 407 secbuf = DISK_readsector(1); 408 if (secbuf == NULL) 409 return (-1); 410 411 memcpy(secbuf, &gh, sizeof(gh)); 412 DISK_writesector(secbuf, 1); 413 free(secbuf); 414 415 gh.gh_lba_self = htole64(altgh); 416 gh.gh_lba_alt = htole64(1); 417 gh.gh_part_lba = htole64(altgp); 418 gh.gh_csum = 0; 419 gh.gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size)); 420 421 secbuf = DISK_readsector(altgh); 422 if (secbuf == NULL) 423 return (-1); 424 425 memcpy(secbuf, &gh, sizeof(gh)); 426 DISK_writesector(secbuf, altgh); 427 free(secbuf); 428 429 /* 430 * Write partition table after primary header 431 * (i.e. at sector 1) and before alt header 432 * (i.e. ending in sector before alt header. 433 * XXX ALWAYS NGPTPARTITIONS! 434 * XXX ASSUME gp is multiple of sector size! 435 */ 436 off = lseek(disk.fd, secsize * 2, SEEK_SET); 437 if (off == secsize * 2) 438 len = write(disk.fd, &gp, sizeof(gp)); 439 else 440 len = -1; 441 if (len == -1 || len != sizeof(gp)) { 442 errno = EIO; 443 return (-1); 444 } 445 446 off = lseek(disk.fd, secsize * altgp, SEEK_SET); 447 if (off == secsize * altgp) 448 len = write(disk.fd, &gp, sizeof(gp)); 449 else 450 len = -1; 451 452 if (len == -1 || len != sizeof(gp)) { 453 errno = EIO; 454 return (-1); 455 } 456 457 /* Refresh in-kernel disklabel from the updated disk information. */ 458 ioctl(disk.fd, DIOCRLDINFO, 0); 459 460 return (0); 461 } 462 463 int 464 gp_lba_start_cmp(const void *e1, const void *e2) 465 { 466 struct gpt_partition *p1 = *(struct gpt_partition **)e1; 467 struct gpt_partition *p2 = *(struct gpt_partition **)e2; 468 u_int64_t o1; 469 u_int64_t o2; 470 471 o1 = letoh64(p1->gp_lba_start); 472 o2 = letoh64(p2->gp_lba_start); 473 474 if (o1 < o2) 475 return -1; 476 else if (o1 > o2) 477 return 1; 478 else 479 return 0; 480 } 481 482 struct gpt_partition ** 483 sort_gpt(void) 484 { 485 static struct gpt_partition *sgp[NGPTPARTITIONS+2]; 486 unsigned int i, j; 487 488 memset(sgp, 0, sizeof(sgp)); 489 490 j = 0; 491 for (i = 0; i < letoh32(gh.gh_part_num); i++) { 492 if (letoh64(gp[i].gp_lba_start) >= letoh64(gh.gh_lba_start)) 493 sgp[j++] = &gp[i]; 494 } 495 496 if (j > 1) { 497 if (mergesort(sgp, j, sizeof(sgp[0]), gp_lba_start_cmp) == -1) { 498 printf("unable to sort gpt by lba start\n"); 499 return NULL; 500 } 501 } 502 503 return (sgp); 504 } 505 506 int 507 GPT_get_lba_start(unsigned int pn) 508 { 509 struct gpt_partition **sgp; 510 uint64_t bs, bigbs, nextbs, ns; 511 unsigned int i; 512 513 bs = letoh64(gh.gh_lba_start); 514 515 if (letoh64(gp[pn].gp_lba_start) >= bs) { 516 bs = letoh64(gp[pn].gp_lba_start); 517 } else { 518 sgp = sort_gpt(); 519 if (sgp == NULL) 520 return -1; 521 if (sgp[0] != NULL) { 522 bigbs = bs; 523 ns = 0; 524 for (i = 0; sgp[i] != NULL; i++) { 525 nextbs = letoh64(sgp[i]->gp_lba_start); 526 if (bs < nextbs && ns < nextbs - bs) { 527 ns = nextbs - bs; 528 bigbs = bs; 529 } 530 bs = letoh64(sgp[i]->gp_lba_end) + 1; 531 } 532 nextbs = letoh64(gh.gh_lba_end) + 1; 533 if (bs < nextbs && ns < nextbs - bs) { 534 ns = nextbs - bs; 535 bigbs = bs; 536 } 537 if (ns == 0) { 538 printf("no space for partition %u\n", pn); 539 return -1; 540 } 541 bs = bigbs; 542 } 543 } 544 545 bs = getuint64("Partition offset", bs, letoh64(gh.gh_lba_start), 546 letoh64(gh.gh_lba_end)); 547 548 for (i = 0; i < letoh32(gh.gh_part_num); i++) { 549 if (i == pn) 550 continue; 551 if (bs >= letoh64(gp[i].gp_lba_start) && 552 bs <= letoh64(gp[i].gp_lba_end)) { 553 printf("partition %u can't start inside partition %u\n", 554 pn, i); 555 return -1; 556 } 557 } 558 559 gp[pn].gp_lba_start = htole64(bs); 560 561 return 0; 562 } 563 564 int 565 GPT_get_lba_end(unsigned int pn) 566 { 567 struct gpt_partition **sgp; 568 uint64_t bs, nextbs, ns; 569 unsigned int i; 570 571 sgp = sort_gpt(); 572 if (sgp == NULL) 573 return -1; 574 575 bs = letoh64(gp[pn].gp_lba_start); 576 ns = letoh64(gh.gh_lba_end) - bs + 1; 577 for (i = 0; sgp[i] != NULL; i++) { 578 nextbs = letoh64(sgp[i]->gp_lba_start); 579 if (nextbs > bs) { 580 ns = nextbs - bs; 581 break; 582 } 583 } 584 ns = getuint64("Partition size", ns, 1, ns); 585 586 gp[pn].gp_lba_end = htole64(bs + ns - 1); 587 588 return 0; 589 } 590