1 /* $NetBSD: gptsubr.c,v 1.5 2025/03/02 01:07:11 riastradh Exp $ */ 2 3 /* 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 14 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 #ifndef lint 28 __RCSID("$NetBSD: gptsubr.c,v 1.5 2025/03/02 01:07:11 riastradh Exp $"); 29 #endif /* not lint */ 30 31 #include <sys/param.h> 32 33 #include <sys/disk.h> 34 #include <sys/disklabel.h> 35 #include <sys/disklabel_gpt.h> 36 #include <sys/dkio.h> 37 #include <sys/ioctl.h> 38 #include <sys/stat.h> 39 #include <sys/statvfs.h> 40 41 #include <err.h> 42 #include <fcntl.h> 43 #include <libgen.h> 44 #include <paths.h> 45 #include <stdbool.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <unistd.h> 49 #include <uuid.h> 50 51 #include "defs.h" 52 #include "gpt.h" 53 #include "gptsubr.h" 54 #include "map.h" 55 56 #define DEBUG 57 58 #ifdef DEBUG 59 #define DPRINTF(fmt, ...) printf(fmt, __VA_ARGS__) 60 #else 61 #define DPRINTF(fmt, ...) do { } while (/*CONSTCOND*/0) 62 #endif 63 64 static const char * 65 map_type_name(int map_type) 66 { 67 const char *xtbl[] = { 68 "UNUSED", // 0 69 "MBR", // 1 70 "MBR_PART", // 2 71 "PRI_GPT_HDR", // 3 72 "SEC_GPT_HDR", // 4 73 "PRI_GPT_TBL", // 5 74 "SEC_GPT_TBL", // 6 75 "GPT_PART", // 7 76 "PMBR", // 8 77 }; 78 79 if (map_type >= (int)__arraycount(xtbl)) 80 return "UNKNOWN"; 81 82 return xtbl[map_type]; 83 } 84 85 #if 0 86 struct map { 87 off_t map_start; 88 off_t map_size; 89 struct map *map_next; 90 struct map *map_prev; 91 int map_type; 92 #define MAP_TYPE_UNUSED 0 93 #define MAP_TYPE_MBR 1 94 #define MAP_TYPE_MBR_PART 2 95 #define MAP_TYPE_PRI_GPT_HDR 3 96 #define MAP_TYPE_SEC_GPT_HDR 4 97 #define MAP_TYPE_PRI_GPT_TBL 5 98 #define MAP_TYPE_SEC_GPT_TBL 6 99 #define MAP_TYPE_GPT_PART 7 100 #define MAP_TYPE_PMBR 8 101 unsigned int map_index; 102 void *map_data; 103 int map_alloc; 104 }; 105 106 struct gpt_ent { 107 uint8_t ent_type[16]; /* partition type GUID */ 108 uint8_t ent_guid[16]; /* unique partition GUID */ 109 uint64_t ent_lba_start; /* start of partition */ 110 uint64_t ent_lba_end; /* end of partition */ 111 uint64_t ent_attr; /* partition attributes */ 112 uint16_t ent_name[36]; /* partition name in UCS-2 */ 113 }; 114 115 struct gpt_hdr { 116 int8_t hdr_sig[8]; /* identifies GUID Partition Table */ 117 uint32_t hdr_revision; /* GPT specification revision */ 118 uint32_t hdr_size; /* size of GPT Header */ 119 uint32_t hdr_crc_self; /* CRC32 of GPT Header */ 120 uint32_t hdr__rsvd0; /* must be zero */ 121 uint64_t hdr_lba_self; /* LBA that contains this Header */ 122 uint64_t hdr_lba_alt; /* LBA of backup GPT Header */ 123 uint64_t hdr_lba_start; /* first LBA usable for partitions */ 124 uint64_t hdr_lba_end; /* last LBA usable for partitions */ 125 uint8_t hdr_guid[16]; /* GUID to identify the disk */ 126 uint64_t hdr_lba_table; /* first LBA of GPE array */ 127 uint32_t hdr_entries; /* number of entries in GPE array */ 128 uint32_t hdr_entsz; /* size of each GPE */ 129 uint32_t hdr_crc_table; /* CRC32 of GPE array */ 130 /* 131 * The remainder of the block that contains the GPT Header 132 * is reserved by EFI for future GPT Header expansion, and 133 * must be zero. 134 */ 135 }; 136 #endif 137 138 struct map_widths { 139 uint start; 140 uint size; 141 uint type_name; 142 }; 143 144 static struct map_widths 145 get_map_widths(gpt_t gpt) 146 { 147 struct map_widths w; 148 off_t max_start = 0; 149 off_t max_size = 0; 150 map_t m; 151 152 w.type_name = 0; 153 for (m = map_first(gpt); m != NULL; m = m->map_next) { 154 uint n; 155 156 if (max_start < m->map_start) 157 max_start = m->map_start; 158 if (max_size < m->map_size) 159 max_size = m->map_size; 160 if (m->map_type == MAP_TYPE_GPT_PART) { 161 struct gpt_ent *ent = m->map_data; 162 gpt_uuid_t gpt_uuid; 163 char ent_type[128]; 164 165 memcpy(&gpt_uuid, ent->ent_type, sizeof(gpt_uuid)); 166 n = (uint)gpt_uuid_snprintf(ent_type, sizeof(ent_type), 167 "%s", gpt_uuid); 168 if (w.type_name < n) 169 w.type_name = n; 170 } 171 172 } 173 w.start = (uint)snprintf(NULL, 0, "%" PRIx64, max_start); 174 w.size = (uint)snprintf(NULL, 0, "%" PRIx64, max_size); 175 176 #define MIN_WIDTH_OF(s) (sizeof(s) - 1) 177 if (w.type_name < MIN_WIDTH_OF("TYPE_NAME")) 178 w.type_name = MIN_WIDTH_OF("TYPE_NAME"); 179 if (w.start < MIN_WIDTH_OF("START")) 180 w.start = MIN_WIDTH_OF("START"); 181 if (w.size < MIN_WIDTH_OF("SIZE")) 182 w.size = MIN_WIDTH_OF("SIZE"); 183 #undef MIN_WIDTH_OF 184 185 return w; 186 } 187 188 static void 189 show_map_banner(struct map_widths w) 190 { 191 192 printf("IDX %*s %*s ATTR PARTITION_GUID" 193 " TYPE_UUID %*s " 194 "TYPE_NAME ENTRY_NAME DESCRIPTION\n", 195 w.start, "START", 196 w.size, "SIZE", 197 w.type_name, ""); 198 } 199 200 static void 201 show_map(map_t m, struct map_widths w) 202 { 203 struct gpt_ent *ent; 204 struct gpt_hdr *hdr; 205 gpt_uuid_t gpt_uuid; 206 uuid_t uuid; 207 uint32_t status; 208 char *type_uuid; 209 char *part_guid; 210 uint64_t ent_attr; 211 uint8_t ent_desc[128]; 212 char ent_type[128]; 213 214 ent_desc[0] = '\0'; 215 ent_type[0] = '\0'; 216 ent_attr = 0; 217 218 switch (m->map_type) { 219 case MAP_TYPE_PRI_GPT_HDR: 220 case MAP_TYPE_SEC_GPT_HDR: 221 hdr = m->map_data; 222 memcpy(&uuid, hdr->hdr_guid, sizeof(uuid)); 223 uuid_to_string(&uuid, &part_guid, &status); 224 type_uuid = estrdup(""); 225 break; 226 227 case MAP_TYPE_GPT_PART: 228 ent = m->map_data; 229 230 memcpy(&uuid, ent->ent_type, sizeof(uuid)); 231 uuid_to_string(&uuid, &type_uuid, &status); 232 233 memcpy(&uuid, ent->ent_guid, sizeof(uuid)); 234 uuid_to_string(&uuid, &part_guid, &status); 235 236 ent_attr = ent->ent_attr; 237 238 memcpy(&gpt_uuid, ent->ent_type, sizeof(uuid)); 239 gpt_uuid_snprintf(ent_type, sizeof(ent_type), "%s", gpt_uuid); 240 241 /* 242 * Use the gpt.c code here rather than our 243 * ucs2_to_utf8() as we are in their world. 244 */ 245 utf16_to_utf8(ent->ent_name, sizeof(ent->ent_name), 246 ent_desc, sizeof(ent_desc)); 247 248 break; 249 250 case MAP_TYPE_MBR_PART: 251 part_guid = estrdup(""); 252 type_uuid = estrdup(""); 253 break; 254 255 case MAP_TYPE_MBR: { 256 struct mbr *mbr = m->map_data; 257 258 easprintf(&part_guid, 259 "%02x%02x%02x%02x-0000-0000-0000-000000000000", 260 mbr->mbr_code[440], 261 mbr->mbr_code[441], 262 mbr->mbr_code[442], 263 mbr->mbr_code[443]); 264 uuid_to_string(&(uuid_t)GPT_ENT_TYPE_MBR, &type_uuid, &status); 265 break; 266 } 267 default: 268 part_guid = estrdup(""); 269 type_uuid = estrdup(""); 270 break; 271 } 272 273 printf("%2u: %*" PRIx64 " %*" PRIx64 " %08" PRIx64 274 " %36s %36s %*s %-11s %s\n", 275 m->map_index, 276 w.start, 277 m->map_start, 278 w.size, 279 m->map_size, 280 ent_attr, 281 part_guid, 282 type_uuid, 283 w.type_name, 284 ent_type, 285 map_type_name(m->map_type), 286 ent_desc); 287 288 free(part_guid); 289 free(type_uuid); 290 } 291 292 PUBLIC map_t 293 find_gpt_map(const char *dev, uint idx) 294 { 295 gpt_t gpt; 296 map_t m; 297 298 gpt = gpt_open(dev, GPT_READONLY, 0, 0, 0, 0); 299 300 if (gpt == NULL) 301 err(EXIT_FAILURE, "gpt_open"); 302 303 if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) == NULL) 304 printf("GPT not found, displaying data from MBR.\n"); 305 306 for (m = map_first(gpt); m != NULL; m = m->map_next) { 307 if (m->map_index == idx) 308 break; 309 } 310 gpt_close(gpt); 311 return m; 312 } 313 314 PUBLIC char * 315 parent_of_fname(const char *fname) 316 { 317 struct dkwedge_info dkinfo; 318 struct statvfs vfsb; 319 struct stat sb; 320 const char *d, *b; 321 char *p; 322 size_t n; 323 int fd, rv; 324 325 rv = stat(fname, &sb); 326 if (rv == -1) 327 err(EXIT_FAILURE, "stat: %s", fname); 328 329 if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) 330 return estrdup(fname); 331 332 rv = statvfs(fname, &vfsb); 333 if (rv == -1) 334 err(EXIT_FAILURE, "statvfs: %s", fname); 335 336 b = basename(vfsb.f_mntfromname); 337 d = dirname(vfsb.f_mntfromname); 338 easprintf(&p, "%s/r%s", d, b); 339 340 fd = open(p, O_RDONLY); 341 if (fd == -1) 342 err(EXIT_FAILURE, "open"); 343 344 rv = ioctl(fd, DIOCGWEDGEINFO, &dkinfo); 345 close(fd); 346 347 if (rv != -1) { 348 free(p); 349 return estrdup(dkinfo.dkw_parent); 350 } 351 352 warn("ioctl: DIOCGWEDGEINFO"); 353 354 /* 355 * Hum. No wedges? Assume we have the old disklabel 356 * "/dev/rwd0x" syntax. Convert it to "/dev/rwd0d". 357 * XXX: this probably won't work. 358 */ 359 n = strlen(p); 360 p[n - 1] = 'd'; 361 362 return p; 363 } 364 365 PUBLIC int 366 mbr_sig_write(const char *fname, uint32_t new_sig, bool force, int verbose) 367 { 368 gpt_t gpt; 369 map_t m; 370 struct mbr *mbr; 371 const char *dev; 372 uint32_t old_sig; 373 374 if (fname == NULL) 375 errx(EXIT_FAILURE, "please specify a device"); 376 377 dev = parent_of_fname(fname); 378 if (dev == NULL) { 379 warnx("unable to find parent device of %s\n", fname); 380 return -1; 381 } 382 383 gpt = gpt_open(dev, GPT_MODIFIED, verbose, 0, 0, 0); 384 385 if (gpt == NULL) 386 err(EXIT_FAILURE, "gpt_open"); 387 388 m = map_find(gpt, MAP_TYPE_MBR); 389 if (m == NULL) 390 printf("No MBR partition found!\n"); 391 else { 392 mbr = m->map_data; 393 394 memcpy(&old_sig, &mbr->mbr_code[440], 4); 395 396 if (old_sig == 0) 397 force = true; 398 399 if (force) { 400 memcpy(&mbr->mbr_code[440], &new_sig, 4); 401 if (gpt_write(gpt, m) == -1) 402 warn("gpt_write"); 403 else if (verbose) 404 printf("sig: 0x%08x -> 0x%08x\n", 405 old_sig, new_sig); 406 } 407 else if (verbose) 408 printf("sig: 0x%08x (unchanged)\n", old_sig); 409 } 410 411 gpt_close(gpt); 412 return 0; 413 } 414 415 PUBLIC int 416 show_gpt(const char *fname, int verbose) 417 { 418 gpt_t gpt; 419 map_t m; 420 struct map_widths w; 421 const char *dev; 422 423 dev = parent_of_fname(fname); 424 if (dev == NULL) 425 return -1; 426 427 gpt = gpt_open(dev, GPT_READONLY, verbose, 0, 0, 0); 428 429 if (gpt == NULL) 430 err(EXIT_FAILURE, "gpt_open"); 431 432 if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) == NULL) 433 warnx("GPT not found, displaying data from MBR."); 434 435 w = get_map_widths(gpt); 436 show_map_banner(w); 437 438 for (m = map_first(gpt); m != NULL; m = m->map_next) 439 show_map(m, w); 440 441 gpt_close(gpt); 442 return 0; 443 } 444 445 #if 0 /* UNUSED */ 446 447 PUBLIC char * 448 wedge_of_fname(const char *fname) 449 { 450 struct statvfs vfsbuf; 451 int rv; 452 453 rv = statvfs(fname, &vfsbuf); 454 if (rv) { 455 warn("statvfs: %s", fname); 456 return NULL; 457 } 458 459 return estrdup(vfsbuf.f_mntfromname); 460 } 461 462 PUBLIC int 463 find_partition_idx(const char *fname, int verbose) 464 { 465 struct dkwedge_info dkinfo; 466 struct stat sb; 467 struct statvfs vfsbuf; 468 gpt_t gpt; 469 map_t m; 470 off_t offset; 471 size_t size; 472 const char *parent; 473 char *b, *d; 474 int rv; 475 476 /* the following are for gpt_open() */ 477 off_t mediasz = 0; 478 u_int secsz = 0; 479 time_t timestamp = 0; 480 481 rv = stat(fname, &sb); 482 if (rv == -1) 483 err(EXIT_FAILURE, "stat: %s", fname); 484 485 if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) { 486 parent = fname; 487 offset = 0; 488 size = 0; 489 goto doit; 490 } 491 492 rv = statvfs(fname, &vfsbuf); 493 if (rv == -1) 494 err(EXIT_FAILURE, "statvfs: %s", fname); 495 496 b = basename(vfsbuf.f_mntfromname); 497 d = dirname(vfsbuf.f_mntfromname); 498 499 { 500 char *p; 501 502 easprintf(&p, "%s/r%s", d, b); 503 504 int fd = open(p, O_RDONLY); 505 if (fd == -1) 506 err(EXIT_FAILURE, "open"); 507 508 rv = ioctl(fd, DIOCGWEDGEINFO, &dkinfo); 509 if (rv != -1) { 510 parent = dkinfo.dkw_parent; 511 offset = dkinfo.dkw_offset; 512 size = dkinfo.dkw_size; 513 } 514 else { 515 struct disklabel dl; 516 517 warn("ioctl: DIOCGWEDGEINFO"); 518 519 rv = ioctl(fd, DIOCGDINFO, &dl); 520 if (rv == -1) 521 err(EXIT_FAILURE, "ioctl: DIOCGDINFO"); 522 523 size_t n = strlen(p); 524 525 int pnum = p[n - 1] - 'a'; 526 p[n - 1] = 'd'; 527 528 printf("num_parts: %u\n", dl.d_npartitions); 529 printf("partition %d\n", pnum); 530 printf(" offset = %u (%#x)\n", 531 dl.d_partitions[pnum].p_offset, 532 dl.d_partitions[pnum].p_offset); 533 printf(" size = %u (%#x)\n", 534 dl.d_partitions[pnum].p_size, 535 dl.d_partitions[pnum].p_size); 536 537 parent = p; // vfsbuf.f_mntfromname; 538 offset = dl.d_partitions[pnum].p_offset; 539 size = dl.d_partitions[pnum].p_size; 540 } 541 542 close(fd); 543 free(p); 544 } 545 546 doit: 547 DPRINTF("parent: %s\n", parent); 548 DPRINTF("offset: 0x%lx\n", offset); 549 DPRINTF("size: 0x%lx\n", size); 550 551 gpt = gpt_open(parent, GPT_READONLY, 552 verbose, mediasz, secsz, timestamp); 553 554 if (gpt == NULL) 555 err(EXIT_FAILURE, "gpt_open"); 556 557 if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) == NULL) 558 printf("GPT not found, displaying data from MBR.\n"); 559 560 int index = -1; 561 struct map_widths w; 562 if (verbose) { 563 w = get_map_widths(gpt); 564 show_map_banner(w); 565 } 566 for (m = map_first(gpt); m != NULL; m = m->map_next) { 567 if (verbose) 568 show_map(m, w); 569 570 if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1) { 571 continue; 572 } 573 574 if ((off_t)offset == m->map_start && 575 (off_t)size == m->map_size) { 576 if (index != -1) 577 printf("match: %u\n", index); 578 index = (int)m->map_index; 579 } 580 } 581 return index; 582 } 583 584 PUBLIC char * 585 find_partition_pathname(const char *fname) 586 { 587 char *pname; /* partition name */ 588 char *rname; /* real name */ 589 struct statvfs vfsbuf; 590 int i, rv; 591 592 rname = realpath(fname, NULL); 593 594 DPRINTF("fname: %s\n", fname); 595 DPRINTF("rname: %s\n", rname); 596 597 rv = statvfs(rname, &vfsbuf); 598 if (rv) { 599 warn("statvfs: %s", rname); 600 free(rname); 601 return NULL; 602 } 603 604 DPRINTF("mount: %s\n", vfsbuf.f_mntonname); 605 606 i = 0; 607 while (vfsbuf.f_mntonname[i] == rname[i] && 608 vfsbuf.f_mntonname[i] != '\0') 609 i++; 610 611 if (vfsbuf.f_mntonname[i] != '\0') 612 errx(EXIT_FAILURE, "mntonname mismatch: %s", 613 vfsbuf.f_mntonname + i); 614 615 pname = estrdup(rname + i); 616 free(rname); 617 618 return pname; 619 } 620 621 #endif 622