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