1 /* $OpenBSD: gpt.c,v 1.16 2021/05/15 22:06:43 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_read(int which) 196 { 197 int valid; 198 199 switch (which) { 200 case PRIMARYGPT: 201 valid = GPT_get_header(GPTSECTOR); 202 break; 203 case SECONDARYGPT: 204 valid = GPT_get_header(DL_GETDSIZE(&dl) - 1); 205 break; 206 case ANYGPT: 207 valid = GPT_get_header(GPTSECTOR); 208 if (valid != 0 || GPT_get_partition_table(gh.gh_part_lba) != 0) 209 valid = GPT_get_header(DL_GETDSIZE(&dl) - 1); 210 break; 211 default: 212 return; 213 } 214 215 if (valid == 0) 216 valid = GPT_get_partition_table(gh.gh_part_lba); 217 218 if (valid != 0) { 219 /* No valid GPT found. Zap any artifacts. */ 220 memset(&gh, 0, sizeof(gh)); 221 memset(&gp, 0, sizeof(gp)); 222 } 223 } 224 225 void 226 GPT_print(char *units, int verbosity) 227 { 228 const int secsize = unit_types[SECTORS].conversion; 229 struct uuid guid; 230 char *guidstr = NULL; 231 double size; 232 int i, u, status; 233 234 u = unit_lookup(units); 235 size = ((double)DL_GETDSIZE(&dl) * secsize) / unit_types[u].conversion; 236 printf("Disk: %s Usable LBA: %llu to %llu [%.0f ", 237 disk.name, letoh64(gh.gh_lba_start), letoh64(gh.gh_lba_end), size); 238 239 if (u == SECTORS && secsize != DEV_BSIZE) 240 printf("%d-byte ", secsize); 241 printf("%s]\n", unit_types[u].lname); 242 243 if (verbosity == VERBOSE) { 244 printf("GUID: "); 245 uuid_dec_le(&gh.gh_guid, &guid); 246 uuid_to_string(&guid, &guidstr, &status); 247 if (status == uuid_s_ok) 248 printf("%s\n", guidstr); 249 else 250 printf("<invalid header GUID>\n"); 251 free(guidstr); 252 } 253 254 GPT_print_parthdr(verbosity); 255 for (i = 0; i < letoh32(gh.gh_part_num); i++) { 256 if (uuid_is_nil(&gp[i].gp_type, NULL)) 257 continue; 258 GPT_print_part(i, units, verbosity); 259 } 260 261 } 262 263 void 264 GPT_print_parthdr(int verbosity) 265 { 266 printf(" #: type " 267 " [ start: size ]\n"); 268 if (verbosity == VERBOSE) 269 printf(" guid name\n"); 270 printf("--------------------------------------------------------" 271 "----------------\n"); 272 } 273 274 void 275 GPT_print_part(int n, char *units, int verbosity) 276 { 277 struct uuid guid; 278 const int secsize = unit_types[SECTORS].conversion; 279 struct gpt_partition *partn = &gp[n]; 280 char *guidstr = NULL; 281 double size; 282 int u, status; 283 284 uuid_dec_le(&partn->gp_type, &guid); 285 u = unit_lookup(units); 286 size = letoh64(partn->gp_lba_end) - letoh64(partn->gp_lba_start) + 1; 287 size = (size * secsize) / unit_types[u].conversion; 288 printf("%c%3d: %-36s [%12lld: %12.0f%s]\n", 289 (letoh64(partn->gp_attrs) & GPTDOSACTIVE)?'*':' ', n, 290 PRT_uuid_to_typename(&guid), letoh64(partn->gp_lba_start), 291 size, unit_types[u].abbr); 292 293 if (verbosity == VERBOSE) { 294 uuid_dec_le(&partn->gp_guid, &guid); 295 uuid_to_string(&guid, &guidstr, &status); 296 if (status != uuid_s_ok) 297 printf(" <invalid partition guid> "); 298 else 299 printf(" %-36s ", guidstr); 300 printf("%-36s\n", utf16le_to_string(partn->gp_name)); 301 free(guidstr); 302 } 303 } 304 305 int 306 GPT_init(void) 307 { 308 extern uint32_t b_arg; 309 const int secsize = unit_types[SECTORS].conversion; 310 struct uuid guid; 311 int needed; 312 uint32_t status; 313 const uint8_t gpt_uuid_efi_system[] = GPT_UUID_EFI_SYSTEM; 314 const uint8_t gpt_uuid_openbsd[] = GPT_UUID_OPENBSD; 315 316 memset(&gh, 0, sizeof(gh)); 317 memset(&gp, 0, sizeof(gp)); 318 319 needed = sizeof(gp) / secsize + 2; 320 /* Start on 64 sector boundary */ 321 if (needed % 64) 322 needed += (64 - (needed % 64)); 323 324 gh.gh_sig = htole64(GPTSIGNATURE); 325 gh.gh_rev = htole32(GPTREVISION); 326 gh.gh_size = htole32(GPTMINHDRSIZE); 327 gh.gh_csum = 0; 328 gh.gh_rsvd = 0; 329 gh.gh_lba_self = htole64(1); 330 gh.gh_lba_alt = htole64(DL_GETDSIZE(&dl) - 1); 331 gh.gh_lba_start = htole64(needed); 332 gh.gh_lba_end = htole64(DL_GETDSIZE(&dl) - needed); 333 gh.gh_part_lba = htole64(2); 334 gh.gh_part_num = htole32(NGPTPARTITIONS); 335 gh.gh_part_size = htole32(GPTMINPARTSIZE); 336 337 uuid_create(&guid, &status); 338 if (status != uuid_s_ok) 339 return (1); 340 uuid_enc_le(&gh.gh_guid, &guid); 341 342 #if defined(__i386__) || defined(__amd64__) 343 if (b_arg > 0) { 344 /* Add an EFI system partition on i386/amd64. */ 345 uuid_dec_be(gpt_uuid_efi_system, &guid); 346 uuid_enc_le(&gp[1].gp_type, &guid); 347 uuid_create(&guid, &status); 348 if (status != uuid_s_ok) 349 return (1); 350 uuid_enc_le(&gp[1].gp_guid, &guid); 351 gp[1].gp_lba_start = gh.gh_lba_start; 352 gp[1].gp_lba_end = htole64(letoh64(gh.gh_lba_start)+b_arg - 1); 353 memcpy(gp[1].gp_name, string_to_utf16le("EFI System Area"), 354 sizeof(gp[1].gp_name)); 355 } 356 #endif 357 uuid_dec_be(gpt_uuid_openbsd, &guid); 358 uuid_enc_le(&gp[3].gp_type, &guid); 359 uuid_create(&guid, &status); 360 if (status != uuid_s_ok) 361 return (1); 362 uuid_enc_le(&gp[3].gp_guid, &guid); 363 gp[3].gp_lba_start = gh.gh_lba_start; 364 #if defined(__i386__) || defined(__amd64__) 365 if (b_arg > 0) { 366 gp[3].gp_lba_start = htole64(letoh64(gp[3].gp_lba_start) + 367 b_arg); 368 if (letoh64(gp[3].gp_lba_start) % 64) 369 gp[3].gp_lba_start = 370 htole64(letoh64(gp[3].gp_lba_start) + 371 (64 - letoh64(gp[3].gp_lba_start) % 64)); 372 } 373 #endif 374 gp[3].gp_lba_end = gh.gh_lba_end; 375 memcpy(gp[3].gp_name, string_to_utf16le("OpenBSD Area"), 376 sizeof(gp[3].gp_name)); 377 378 gh.gh_part_csum = crc32((unsigned char *)&gp, sizeof(gp)); 379 gh.gh_csum = crc32((unsigned char *)&gh, sizeof(gh)); 380 381 return 0; 382 } 383 384 int 385 GPT_write(void) 386 { 387 char *secbuf; 388 const int secsize = unit_types[SECTORS].conversion; 389 ssize_t len; 390 off_t off; 391 uint64_t altgh, altgp, prigh, prigp; 392 393 /* Assume we always write full-size partition table. XXX */ 394 prigh = GPTSECTOR; 395 prigp = prigh + 1; 396 altgh = DL_GETDSIZE(&dl) - 1; 397 altgp = DL_GETDSIZE(&dl) - 1 - (sizeof(gp) / secsize); 398 399 gh.gh_lba_self = htole64(prigh); 400 gh.gh_lba_alt = htole64(altgh); 401 gh.gh_part_lba = htole64(prigp); 402 gh.gh_part_csum = crc32((unsigned char *)&gp, sizeof(gp)); 403 gh.gh_csum = 0; 404 gh.gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size)); 405 406 secbuf = DISK_readsector(prigh); 407 if (secbuf == NULL) 408 return (-1); 409 410 memcpy(secbuf, &gh, sizeof(gh)); 411 DISK_writesector(secbuf, prigh); 412 free(secbuf); 413 414 gh.gh_lba_self = htole64(altgh); 415 gh.gh_lba_alt = htole64(prigh); 416 gh.gh_part_lba = htole64(altgp); 417 gh.gh_csum = 0; 418 gh.gh_csum = crc32((unsigned char *)&gh, letoh32(gh.gh_size)); 419 420 secbuf = DISK_readsector(altgh); 421 if (secbuf == NULL) 422 return (-1); 423 424 memcpy(secbuf, &gh, sizeof(gh)); 425 DISK_writesector(secbuf, altgh); 426 free(secbuf); 427 428 /* 429 * XXX ALWAYS NGPTPARTITIONS! 430 * XXX ASSUME gp is multiple of sector size! 431 */ 432 off = lseek(disk.fd, secsize * prigp, SEEK_SET); 433 if (off == secsize * prigp) 434 len = write(disk.fd, &gp, sizeof(gp)); 435 else 436 len = -1; 437 if (len == -1 || len != sizeof(gp)) { 438 errno = EIO; 439 return (-1); 440 } 441 442 off = lseek(disk.fd, secsize * altgp, SEEK_SET); 443 if (off == secsize * altgp) 444 len = write(disk.fd, &gp, sizeof(gp)); 445 else 446 len = -1; 447 448 if (len == -1 || len != sizeof(gp)) { 449 errno = EIO; 450 return (-1); 451 } 452 453 /* Refresh in-kernel disklabel from the updated disk information. */ 454 ioctl(disk.fd, DIOCRLDINFO, 0); 455 456 return (0); 457 } 458 459 int 460 gp_lba_start_cmp(const void *e1, const void *e2) 461 { 462 struct gpt_partition *p1 = *(struct gpt_partition **)e1; 463 struct gpt_partition *p2 = *(struct gpt_partition **)e2; 464 uint64_t o1; 465 uint64_t o2; 466 467 o1 = letoh64(p1->gp_lba_start); 468 o2 = letoh64(p2->gp_lba_start); 469 470 if (o1 < o2) 471 return -1; 472 else if (o1 > o2) 473 return 1; 474 else 475 return 0; 476 } 477 478 struct gpt_partition ** 479 sort_gpt(void) 480 { 481 static struct gpt_partition *sgp[NGPTPARTITIONS+2]; 482 unsigned int i, j; 483 484 memset(sgp, 0, sizeof(sgp)); 485 486 j = 0; 487 for (i = 0; i < letoh32(gh.gh_part_num); i++) { 488 if (letoh64(gp[i].gp_lba_start) >= letoh64(gh.gh_lba_start)) 489 sgp[j++] = &gp[i]; 490 } 491 492 if (j > 1) { 493 if (mergesort(sgp, j, sizeof(sgp[0]), gp_lba_start_cmp) == -1) { 494 printf("unable to sort gpt by lba start\n"); 495 return NULL; 496 } 497 } 498 499 return (sgp); 500 } 501 502 int 503 GPT_get_lba_start(unsigned int pn) 504 { 505 struct gpt_partition **sgp; 506 uint64_t bs, bigbs, nextbs, ns; 507 unsigned int i; 508 509 bs = letoh64(gh.gh_lba_start); 510 511 if (letoh64(gp[pn].gp_lba_start) >= bs) { 512 bs = letoh64(gp[pn].gp_lba_start); 513 } else { 514 sgp = sort_gpt(); 515 if (sgp == NULL) 516 return -1; 517 if (sgp[0] != NULL) { 518 bigbs = bs; 519 ns = 0; 520 for (i = 0; sgp[i] != NULL; i++) { 521 nextbs = letoh64(sgp[i]->gp_lba_start); 522 if (bs < nextbs && ns < nextbs - bs) { 523 ns = nextbs - bs; 524 bigbs = bs; 525 } 526 bs = letoh64(sgp[i]->gp_lba_end) + 1; 527 } 528 nextbs = letoh64(gh.gh_lba_end) + 1; 529 if (bs < nextbs && ns < nextbs - bs) { 530 ns = nextbs - bs; 531 bigbs = bs; 532 } 533 if (ns == 0) { 534 printf("no space for partition %u\n", pn); 535 return -1; 536 } 537 bs = bigbs; 538 } 539 } 540 541 bs = getuint64("Partition offset", bs, letoh64(gh.gh_lba_start), 542 letoh64(gh.gh_lba_end)); 543 544 for (i = 0; i < letoh32(gh.gh_part_num); i++) { 545 if (i == pn) 546 continue; 547 if (bs >= letoh64(gp[i].gp_lba_start) && 548 bs <= letoh64(gp[i].gp_lba_end)) { 549 printf("partition %u can't start inside partition %u\n", 550 pn, i); 551 return -1; 552 } 553 } 554 555 gp[pn].gp_lba_start = htole64(bs); 556 557 return 0; 558 } 559 560 int 561 GPT_get_lba_end(unsigned int pn) 562 { 563 struct gpt_partition **sgp; 564 uint64_t bs, nextbs, ns; 565 unsigned int i; 566 567 sgp = sort_gpt(); 568 if (sgp == NULL) 569 return -1; 570 571 bs = letoh64(gp[pn].gp_lba_start); 572 ns = letoh64(gh.gh_lba_end) - bs + 1; 573 for (i = 0; sgp[i] != NULL; i++) { 574 nextbs = letoh64(sgp[i]->gp_lba_start); 575 if (nextbs > bs) { 576 ns = nextbs - bs; 577 break; 578 } 579 } 580 ns = getuint64("Partition size", ns, 1, ns); 581 582 gp[pn].gp_lba_end = htole64(bs + ns - 1); 583 584 return 0; 585 } 586