1 /* $OpenBSD: cmd.c,v 1.154 2022/03/16 17:03:13 krw Exp $ */ 2 3 /* 4 * Copyright (c) 1997 Tobias Weingartner 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/types.h> 20 #include <sys/disklabel.h> 21 22 #include <err.h> 23 #include <signal.h> 24 #include <stdint.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <uuid.h> 29 30 #include "part.h" 31 #include "disk.h" 32 #include "misc.h" 33 #include "mbr.h" 34 #include "gpt.h" 35 #include "user.h" 36 #include "cmd.h" 37 38 int gedit(const int); 39 int edit(const int, struct mbr *); 40 int gsetpid(const int); 41 int setpid(const int, struct mbr *); 42 int parsepn(const char *); 43 44 int ask_num(const char *, int, int, int); 45 int ask_pid(const int); 46 struct uuid *ask_uuid(const struct uuid *); 47 char *ask_string(const char *, const char *); 48 49 extern const unsigned char manpage[]; 50 extern const int manpage_sz; 51 52 int 53 Xreinit(char *args, struct mbr *mbr) 54 { 55 int dogpt; 56 57 dogpt = 0; 58 59 if (strcasecmp(args, "gpt") == 0) 60 dogpt = 1; 61 else if (strcasecmp(args, "mbr") == 0) 62 dogpt = 0; 63 else if (strlen(args) > 0) { 64 printf("Unrecognized modifier '%s'\n", args); 65 return CMD_CONT; 66 } 67 68 if (dogpt) { 69 GPT_init(GHANDGP); 70 GPT_print("s", TERSE); 71 } else { 72 MBR_init(mbr); 73 MBR_print(mbr, "s"); 74 } 75 76 printf("Use 'write' to update disk.\n"); 77 78 return CMD_DIRTY; 79 } 80 81 int 82 Xswap(char *args, struct mbr *mbr) 83 { 84 char *from, *to; 85 int pf, pt; 86 struct prt pp; 87 struct gpt_partition gg; 88 89 to = args; 90 from = strsep(&to, WHITESPACE); 91 92 pt = parsepn(to); 93 if (pt == -1) 94 return CMD_CONT; 95 96 pf = parsepn(from); 97 if (pf == -1) 98 return CMD_CONT; 99 100 if (pt == pf) { 101 printf("%d same partition as %d, doing nothing.\n", pt, pf); 102 return CMD_CONT; 103 } 104 105 if (letoh64(gh.gh_sig) == GPTSIGNATURE) { 106 gg = gp[pt]; 107 gp[pt] = gp[pf]; 108 gp[pf] = gg; 109 } else { 110 pp = mbr->mbr_prt[pt]; 111 mbr->mbr_prt[pt] = mbr->mbr_prt[pf]; 112 mbr->mbr_prt[pf] = pp; 113 } 114 115 return CMD_DIRTY; 116 } 117 118 int 119 gedit(const int pn) 120 { 121 struct uuid oldtype; 122 struct gpt_partition *gg; 123 char *name; 124 uint16_t *utf; 125 int i; 126 127 gg = &gp[pn]; 128 oldtype = gg->gp_type; 129 130 if (gsetpid(pn)) 131 return -1; 132 133 if (uuid_is_nil(&gg->gp_type, NULL)) { 134 if (uuid_is_nil(&oldtype, NULL) == 0) { 135 memset(gg, 0, sizeof(struct gpt_partition)); 136 printf("Partition %d is disabled.\n", pn); 137 } 138 return 0; 139 } 140 141 if (GPT_get_lba_start(pn) == -1 || 142 GPT_get_lba_end(pn) == -1) { 143 return -1; 144 } 145 146 name = ask_string("Partition name", utf16le_to_string(gg->gp_name)); 147 if (strlen(name) >= GPTPARTNAMESIZE) { 148 printf("partition name must be < %d characters\n", 149 GPTPARTNAMESIZE); 150 return -1; 151 } 152 /* 153 * N.B.: simple memcpy() could copy trash from static buf! This 154 * would create false positives for the partition having changed. 155 */ 156 utf = string_to_utf16le(name); 157 for (i = 0; i < GPTPARTNAMESIZE; i++) { 158 gg->gp_name[i] = utf[i]; 159 if (utf[i] == 0) 160 break; 161 } 162 return 0; 163 } 164 165 int 166 parsepn(const char *pnstr) 167 { 168 const char *errstr; 169 int maxpn, pn; 170 171 if (pnstr == NULL) { 172 printf("no partition number\n"); 173 return -1; 174 } 175 176 if (letoh64(gh.gh_sig) == GPTSIGNATURE) 177 maxpn = letoh32(gh.gh_part_num) - 1; 178 else 179 maxpn = NDOSPART - 1; 180 181 pn = strtonum(pnstr, 0, maxpn, &errstr); 182 if (errstr) { 183 printf("partition number is %s: %s\n", errstr, pnstr); 184 return -1; 185 } 186 187 return pn; 188 } 189 190 int 191 edit(const int pn, struct mbr *mbr) 192 { 193 struct chs start, end; 194 struct prt *pp; 195 uint64_t track; 196 unsigned char oldid; 197 198 pp = &mbr->mbr_prt[pn]; 199 oldid = pp->prt_id; 200 201 if (setpid(pn, mbr)) 202 return -1; 203 204 if (pp->prt_id == DOSPTYP_UNUSED) { 205 if (oldid != DOSPTYP_UNUSED) { 206 memset(pp, 0, sizeof(*pp)); 207 printf("Partition %d is disabled.\n", pn); 208 } 209 return 0; 210 } 211 212 if (ask_yn("Do you wish to edit in CHS mode?")) { 213 PRT_lba_to_chs(pp, &start, &end); 214 start.chs_cyl = ask_num("BIOS Starting cylinder", start.chs_cyl, 215 0, disk.dk_cylinders - 1); 216 start.chs_head = ask_num("BIOS Starting head", start.chs_head, 217 0, disk.dk_heads - 1); 218 start.chs_sect = ask_num("BIOS Starting sector", start.chs_sect, 219 1, disk.dk_sectors); 220 221 end.chs_cyl = ask_num("BIOS Ending cylinder", end.chs_cyl, 222 start.chs_cyl, disk.dk_cylinders - 1); 223 end.chs_head = ask_num("BIOS Ending head", end.chs_head, 224 (start.chs_cyl == end.chs_cyl) ? start.chs_head : 0, 225 disk.dk_heads - 1); 226 end.chs_sect = ask_num("BIOS Ending sector", end.chs_sect, 227 (start.chs_cyl == end.chs_cyl && start.chs_head == 228 end.chs_head) ? start.chs_sect : 1, disk.dk_sectors); 229 230 /* The ATA/ATAPI spec says LBA = (C × HPC + H) × SPT + (S − 1) */ 231 track = start.chs_cyl * disk.dk_heads + start.chs_head; 232 pp->prt_bs = track * disk.dk_sectors + (start.chs_sect - 1); 233 track = end.chs_cyl * disk.dk_heads + end.chs_head; 234 pp->prt_ns = track * disk.dk_sectors + (end.chs_sect - 1) - 235 pp->prt_bs + 1; 236 } else { 237 pp->prt_bs = getuint64("Partition offset", pp->prt_bs, 0, 238 disk.dk_size - 1); 239 pp->prt_ns = getuint64("Partition size", pp->prt_ns, 1, 240 disk.dk_size - pp->prt_bs); 241 } 242 243 return 0; 244 } 245 246 int 247 Xedit(char *args, struct mbr *mbr) 248 { 249 struct gpt_partition oldgg; 250 struct prt oldprt; 251 struct gpt_partition *gg; 252 int pn; 253 254 pn = parsepn(args); 255 if (pn == -1) 256 return CMD_CONT; 257 258 if (letoh64(gh.gh_sig) == GPTSIGNATURE) { 259 oldgg = gp[pn]; 260 if (gedit(pn)) 261 gp[pn] = oldgg; 262 else if (memcmp(&gp[pn], &oldgg, sizeof(oldgg))) 263 return CMD_DIRTY; 264 } else { 265 oldprt = mbr->mbr_prt[pn]; 266 if (edit(pn, mbr)) 267 mbr->mbr_prt[pn] = oldprt; 268 else if (memcmp(&mbr->mbr_prt[pn], &oldprt, sizeof(oldprt))) 269 return CMD_DIRTY; 270 } 271 272 return CMD_CONT; 273 } 274 275 int 276 gsetpid(const int pn) 277 { 278 struct uuid gp_type, gp_guid; 279 struct gpt_partition *gg; 280 uint32_t status; 281 282 gg = &gp[pn]; 283 284 GPT_print_parthdr(TERSE); 285 GPT_print_part(pn, "s", TERSE); 286 287 uuid_dec_le(&gg->gp_type, &gp_type); 288 if (PRT_protected_guid(&gp_type)) { 289 printf("can't edit partition type %s\n", 290 PRT_uuid_to_typename(&gp_type)); 291 return -1; 292 } 293 294 gp_type = *ask_uuid(&gp_type); 295 if (PRT_protected_guid(&gp_type)) { 296 printf("can't change partition type to %s\n", 297 PRT_uuid_to_typename(&gp_type)); 298 return -1; 299 } 300 301 uuid_dec_le(&gg->gp_guid, &gp_guid); 302 if (uuid_is_nil(&gp_guid, NULL)) { 303 uuid_create(&gp_guid, &status); 304 if (status != uuid_s_ok) { 305 printf("could not create guid for partition\n"); 306 return -1; 307 } 308 } 309 310 uuid_enc_le(&gg->gp_type, &gp_type); 311 uuid_enc_le(&gg->gp_guid, &gp_guid); 312 313 return 0; 314 } 315 316 int 317 setpid(const int pn, struct mbr *mbr) 318 { 319 struct prt *pp; 320 321 pp = &mbr->mbr_prt[pn]; 322 323 PRT_print_parthdr(); 324 PRT_print_part(pn, pp, "s"); 325 326 pp->prt_id = ask_pid(pp->prt_id); 327 328 return 0; 329 } 330 331 int 332 Xsetpid(char *args, struct mbr *mbr) 333 { 334 struct gpt_partition oldgg; 335 struct prt oldprt; 336 int pn; 337 338 pn = parsepn(args); 339 if (pn == -1) 340 return CMD_CONT; 341 342 if (letoh64(gh.gh_sig) == GPTSIGNATURE) { 343 oldgg = gp[pn]; 344 if (gsetpid(pn)) 345 gp[pn] = oldgg; 346 else if (memcmp(&gp[pn], &oldgg, sizeof(oldgg))) 347 return CMD_DIRTY; 348 } else { 349 oldprt = mbr->mbr_prt[pn]; 350 if (setpid(pn, mbr)) 351 mbr->mbr_prt[pn] = oldprt; 352 else if (memcmp(&mbr->mbr_prt[pn], &oldprt, sizeof(oldprt))) 353 return CMD_DIRTY; 354 } 355 356 return CMD_CONT; 357 } 358 359 int 360 Xselect(char *args, struct mbr *mbr) 361 { 362 static uint64_t lba_firstembr = 0; 363 uint64_t lba_self; 364 int pn; 365 366 pn = parsepn(args); 367 if (pn == -1) 368 return CMD_CONT; 369 370 lba_self = mbr->mbr_prt[pn].prt_bs; 371 372 if ((mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTEND) && 373 (mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTENDL)) { 374 printf("Partition %d is not an extended partition.\n", pn); 375 return CMD_CONT; 376 } 377 378 if (lba_firstembr == 0) 379 lba_firstembr = lba_self; 380 381 if (lba_self == 0) { 382 printf("Loop to MBR (sector 0)! Not selected.\n"); 383 return CMD_CONT; 384 } else { 385 printf("Selected extended partition %d\n", pn); 386 printf("New EMBR at offset %llu.\n", lba_self); 387 } 388 389 USER_edit(lba_self, lba_firstembr); 390 391 return CMD_CONT; 392 } 393 394 int 395 Xprint(char *args, struct mbr *mbr) 396 { 397 if (letoh64(gh.gh_sig) == GPTSIGNATURE) 398 GPT_print(args, VERBOSE); 399 else 400 MBR_print(mbr, args); 401 402 return CMD_CONT; 403 } 404 405 int 406 Xwrite(char *args, struct mbr *mbr) 407 { 408 int i, n; 409 410 for (i = 0, n = 0; i < NDOSPART; i++) 411 if (mbr->mbr_prt[i].prt_id == 0xA6) 412 n++; 413 if (n >= 2) { 414 warnx("MBR contains more than one OpenBSD partition!"); 415 if (!ask_yn("Write MBR anyway?")) 416 return CMD_CONT; 417 } 418 419 if (letoh64(gh.gh_sig) == GPTSIGNATURE) { 420 printf("Writing GPT.\n"); 421 if (GPT_write() == -1) { 422 warn("error writing GPT"); 423 return CMD_CONT; 424 } 425 } else { 426 printf("Writing MBR at offset %llu.\n", mbr->mbr_lba_self); 427 if (MBR_write(mbr) == -1) { 428 warn("error writing MBR"); 429 return CMD_CONT; 430 } 431 GPT_zap_headers(); 432 } 433 434 return CMD_CLEAN; 435 } 436 437 int 438 Xquit(char *args, struct mbr *mbr) 439 { 440 return CMD_QUIT; 441 } 442 443 int 444 Xabort(char *args, struct mbr *mbr) 445 { 446 exit(0); 447 } 448 449 int 450 Xexit(char *args, struct mbr *mbr) 451 { 452 return CMD_EXIT; 453 } 454 455 int 456 Xhelp(char *args, struct mbr *mbr) 457 { 458 USER_help(); 459 460 return CMD_CONT; 461 } 462 463 int 464 Xupdate(char *args, struct mbr *mbr) 465 { 466 memcpy(mbr->mbr_code, default_dmbr.dmbr_boot, sizeof(mbr->mbr_code)); 467 mbr->mbr_signature = DOSMBR_SIGNATURE; 468 printf("Machine code updated.\n"); 469 return CMD_DIRTY; 470 } 471 472 int 473 Xflag(char *args, struct mbr *mbr) 474 { 475 const char *errstr; 476 char *part, *flag; 477 long long val = -1; 478 int i, pn; 479 480 flag = args; 481 part = strsep(&flag, WHITESPACE); 482 483 pn = parsepn(part); 484 if (pn == -1) 485 return CMD_CONT; 486 487 if (flag != NULL) { 488 if (letoh64(gh.gh_sig) == GPTSIGNATURE) 489 val = strtonum(flag, 0, INT64_MAX, &errstr); 490 else 491 val = strtonum(flag, 0, 0xff, &errstr); 492 if (errstr) { 493 printf("flag value is %s: %s.\n", errstr, flag); 494 return CMD_CONT; 495 } 496 if (letoh64(gh.gh_sig) == GPTSIGNATURE) 497 gp[pn].gp_attrs = htole64(val); 498 else 499 mbr->mbr_prt[pn].prt_flag = val; 500 printf("Partition %d flag value set to 0x%llx.\n", pn, val); 501 } else { 502 if (letoh64(gh.gh_sig) == GPTSIGNATURE) { 503 for (i = 0; i < letoh32(gh.gh_part_num); i++) { 504 if (i == pn) 505 gp[i].gp_attrs = htole64(GPTDOSACTIVE); 506 else 507 gp[i].gp_attrs = htole64(0); 508 } 509 } else { 510 for (i = 0; i < NDOSPART; i++) { 511 if (i == pn) 512 mbr->mbr_prt[i].prt_flag = DOSACTIVE; 513 else 514 mbr->mbr_prt[i].prt_flag = 0x00; 515 } 516 } 517 printf("Partition %d marked active.\n", pn); 518 } 519 520 return CMD_DIRTY; 521 } 522 523 int 524 Xmanual(char *args, struct mbr *mbr) 525 { 526 char *pager = "/usr/bin/less"; 527 char *p; 528 FILE *f; 529 sig_t opipe; 530 531 opipe = signal(SIGPIPE, SIG_IGN); 532 if ((p = getenv("PAGER")) != NULL && (*p != '\0')) 533 pager = p; 534 if (asprintf(&p, "gunzip -qc|%s", pager) != -1) { 535 f = popen(p, "w"); 536 if (f) { 537 fwrite(manpage, manpage_sz, 1, f); 538 pclose(f); 539 } 540 free(p); 541 } 542 543 signal(SIGPIPE, opipe); 544 545 return CMD_CONT; 546 } 547 548 int 549 ask_num(const char *str, int dflt, int low, int high) 550 { 551 char lbuf[100]; 552 const char *errstr; 553 int num; 554 555 if (dflt < low) 556 dflt = low; 557 else if (dflt > high) 558 dflt = high; 559 560 do { 561 printf("%s [%d - %d]: [%d] ", str, low, high, dflt); 562 string_from_line(lbuf, sizeof(lbuf), TRIMMED); 563 564 if (lbuf[0] == '\0') { 565 num = dflt; 566 errstr = NULL; 567 } else { 568 num = (int)strtonum(lbuf, low, high, &errstr); 569 if (errstr) 570 printf("%s is %s: %s.\n", str, errstr, lbuf); 571 } 572 } while (errstr); 573 574 return num; 575 } 576 577 int 578 ask_pid(const int dflt) 579 { 580 char lbuf[100]; 581 int num; 582 583 for (;;) { 584 printf("Partition id ('0' to disable) [01 - FF]: [%02X] ", dflt); 585 printf("(? for help) "); 586 string_from_line(lbuf, sizeof(lbuf), TRIMMED); 587 588 if (strlen(lbuf) == 0) 589 return dflt; 590 if (strcmp(lbuf, "?") == 0) { 591 PRT_print_mbrtypes(); 592 continue; 593 } 594 595 num = hex_octet(lbuf); 596 if (num != -1) 597 return num; 598 599 printf("'%s' is not a valid partition id.\n", lbuf); 600 } 601 } 602 603 struct uuid * 604 ask_uuid(const struct uuid *olduuid) 605 { 606 static struct uuid uuid; 607 char lbuf[100]; 608 char *dflt = NULL; 609 uint32_t status; 610 int num = 0; 611 612 uuid = *olduuid; 613 if (uuid_is_nil(&uuid, NULL) == 0) { 614 num = PRT_uuid_to_type(&uuid); 615 if (num == 0) { 616 uuid_to_string(&uuid, &dflt, &status); 617 if (status != uuid_s_ok) { 618 printf("uuid_to_string() failed\n"); 619 goto done; 620 } 621 } 622 } 623 if (dflt == NULL) { 624 if (asprintf(&dflt, "%X", num) == -1) { 625 warn("asprintf()"); 626 goto done; 627 } 628 } 629 630 for (;;) { 631 printf("Partition id ('0' to disable) [01 - FF, <uuid>]: [%s] ", 632 dflt); 633 printf("(? for help) "); 634 string_from_line(lbuf, sizeof(lbuf), TRIMMED); 635 636 if (strcmp(lbuf, "?") == 0) { 637 PRT_print_gpttypes(); 638 continue; 639 } else if (strlen(lbuf) == 0) { 640 uuid = *olduuid; 641 goto done; 642 } 643 644 uuid_from_string(lbuf, &uuid, &status); 645 if (status == uuid_s_ok) 646 goto done; 647 648 num = hex_octet(lbuf); 649 switch (num) { 650 case -1: 651 printf("'%s' is not a valid partition id\n", lbuf); 652 break; 653 case 0: 654 uuid_create_nil(&uuid, NULL); 655 goto done; 656 default: 657 uuid = *PRT_type_to_uuid(num); 658 if (uuid_is_nil(&uuid, NULL) == 0) 659 goto done; 660 printf("'%s' has no associated UUID\n", lbuf); 661 break; 662 } 663 } 664 665 done: 666 free(dflt); 667 return &uuid; 668 } 669 670 char * 671 ask_string(const char *prompt, const char *oval) 672 { 673 static char buf[UUID_STR_LEN + 1]; 674 675 buf[0] = '\0'; 676 printf("%s: [%s] ", prompt, oval ? oval : ""); 677 string_from_line(buf, sizeof(buf), UNTRIMMED); 678 679 if (buf[0] == '\0' && oval) 680 strlcpy(buf, oval, sizeof(buf)); 681 682 return buf; 683 } 684