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