1 /* $OpenBSD: cmd.c,v 1.104 2021/05/15 15:59:15 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 "disk.h" 31 #include "misc.h" 32 #include "part.h" 33 #include "mbr.h" 34 #include "gpt.h" 35 #include "user.h" 36 #include "cmd.h" 37 38 int reinited; 39 40 /* Some helper functions for GPT handling. */ 41 int Xgedit(char *); 42 int Xgsetpid(char *); 43 44 int 45 Xreinit(char *args, struct mbr *mbr) 46 { 47 struct dos_mbr dos_mbr; 48 int efi, dogpt; 49 50 efi = MBR_protective_mbr(mbr); 51 52 if (strncasecmp(args, "gpt", 3) == 0) 53 dogpt = 1; 54 else if (strncasecmp(args, "mbr", 3) == 0) 55 dogpt = 0; 56 else if (strlen(args) > 0) { 57 printf("Unrecognized modifier '%s'\n", args); 58 return (CMD_CONT); 59 } else if (efi != -1) 60 dogpt = 1; 61 else 62 dogpt = 0; 63 64 MBR_make(&initial_mbr, &dos_mbr); 65 MBR_parse(&dos_mbr, mbr->offset, mbr->reloffset, mbr); 66 67 if (dogpt) { 68 MBR_init_GPT(mbr); 69 GPT_init(); 70 GPT_print("s", TERSE); 71 } else { 72 memset(&gh, 0, sizeof(gh)); 73 MBR_init(mbr); 74 MBR_print(mbr, "s"); 75 } 76 reinited = 1; 77 78 printf("Use 'write' to update disk.\n"); 79 80 return (CMD_DIRTY); 81 } 82 83 int 84 Xdisk(char *args, struct mbr *mbr) 85 { 86 int maxcyl = 1024; 87 int maxhead = 256; 88 int maxsec = 63; 89 90 /* Print out disk info */ 91 DISK_printgeometry(args); 92 93 #if defined (__powerpc__) || defined (__mips__) 94 maxcyl = 9999999; 95 maxhead = 9999999; 96 maxsec = 9999999; 97 #endif 98 99 /* Ask for new info */ 100 if (ask_yn("Change disk geometry?")) { 101 disk.cylinders = ask_num("BIOS Cylinders", 102 disk.cylinders, 1, maxcyl); 103 disk.heads = ask_num("BIOS Heads", 104 disk.heads, 1, maxhead); 105 disk.sectors = ask_num("BIOS Sectors", 106 disk.sectors, 1, maxsec); 107 108 disk.size = disk.cylinders * disk.heads * disk.sectors; 109 } 110 111 return (CMD_CONT); 112 } 113 114 int 115 Xswap(char *args, struct mbr *mbr) 116 { 117 const char *errstr; 118 char *from, *to; 119 int pf, pt, maxpn; 120 struct prt pp; 121 struct gpt_partition gg; 122 123 to = args; 124 from = strsep(&to, " \t"); 125 126 if (to == NULL) { 127 printf("partition number is invalid:\n"); 128 return (CMD_CONT); 129 } 130 131 if (letoh64(gh.gh_sig) == GPTSIGNATURE) 132 maxpn = NGPTPARTITIONS - 1; 133 else 134 maxpn = NDOSPART - 1; 135 136 pf = strtonum(from, 0, maxpn, &errstr); 137 if (errstr) { 138 printf("partition number is %s: %s\n", errstr, from); 139 return (CMD_CONT); 140 } 141 pt = strtonum(to, 0, maxpn, &errstr); 142 if (errstr) { 143 printf("partition number is %s: %s\n", errstr, to); 144 return (CMD_CONT); 145 } 146 147 if (pt == pf) { 148 printf("%d same partition as %d, doing nothing.\n", pt, pf); 149 return (CMD_CONT); 150 } 151 152 if (letoh64(gh.gh_sig) == GPTSIGNATURE) { 153 gg = gp[pt]; 154 gp[pt] = gp[pf]; 155 gp[pf] = gg; 156 } else { 157 pp = mbr->part[pt]; 158 mbr->part[pt] = mbr->part[pf]; 159 mbr->part[pf] = pp; 160 } 161 162 return (CMD_DIRTY); 163 } 164 165 int 166 Xgedit(char *args) 167 { 168 struct gpt_partition oldpart; 169 const char *errstr; 170 struct gpt_partition *gg; 171 char *name; 172 uint16_t *utf; 173 int i, pn; 174 175 pn = strtonum(args, 0, NGPTPARTITIONS - 1, &errstr); 176 if (errstr) { 177 printf("partition number is %s: %s\n", errstr, args); 178 return (CMD_CONT); 179 } 180 gg = &gp[pn]; 181 oldpart = *gg; 182 183 Xgsetpid(args); 184 if (uuid_is_nil(&gg->gp_type, NULL)) { 185 if (uuid_is_nil(&oldpart.gp_type, NULL) == 0) { 186 memset(gg, 0, sizeof(struct gpt_partition)); 187 printf("Partition %d is disabled.\n", pn); 188 } 189 goto done; 190 } 191 192 if (GPT_get_lba_start(pn) == -1 || 193 GPT_get_lba_end(pn) == -1) { 194 *gg = oldpart; 195 goto done; 196 } 197 198 name = ask_string("Partition name", utf16le_to_string(gg->gp_name)); 199 if (strlen(name) >= GPTPARTNAMESIZE) { 200 printf("partition name must be < %d characters\n", 201 GPTPARTNAMESIZE); 202 goto done; 203 } 204 /* 205 * N.B.: simple memcpy() could copy trash from static buf! This 206 * would create false positives for the partition having changed. 207 */ 208 utf = string_to_utf16le(name); 209 for (i = 0; i < GPTPARTNAMESIZE; i++) { 210 gg->gp_name[i] = utf[i]; 211 if (utf[i] == 0) 212 break; 213 } 214 215 done: 216 if (memcmp(gg, &oldpart, sizeof(*gg))) 217 return (CMD_DIRTY); 218 else 219 return (CMD_CONT); 220 } 221 222 int 223 Xedit(char *args, struct mbr *mbr) 224 { 225 struct prt oldpart; 226 const char *errstr; 227 struct prt *pp; 228 int pn; 229 230 if (letoh64(gh.gh_sig) == GPTSIGNATURE) 231 return (Xgedit(args)); 232 233 pn = strtonum(args, 0, 3, &errstr); 234 if (errstr) { 235 printf("partition number is %s: %s\n", errstr, args); 236 return (CMD_CONT); 237 } 238 pp = &mbr->part[pn]; 239 oldpart = *pp; 240 241 Xsetpid(args, mbr); 242 if (pp->id == DOSPTYP_UNUSED) { 243 if (oldpart.id != DOSPTYP_UNUSED) { 244 memset(pp, 0, sizeof(*pp)); 245 printf("Partition %d is disabled.\n", pn); 246 } 247 goto done; 248 } 249 250 if (ask_yn("Do you wish to edit in CHS mode?")) { 251 pp->scyl = ask_num("BIOS Starting cylinder", pp->scyl, 0, 252 disk.cylinders - 1); 253 pp->shead = ask_num("BIOS Starting head", pp->shead, 0, 254 disk.heads - 1); 255 pp->ssect = ask_num("BIOS Starting sector", pp->ssect, 1, 256 disk.sectors); 257 258 pp->ecyl = ask_num("BIOS Ending cylinder", pp->ecyl, 259 pp->scyl, disk.cylinders - 1); 260 pp->ehead = ask_num("BIOS Ending head", pp->ehead, 261 (pp->scyl == pp->ecyl) ? pp->shead : 0, disk.heads - 1); 262 pp->esect = ask_num("BIOS Ending sector", pp->esect, 263 (pp->scyl == pp->ecyl && pp->shead == pp->ehead) ? pp->ssect 264 : 1, disk.sectors); 265 266 /* Fix up off/size values */ 267 PRT_fix_BN(pp, pn); 268 /* Fix up CHS values for LBA */ 269 PRT_fix_CHS(pp); 270 } else { 271 pp->bs = getuint64("Partition offset", pp->bs, 0, disk.size - 1); 272 pp->ns = getuint64("Partition size", pp->ns, 1, 273 disk.size - pp->bs); 274 275 /* Fix up CHS values */ 276 PRT_fix_CHS(pp); 277 } 278 279 done: 280 if (memcmp(pp, &oldpart, sizeof(*pp))) 281 return (CMD_DIRTY); 282 else 283 return (CMD_CONT); 284 } 285 286 int 287 Xgsetpid(char *args) 288 { 289 const char *errstr; 290 struct uuid guid; 291 struct gpt_partition *gg; 292 int pn, num, status; 293 294 pn = strtonum(args, 0, NGPTPARTITIONS - 1, &errstr); 295 if (errstr) { 296 printf("partition number is %s: %s\n", errstr, args); 297 return (CMD_CONT); 298 } 299 gg = &gp[pn]; 300 301 /* Print out current table entry */ 302 GPT_print_parthdr(TERSE); 303 GPT_print_part(pn, "s", TERSE); 304 305 /* Ask for partition type or GUID. */ 306 uuid_dec_le(&gg->gp_type, &guid); 307 num = ask_pid(PRT_uuid_to_type(&guid), &guid); 308 if (num <= 0xff) 309 guid = *(PRT_type_to_uuid(num)); 310 uuid_enc_le(&gg->gp_type, &guid); 311 312 if (uuid_is_nil(&gg->gp_guid, NULL)) { 313 uuid_create(&guid, &status); 314 if (status != uuid_s_ok) { 315 printf("could not create guid for partition\n"); 316 return (CMD_CONT); 317 } 318 uuid_enc_le(&gg->gp_guid, &guid); 319 } 320 321 return (CMD_DIRTY); 322 } 323 324 int 325 Xsetpid(char *args, struct mbr *mbr) 326 { 327 const char *errstr; 328 int pn, num; 329 struct prt *pp; 330 331 if (letoh64(gh.gh_sig) == GPTSIGNATURE) 332 return (Xgsetpid(args)); 333 334 pn = strtonum(args, 0, 3, &errstr); 335 if (errstr) { 336 printf("partition number is %s: %s\n", errstr, args); 337 return (CMD_CONT); 338 } 339 pp = &mbr->part[pn]; 340 341 /* Print out current table entry */ 342 PRT_print(0, NULL, NULL); 343 PRT_print(pn, pp, NULL); 344 345 /* Ask for MBR partition type */ 346 num = ask_pid(pp->id, NULL); 347 if (num == pp->id) 348 return (CMD_CONT); 349 350 pp->id = num; 351 352 return (CMD_DIRTY); 353 } 354 355 int 356 Xselect(char *args, struct mbr *mbr) 357 { 358 const char *errstr; 359 static off_t firstoff = 0; 360 off_t off; 361 int pn; 362 363 pn = strtonum(args, 0, 3, &errstr); 364 if (errstr) { 365 printf("partition number is %s: %s\n", errstr, args); 366 return (CMD_CONT); 367 } 368 369 off = mbr->part[pn].bs; 370 371 /* Sanity checks */ 372 if ((mbr->part[pn].id != DOSPTYP_EXTEND) && 373 (mbr->part[pn].id != DOSPTYP_EXTENDL)) { 374 printf("Partition %d is not an extended partition.\n", pn); 375 return (CMD_CONT); 376 } 377 378 if (firstoff == 0) 379 firstoff = off; 380 381 if (!off) { 382 printf("Loop to offset 0! Not selected.\n"); 383 return (CMD_CONT); 384 } else { 385 printf("Selected extended partition %d\n", pn); 386 printf("New MBR at offset %lld.\n", (long long)off); 387 } 388 389 /* Recursion is beautiful! */ 390 USER_edit(off, firstoff); 391 392 return (CMD_CONT); 393 } 394 395 int 396 Xprint(char *args, struct mbr *mbr) 397 { 398 int efi; 399 400 efi = MBR_protective_mbr(mbr); 401 if (efi != -1 && letoh64(gh.gh_sig) == GPTSIGNATURE) 402 GPT_print(args, VERBOSE); 403 else 404 MBR_print(mbr, args); 405 406 return (CMD_CONT); 407 } 408 409 int 410 Xwrite(char *args, struct mbr *mbr) 411 { 412 struct dos_mbr dos_mbr; 413 int efi, i, n; 414 415 for (i = 0, n = 0; i < NDOSPART; i++) 416 if (mbr->part[i].id == 0xA6) 417 n++; 418 if (n >= 2) { 419 warnx("MBR contains more than one OpenBSD partition!"); 420 if (!ask_yn("Write MBR anyway?")) 421 return (CMD_CONT); 422 } 423 424 MBR_make(mbr, &dos_mbr); 425 426 printf("Writing MBR at offset %lld.\n", (long long)mbr->offset); 427 if (MBR_write(mbr->offset, &dos_mbr) == -1) { 428 warn("error writing MBR"); 429 return (CMD_CONT); 430 } 431 432 if (letoh64(gh.gh_sig) == GPTSIGNATURE) { 433 printf("Writing GPT.\n"); 434 efi = MBR_protective_mbr(mbr); 435 if (efi == -1 || GPT_write() == -1) { 436 warn("error writing GPT"); 437 return (CMD_CONT); 438 } 439 } else { 440 /* Ensure any on-disk GPT headers are zeroed. */ 441 MBR_zapgpt(&dos_mbr, DL_GETDSIZE(&dl) - 1); 442 } 443 444 /* Refresh in memory copy to reflect what was just written. */ 445 MBR_parse(&dos_mbr, mbr->offset, mbr->reloffset, mbr); 446 447 return (CMD_CLEAN); 448 } 449 450 int 451 Xquit(char *args, struct mbr *mbr) 452 { 453 return (CMD_SAVE); 454 } 455 456 int 457 Xabort(char *args, struct mbr *mbr) 458 { 459 exit(0); 460 } 461 462 int 463 Xexit(char *args, struct mbr *mbr) 464 { 465 return (CMD_EXIT); 466 } 467 468 int 469 Xhelp(char *args, struct mbr *mbr) 470 { 471 char help[80]; 472 char *mbrstr; 473 int i; 474 475 for (i = 0; cmd_table[i].cmd != NULL; i++) { 476 strlcpy(help, cmd_table[i].help, sizeof(help)); 477 if (letoh64(gh.gh_sig) == GPTSIGNATURE) { 478 if (cmd_table[i].gpt == 0) 479 continue; 480 mbrstr = strstr(help, "MBR"); 481 if (mbrstr) 482 memcpy(mbrstr, "GPT", 3); 483 } 484 printf("\t%s\t\t%s\n", cmd_table[i].cmd, help); 485 } 486 487 return (CMD_CONT); 488 } 489 490 int 491 Xupdate(char *args, struct mbr *mbr) 492 { 493 /* Update code */ 494 memcpy(mbr->code, initial_mbr.code, sizeof(mbr->code)); 495 mbr->signature = DOSMBR_SIGNATURE; 496 printf("Machine code updated.\n"); 497 return (CMD_DIRTY); 498 } 499 500 int 501 Xflag(char *args, struct mbr *mbr) 502 { 503 const char *errstr; 504 int i, maxpn, pn = -1; 505 long long val = -1; 506 char *part, *flag; 507 508 flag = args; 509 part = strsep(&flag, " \t"); 510 511 if (letoh64(gh.gh_sig) == GPTSIGNATURE) 512 maxpn = NGPTPARTITIONS - 1; 513 else 514 maxpn = NDOSPART - 1; 515 516 pn = strtonum(part, 0, maxpn, &errstr); 517 if (errstr) { 518 printf("partition number is %s: %s.\n", errstr, part); 519 return (CMD_CONT); 520 } 521 522 if (flag != NULL) { 523 /* Set flag to value provided. */ 524 if (letoh64(gh.gh_sig) == GPTSIGNATURE) 525 val = strtonum(flag, 0, INT64_MAX, &errstr); 526 else 527 val = strtonum(flag, 0, 0xff, &errstr); 528 if (errstr) { 529 printf("flag value is %s: %s.\n", errstr, flag); 530 return (CMD_CONT); 531 } 532 if (letoh64(gh.gh_sig) == GPTSIGNATURE) 533 gp[pn].gp_attrs = htole64(val); 534 else 535 mbr->part[pn].flag = val; 536 printf("Partition %d flag value set to 0x%llx.\n", pn, val); 537 } else { 538 /* Set active flag */ 539 if (letoh64(gh.gh_sig) == GPTSIGNATURE) { 540 for (i = 0; i < NGPTPARTITIONS; i++) { 541 if (i == pn) 542 gp[i].gp_attrs = htole64(GPTDOSACTIVE); 543 else 544 gp[i].gp_attrs = htole64(0); 545 } 546 } else { 547 for (i = 0; i < NDOSPART; i++) { 548 if (i == pn) 549 mbr->part[i].flag = DOSACTIVE; 550 else 551 mbr->part[i].flag = 0x00; 552 } 553 } 554 printf("Partition %d marked active.\n", pn); 555 } 556 557 return (CMD_DIRTY); 558 } 559 560 int 561 Xmanual(char *args, struct mbr *mbr) 562 { 563 char *pager = "/usr/bin/less"; 564 char *p; 565 sig_t opipe; 566 extern const unsigned char manpage[]; 567 extern const int manpage_sz; 568 FILE *f; 569 570 opipe = signal(SIGPIPE, SIG_IGN); 571 if ((p = getenv("PAGER")) != NULL && (*p != '\0')) 572 pager = p; 573 if (asprintf(&p, "gunzip -qc|%s", pager) != -1) { 574 f = popen(p, "w"); 575 if (f) { 576 fwrite(manpage, manpage_sz, 1, f); 577 pclose(f); 578 } 579 free(p); 580 } 581 582 signal(SIGPIPE, opipe); 583 584 return (CMD_CONT); 585 } 586