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