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