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