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