1 /* $OpenBSD: cmd.c,v 1.147 2021/10/21 13:16:49 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 prt *pp; 194 unsigned char oldid; 195 196 pp = &mbr->mbr_prt[pn]; 197 oldid = pp->prt_id; 198 199 if (setpid(pn, mbr)) 200 return -1; 201 202 if (pp->prt_id == DOSPTYP_UNUSED) { 203 if (oldid != DOSPTYP_UNUSED) { 204 memset(pp, 0, sizeof(*pp)); 205 printf("Partition %d is disabled.\n", pn); 206 } 207 return 0; 208 } 209 210 if (ask_yn("Do you wish to edit in CHS mode?")) { 211 pp->prt_scyl = ask_num("BIOS Starting cylinder", pp->prt_scyl, 0, 212 disk.dk_cylinders - 1); 213 pp->prt_shead = ask_num("BIOS Starting head", pp->prt_shead, 0, 214 disk.dk_heads - 1); 215 pp->prt_ssect = ask_num("BIOS Starting sector", pp->prt_ssect, 1, 216 disk.dk_sectors); 217 218 pp->prt_ecyl = ask_num("BIOS Ending cylinder", pp->prt_ecyl, 219 pp->prt_scyl, disk.dk_cylinders - 1); 220 pp->prt_ehead = ask_num("BIOS Ending head", pp->prt_ehead, 221 (pp->prt_scyl == pp->prt_ecyl) ? pp->prt_shead : 0, disk.dk_heads - 1); 222 pp->prt_esect = ask_num("BIOS Ending sector", pp->prt_esect, 223 (pp->prt_scyl == pp->prt_ecyl && pp->prt_shead == pp->prt_ehead) ? pp->prt_ssect 224 : 1, disk.dk_sectors); 225 226 /* Fix up off/size values */ 227 PRT_fix_BN(pp, pn); 228 /* Fix up CHS values for LBA */ 229 PRT_fix_CHS(pp); 230 } else { 231 pp->prt_bs = getuint64("Partition offset", pp->prt_bs, 0, disk.dk_size - 1); 232 pp->prt_ns = getuint64("Partition size", pp->prt_ns, 1, 233 disk.dk_size - pp->prt_bs); 234 235 /* Fix up CHS values */ 236 PRT_fix_CHS(pp); 237 } 238 239 return 0; 240 } 241 242 int 243 Xedit(char *args, struct mbr *mbr) 244 { 245 struct gpt_partition oldgg; 246 struct prt oldprt; 247 struct gpt_partition *gg; 248 int pn; 249 250 pn = parsepn(args); 251 if (pn == -1) 252 return CMD_CONT; 253 254 if (letoh64(gh.gh_sig) == GPTSIGNATURE) { 255 oldgg = gp[pn]; 256 if (gedit(pn)) 257 gp[pn] = oldgg; 258 else if (memcmp(&gp[pn], &oldgg, sizeof(oldgg))) 259 return CMD_DIRTY; 260 } else { 261 oldprt = mbr->mbr_prt[pn]; 262 if (edit(pn, mbr)) 263 mbr->mbr_prt[pn] = oldprt; 264 else if (memcmp(&mbr->mbr_prt[pn], &oldprt, sizeof(oldprt))) 265 return CMD_DIRTY; 266 } 267 268 return CMD_CONT; 269 } 270 271 int 272 gsetpid(const int pn) 273 { 274 struct uuid gp_type, gp_guid; 275 struct gpt_partition *gg; 276 int status; 277 278 gg = &gp[pn]; 279 280 GPT_print_parthdr(TERSE); 281 GPT_print_part(pn, "s", TERSE); 282 283 uuid_dec_le(&gg->gp_type, &gp_type); 284 if (PRT_protected_guid(&gp_type)) { 285 printf("can't edit partition type %s\n", 286 PRT_uuid_to_typename(&gp_type)); 287 return -1; 288 } 289 290 gp_type = *ask_uuid(&gp_type); 291 if (PRT_protected_guid(&gp_type)) { 292 printf("can't change partition type to %s\n", 293 PRT_uuid_to_typename(&gp_type)); 294 return -1; 295 } 296 297 uuid_dec_le(&gg->gp_guid, &gp_guid); 298 if (uuid_is_nil(&gp_guid, NULL)) { 299 uuid_create(&gp_guid, &status); 300 if (status != uuid_s_ok) { 301 printf("could not create guid for partition\n"); 302 return -1; 303 } 304 } 305 306 uuid_enc_le(&gg->gp_type, &gp_type); 307 uuid_enc_le(&gg->gp_guid, &gp_guid); 308 309 return 0; 310 } 311 312 int 313 setpid(const int pn, struct mbr *mbr) 314 { 315 struct prt *pp; 316 317 pp = &mbr->mbr_prt[pn]; 318 319 PRT_print(0, NULL, "s"); 320 PRT_print(pn, pp, "s"); 321 322 pp->prt_id = ask_pid(pp->prt_id); 323 324 return 0; 325 } 326 327 int 328 Xsetpid(char *args, struct mbr *mbr) 329 { 330 struct gpt_partition oldgg; 331 struct prt oldprt; 332 int pn; 333 334 pn = parsepn(args); 335 if (pn == -1) 336 return CMD_CONT; 337 338 if (letoh64(gh.gh_sig) == GPTSIGNATURE) { 339 oldgg = gp[pn]; 340 if (gsetpid(pn)) 341 gp[pn] = oldgg; 342 else if (memcmp(&gp[pn], &oldgg, sizeof(oldgg))) 343 return CMD_DIRTY; 344 } else { 345 oldprt = mbr->mbr_prt[pn]; 346 if (setpid(pn, mbr)) 347 mbr->mbr_prt[pn] = oldprt; 348 else if (memcmp(&mbr->mbr_prt[pn], &oldprt, sizeof(oldprt))) 349 return CMD_DIRTY; 350 } 351 352 return CMD_CONT; 353 } 354 355 int 356 Xselect(char *args, struct mbr *mbr) 357 { 358 static uint64_t lba_firstembr = 0; 359 uint64_t lba_self; 360 int pn; 361 362 pn = parsepn(args); 363 if (pn == -1) 364 return CMD_CONT; 365 366 lba_self = mbr->mbr_prt[pn].prt_bs; 367 368 if ((mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTEND) && 369 (mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTENDL)) { 370 printf("Partition %d is not an extended partition.\n", pn); 371 return CMD_CONT; 372 } 373 374 if (lba_firstembr == 0) 375 lba_firstembr = lba_self; 376 377 if (lba_self == 0) { 378 printf("Loop to MBR (sector 0)! Not selected.\n"); 379 return CMD_CONT; 380 } else { 381 printf("Selected extended partition %d\n", pn); 382 printf("New EMBR at offset %llu.\n", lba_self); 383 } 384 385 USER_edit(lba_self, lba_firstembr); 386 387 return CMD_CONT; 388 } 389 390 int 391 Xprint(char *args, struct mbr *mbr) 392 { 393 if (letoh64(gh.gh_sig) == GPTSIGNATURE) 394 GPT_print(args, VERBOSE); 395 else 396 MBR_print(mbr, args); 397 398 return CMD_CONT; 399 } 400 401 int 402 Xwrite(char *args, struct mbr *mbr) 403 { 404 int i, n; 405 406 for (i = 0, n = 0; i < NDOSPART; i++) 407 if (mbr->mbr_prt[i].prt_id == 0xA6) 408 n++; 409 if (n >= 2) { 410 warnx("MBR contains more than one OpenBSD partition!"); 411 if (!ask_yn("Write MBR anyway?")) 412 return CMD_CONT; 413 } 414 415 if (letoh64(gh.gh_sig) == GPTSIGNATURE) { 416 printf("Writing GPT.\n"); 417 if (GPT_write() == -1) { 418 warn("error writing GPT"); 419 return CMD_CONT; 420 } 421 } else { 422 printf("Writing MBR at offset %lld.\n", (long long)mbr->mbr_lba_self); 423 if (MBR_write(mbr) == -1) { 424 warn("error writing MBR"); 425 return CMD_CONT; 426 } 427 GPT_zap_headers(); 428 } 429 430 return CMD_CLEAN; 431 } 432 433 int 434 Xquit(char *args, struct mbr *mbr) 435 { 436 return CMD_QUIT; 437 } 438 439 int 440 Xabort(char *args, struct mbr *mbr) 441 { 442 exit(0); 443 } 444 445 int 446 Xexit(char *args, struct mbr *mbr) 447 { 448 return CMD_EXIT; 449 } 450 451 int 452 Xhelp(char *args, struct mbr *mbr) 453 { 454 USER_help(); 455 456 return CMD_CONT; 457 } 458 459 int 460 Xupdate(char *args, struct mbr *mbr) 461 { 462 memcpy(mbr->mbr_code, default_dmbr.dmbr_boot, sizeof(mbr->mbr_code)); 463 mbr->mbr_signature = DOSMBR_SIGNATURE; 464 printf("Machine code updated.\n"); 465 return CMD_DIRTY; 466 } 467 468 int 469 Xflag(char *args, struct mbr *mbr) 470 { 471 const char *errstr; 472 char *part, *flag; 473 long long val = -1; 474 int i, pn; 475 476 flag = args; 477 part = strsep(&flag, WHITESPACE); 478 479 pn = parsepn(part); 480 if (pn == -1) 481 return CMD_CONT; 482 483 if (flag != NULL) { 484 if (letoh64(gh.gh_sig) == GPTSIGNATURE) 485 val = strtonum(flag, 0, INT64_MAX, &errstr); 486 else 487 val = strtonum(flag, 0, 0xff, &errstr); 488 if (errstr) { 489 printf("flag value is %s: %s.\n", errstr, flag); 490 return CMD_CONT; 491 } 492 if (letoh64(gh.gh_sig) == GPTSIGNATURE) 493 gp[pn].gp_attrs = htole64(val); 494 else 495 mbr->mbr_prt[pn].prt_flag = val; 496 printf("Partition %d flag value set to 0x%llx.\n", pn, val); 497 } else { 498 if (letoh64(gh.gh_sig) == GPTSIGNATURE) { 499 for (i = 0; i < NGPTPARTITIONS; i++) { 500 if (i == pn) 501 gp[i].gp_attrs = htole64(GPTDOSACTIVE); 502 else 503 gp[i].gp_attrs = htole64(0); 504 } 505 } else { 506 for (i = 0; i < NDOSPART; i++) { 507 if (i == pn) 508 mbr->mbr_prt[i].prt_flag = DOSACTIVE; 509 else 510 mbr->mbr_prt[i].prt_flag = 0x00; 511 } 512 } 513 printf("Partition %d marked active.\n", pn); 514 } 515 516 return CMD_DIRTY; 517 } 518 519 int 520 Xmanual(char *args, struct mbr *mbr) 521 { 522 char *pager = "/usr/bin/less"; 523 char *p; 524 FILE *f; 525 sig_t opipe; 526 527 opipe = signal(SIGPIPE, SIG_IGN); 528 if ((p = getenv("PAGER")) != NULL && (*p != '\0')) 529 pager = p; 530 if (asprintf(&p, "gunzip -qc|%s", pager) != -1) { 531 f = popen(p, "w"); 532 if (f) { 533 fwrite(manpage, manpage_sz, 1, f); 534 pclose(f); 535 } 536 free(p); 537 } 538 539 signal(SIGPIPE, opipe); 540 541 return CMD_CONT; 542 } 543 544 int 545 ask_num(const char *str, int dflt, int low, int high) 546 { 547 char lbuf[100]; 548 const char *errstr; 549 int num; 550 551 if (dflt < low) 552 dflt = low; 553 else if (dflt > high) 554 dflt = high; 555 556 do { 557 printf("%s [%d - %d]: [%d] ", str, low, high, dflt); 558 string_from_line(lbuf, sizeof(lbuf), TRIMMED); 559 560 if (lbuf[0] == '\0') { 561 num = dflt; 562 errstr = NULL; 563 } else { 564 num = (int)strtonum(lbuf, low, high, &errstr); 565 if (errstr) 566 printf("%s is %s: %s.\n", str, errstr, lbuf); 567 } 568 } while (errstr); 569 570 return num; 571 } 572 573 int 574 ask_pid(const int dflt) 575 { 576 char lbuf[100]; 577 int num; 578 579 for (;;) { 580 printf("Partition id ('0' to disable) [01 - FF]: [%02X] ", dflt); 581 printf("(? for help) "); 582 string_from_line(lbuf, sizeof(lbuf), TRIMMED); 583 584 if (strlen(lbuf) == 0) 585 return dflt; 586 if (strcmp(lbuf, "?") == 0) { 587 PRT_print_mbrtypes(); 588 continue; 589 } 590 591 num = hex_octet(lbuf); 592 if (num != -1) 593 return num; 594 595 printf("'%s' is not a valid partition id.\n", lbuf); 596 } 597 } 598 599 struct uuid * 600 ask_uuid(const struct uuid *olduuid) 601 { 602 static struct uuid uuid; 603 char lbuf[100]; 604 char *dflt = NULL; 605 int status; 606 int num = 0; 607 608 uuid = *olduuid; 609 if (uuid_is_nil(&uuid, NULL) == 0) { 610 num = PRT_uuid_to_type(&uuid); 611 if (num == 0) { 612 uuid_to_string(&uuid, &dflt, &status); 613 if (status != uuid_s_ok) { 614 printf("uuid_to_string() failed\n"); 615 goto done; 616 } 617 } 618 } 619 if (dflt == NULL) { 620 if (asprintf(&dflt, "%X", num) == -1) { 621 warn("asprintf()"); 622 goto done; 623 } 624 } 625 626 for (;;) { 627 printf("Partition id ('0' to disable) [01 - FF, <uuid>]: [%s] ", 628 dflt); 629 printf("(? for help) "); 630 string_from_line(lbuf, sizeof(lbuf), TRIMMED); 631 632 if (strcmp(lbuf, "?") == 0) { 633 PRT_print_gpttypes(); 634 continue; 635 } else if (strlen(lbuf) == 0) { 636 uuid = *olduuid; 637 goto done; 638 } 639 640 uuid_from_string(lbuf, &uuid, &status); 641 if (status == uuid_s_ok) 642 goto done; 643 644 num = hex_octet(lbuf); 645 switch (num) { 646 case -1: 647 printf("'%s' is not a valid partition id\n", lbuf); 648 break; 649 case 0: 650 uuid_create_nil(&uuid, NULL); 651 goto done; 652 default: 653 uuid = *PRT_type_to_uuid(num); 654 if (uuid_is_nil(&uuid, NULL) == 0) 655 goto done; 656 printf("'%s' has no associated UUID\n", lbuf); 657 break; 658 } 659 } 660 661 done: 662 free(dflt); 663 return &uuid; 664 } 665 666 char * 667 ask_string(const char *prompt, const char *oval) 668 { 669 static char buf[UUID_STR_LEN + 1]; 670 671 buf[0] = '\0'; 672 printf("%s: [%s] ", prompt, oval ? oval : ""); 673 string_from_line(buf, sizeof(buf), UNTRIMMED); 674 675 if (buf[0] == '\0' && oval) 676 strlcpy(buf, oval, sizeof(buf)); 677 678 return buf; 679 } 680