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