1/* $OpenBSD: cmd.c,v 1.180 2024/03/01 17:48:03 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 39int gedit(const int); 40int edit(const int, struct mbr *); 41int gsetpid(const int); 42int setpid(const int, struct mbr *); 43int parsepn(const char *); 44int parseflag(const char *, uint64_t *); 45 46int ask_num(const char *, int, int, int); 47int ask_pid(const int); 48const struct uuid *ask_uuid(const struct uuid *); 49 50extern const unsigned char manpage[]; 51extern const int manpage_sz; 52 53int 54Xreinit(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 82int 83Xswap(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 125int 126gedit(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 152int 153parsepn(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 177int 178parseflag(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 210int 211edit(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 266int 267Xedit(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 294int 295gsetpid(const int pn) 296{ 297 int32_t is_nil; 298 uint32_t status; 299 300 GPT_print_parthdr(TERSE); 301 GPT_print_part(pn, "s", TERSE); 302 303 if (PRT_protected_uuid(&gp[pn].gp_type)) { 304 printf("can't edit partition type %s\n", 305 PRT_uuid_to_desc(&gp[pn].gp_type)); 306 return -1; 307 } 308 309 is_nil = uuid_is_nil(&gp[pn].gp_type, NULL); 310 gp[pn].gp_type = *ask_uuid(&gp[pn].gp_type); 311 if (PRT_protected_uuid(&gp[pn].gp_type) && is_nil == 0) { 312 printf("can't change partition type to %s\n", 313 PRT_uuid_to_desc(&gp[pn].gp_type)); 314 return -1; 315 } 316 317 if (uuid_is_nil(&gp[pn].gp_guid, NULL)) { 318 uuid_create(&gp[pn].gp_guid, &status); 319 if (status != uuid_s_ok) { 320 printf("could not create guid for partition\n"); 321 return -1; 322 } 323 } 324 325 return 0; 326} 327 328int 329setpid(const int pn, struct mbr *mbr) 330{ 331 struct prt *pp; 332 333 pp = &mbr->mbr_prt[pn]; 334 335 PRT_print_parthdr(); 336 PRT_print_part(pn, pp, "s"); 337 338 pp->prt_id = ask_pid(pp->prt_id); 339 340 return 0; 341} 342 343int 344Xsetpid(const char *args, struct mbr *mbr) 345{ 346 struct gpt_partition oldgg; 347 struct prt oldprt; 348 int pn; 349 350 pn = parsepn(args); 351 if (pn == -1) 352 return CMD_CONT; 353 354 if (gh.gh_sig == GPTSIGNATURE) { 355 oldgg = gp[pn]; 356 if (gsetpid(pn)) 357 gp[pn] = oldgg; 358 else if (memcmp(&gp[pn], &oldgg, sizeof(gp[pn]))) 359 return CMD_DIRTY; 360 } else { 361 oldprt = mbr->mbr_prt[pn]; 362 if (setpid(pn, mbr)) 363 mbr->mbr_prt[pn] = oldprt; 364 else if (memcmp(&mbr->mbr_prt[pn], &oldprt, sizeof(oldprt))) 365 return CMD_DIRTY; 366 } 367 368 return CMD_CONT; 369} 370 371int 372Xselect(const char *args, struct mbr *mbr) 373{ 374 static uint64_t lba_firstembr = 0; 375 uint64_t lba_self; 376 int pn; 377 378 pn = parsepn(args); 379 if (pn == -1) 380 return CMD_CONT; 381 382 lba_self = mbr->mbr_prt[pn].prt_bs; 383 384 if ((mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTEND) && 385 (mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTENDL)) { 386 printf("Partition %d is not an extended partition.\n", pn); 387 return CMD_CONT; 388 } 389 390 if (lba_firstembr == 0) 391 lba_firstembr = lba_self; 392 393 if (lba_self == 0) { 394 printf("Loop to MBR (sector 0)! Not selected.\n"); 395 return CMD_CONT; 396 } else { 397 printf("Selected extended partition %d\n", pn); 398 printf("New EMBR at offset %llu.\n", lba_self); 399 } 400 401 USER_edit(lba_self, lba_firstembr); 402 403 return CMD_CONT; 404} 405 406int 407Xprint(const char *args, struct mbr *mbr) 408{ 409 if (gh.gh_sig == GPTSIGNATURE) 410 GPT_print(args, VERBOSE); 411 else if (MBR_valid_prt(mbr)) 412 MBR_print(mbr, args); 413 else { 414 DISK_printgeometry("s"); 415 printf("Offset: %d\tSignature: 0x%X.\tNo MBR or GPT.\n", 416 DOSBBSECTOR, (int)mbr->mbr_signature); 417 } 418 419 return CMD_CONT; 420} 421 422int 423Xwrite(const char *args, struct mbr *mbr) 424{ 425 unsigned int i, n; 426 427 for (i = 0, n = 0; i < nitems(mbr->mbr_prt); i++) 428 if (mbr->mbr_prt[i].prt_id == DOSPTYP_OPENBSD) 429 n++; 430 if (n > 1) { 431 warnx("MBR contains more than one OpenBSD partition!"); 432 if (ask_yn("Write MBR anyway?") == 0) 433 return CMD_CONT; 434 } 435 436 if (gh.gh_sig == GPTSIGNATURE) { 437 printf("Writing GPT.\n"); 438 if (GPT_write() == -1) { 439 warnx("error writing GPT"); 440 return CMD_CONT; 441 } 442 } else { 443 printf("Writing MBR at offset %llu.\n", mbr->mbr_lba_self); 444 if (MBR_write(mbr) == -1) { 445 warnx("error writing MBR"); 446 return CMD_CONT; 447 } 448 GPT_zap_headers(); 449 } 450 451 return CMD_CLEAN; 452} 453 454int 455Xquit(const char *args, struct mbr *mbr) 456{ 457 return CMD_QUIT; 458} 459 460int 461Xabort(const char *args, struct mbr *mbr) 462{ 463 exit(0); 464} 465 466int 467Xexit(const char *args, struct mbr *mbr) 468{ 469 return CMD_EXIT; 470} 471 472int 473Xhelp(const char *args, struct mbr *mbr) 474{ 475 USER_help(mbr); 476 477 return CMD_CONT; 478} 479 480int 481Xupdate(const char *args, struct mbr *mbr) 482{ 483 memcpy(mbr->mbr_code, default_dmbr.dmbr_boot, sizeof(mbr->mbr_code)); 484 mbr->mbr_signature = DOSMBR_SIGNATURE; 485 printf("Machine code updated.\n"); 486 return CMD_DIRTY; 487} 488 489int 490Xflag(const char *args, struct mbr *mbr) 491{ 492 char lbuf[LINEBUFSZ]; 493 char *part, *flag; 494 uint64_t val; 495 int i, pn; 496 497 if (strlcpy(lbuf, args, sizeof(lbuf)) >= sizeof(lbuf)) { 498 printf("argument string too long\n"); 499 return CMD_CONT; 500 } 501 502 flag = lbuf; 503 part = strsep(&flag, WHITESPACE); 504 505 pn = parsepn(part); 506 if (pn == -1) 507 return CMD_CONT; 508 509 if (flag != NULL) { 510 if (parseflag(flag, &val) == -1) 511 return CMD_CONT; 512 if (gh.gh_sig == GPTSIGNATURE) 513 gp[pn].gp_attrs = val; 514 else 515 mbr->mbr_prt[pn].prt_flag = val; 516 printf("Partition %d flag value set to 0x%llx.\n", pn, val); 517 } else { 518 if (gh.gh_sig == GPTSIGNATURE) { 519 for (i = 0; i < gh.gh_part_num; i++) { 520 if (i == pn) 521 gp[i].gp_attrs = GPTPARTATTR_BOOTABLE; 522 else 523 gp[i].gp_attrs &= ~GPTPARTATTR_BOOTABLE; 524 } 525 } else { 526 for (i = 0; i < nitems(mbr->mbr_prt); i++) { 527 if (i == pn) 528 mbr->mbr_prt[i].prt_flag = DOSACTIVE; 529 else 530 mbr->mbr_prt[i].prt_flag = 0x00; 531 } 532 } 533 printf("Partition %d marked active.\n", pn); 534 } 535 536 return CMD_DIRTY; 537} 538 539int 540Xmanual(const char *args, struct mbr *mbr) 541{ 542 char *pager = "/usr/bin/less"; 543 char *p; 544 FILE *f; 545 sig_t opipe; 546 547 opipe = signal(SIGPIPE, SIG_IGN); 548 if ((p = getenv("PAGER")) != NULL && (*p != '\0')) 549 pager = p; 550 if (asprintf(&p, "gunzip -qc|%s", pager) != -1) { 551 f = popen(p, "w"); 552 if (f) { 553 fwrite(manpage, manpage_sz, 1, f); 554 pclose(f); 555 } 556 free(p); 557 } 558 559 signal(SIGPIPE, opipe); 560 561 return CMD_CONT; 562} 563 564int 565ask_num(const char *str, int dflt, int low, int high) 566{ 567 char lbuf[LINEBUFSZ]; 568 const char *errstr; 569 int num; 570 571 if (dflt < low) 572 dflt = low; 573 else if (dflt > high) 574 dflt = high; 575 576 do { 577 printf("%s [%d - %d]: [%d] ", str, low, high, dflt); 578 string_from_line(lbuf, sizeof(lbuf), TRIMMED); 579 580 if (lbuf[0] == '\0') { 581 num = dflt; 582 errstr = NULL; 583 } else { 584 num = (int)strtonum(lbuf, low, high, &errstr); 585 if (errstr) 586 printf("%s is %s: %s.\n", str, errstr, lbuf); 587 } 588 } while (errstr); 589 590 return num; 591} 592 593int 594ask_pid(const int dflt) 595{ 596 char lbuf[LINEBUFSZ]; 597 int num; 598 599 for (;;) { 600 printf("Partition id ('0' to disable) [01 - FF]: [%02X] ", dflt); 601 printf("(? for help) "); 602 string_from_line(lbuf, sizeof(lbuf), TRIMMED); 603 604 if (strlen(lbuf) == 0) 605 return dflt; 606 if (strcmp(lbuf, "?") == 0) { 607 PRT_print_mbrmenu(lbuf, sizeof(lbuf)); 608 if (strlen(lbuf) == 0) 609 continue; 610 } 611 612 num = hex_octet(lbuf); 613 if (num != -1) 614 return num; 615 616 printf("'%s' is not a valid partition id.\n", lbuf); 617 } 618} 619 620const struct uuid * 621ask_uuid(const struct uuid *olduuid) 622{ 623 char lbuf[LINEBUFSZ]; 624 static struct uuid uuid; 625 const char *guid; 626 char *dflt; 627 uint32_t status; 628 629 dflt = PRT_uuid_to_menudflt(olduuid); 630 if (dflt == NULL) { 631 if (asprintf(&dflt, "00") == -1) { 632 warn("asprintf()"); 633 goto done; 634 } 635 } 636 637 for (;;) { 638 printf("Partition id ('0' to disable) [01 - FF, <uuid>]: [%s] ", 639 dflt); 640 printf("(? for help) "); 641 string_from_line(lbuf, sizeof(lbuf), TRIMMED); 642 643 if (strcmp(lbuf, "?") == 0) { 644 PRT_print_gptmenu(lbuf, sizeof(lbuf)); 645 if (strlen(lbuf) == 0) 646 continue; 647 } else if (strlen(lbuf) == 0) { 648 uuid = *olduuid; 649 goto done; 650 } 651 652 guid = PRT_menuid_to_guid(hex_octet(lbuf)); 653 if (guid == NULL) 654 guid = lbuf; 655 656 uuid_from_string(guid, &uuid, &status); 657 if (status == uuid_s_ok) 658 goto done; 659 660 printf("'%s' has no associated UUID\n", lbuf); 661 } 662 663 done: 664 free(dflt); 665 return &uuid; 666} 667