1 /* $NetBSD: fdisk.c,v 1.85 2004/11/12 16:57:46 christos Exp $ */ 2 3 /* 4 * Mach Operating System 5 * Copyright (c) 1992 Carnegie Mellon University 6 * All Rights Reserved. 7 * 8 * Permission to use, copy, modify and distribute this software and its 9 * documentation is hereby granted, provided that both the copyright 10 * notice and this permission notice appear in all copies of the 11 * software, derivative works or modified versions, and any portions 12 * thereof, and that both notices appear in supporting documentation. 13 * 14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 15 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 17 * 18 * Carnegie Mellon requests users of this software to return to 19 * 20 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 21 * School of Computer Science 22 * Carnegie Mellon University 23 * Pittsburgh PA 15213-3890 24 * 25 * any improvements or extensions that they make and grant Carnegie Mellon 26 * the rights to redistribute these changes. 27 */ 28 29 /* 30 * 14-Dec-89 Robert Baron (rvb) at Carnegie-Mellon University 31 * Copyright (c) 1989 Robert. V. Baron 32 * Created. 33 */ 34 35 #include <sys/cdefs.h> 36 37 #ifndef lint 38 __RCSID("$NetBSD: fdisk.c,v 1.85 2004/11/12 16:57:46 christos Exp $"); 39 #endif /* not lint */ 40 41 #define MBRPTYPENAMES 42 #include <sys/types.h> 43 #include <sys/disklabel.h> 44 #include <sys/bootblock.h> 45 #include <sys/ioctl.h> 46 #include <sys/param.h> 47 #include <sys/stat.h> 48 #include <sys/sysctl.h> 49 50 #include <ctype.h> 51 #include <disktab.h> 52 #include <err.h> 53 #include <errno.h> 54 #include <fcntl.h> 55 #include <paths.h> 56 #include <stdarg.h> 57 #include <stddef.h> 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <string.h> 61 #include <unistd.h> 62 #include <util.h> 63 64 #define DEFAULT_BOOTDIR "/usr/mdec" 65 66 #if defined(__i386__) || defined(__x86_64__) 67 #include <machine/cpu.h> 68 #define BOOTSEL 69 70 #define DEFAULT_BOOTCODE "mbr" 71 #define DEFAULT_BOOTSELCODE "mbr_bootsel" 72 #define DEFAULT_BOOTEXTCODE "mbr_ext" 73 74 /* Scan values for the various keys we use, as returned by the BIOS */ 75 #define SCAN_ENTER 0x1c 76 #define SCAN_F1 0x3b 77 #define SCAN_1 0x2 78 79 #endif 80 81 #define LBUF 100 82 static char lbuf[LBUF]; 83 84 #ifndef PRIdaddr 85 #define PRIdaddr PRId64 86 #endif 87 88 #ifndef _PATH_DEFDISK 89 #define _PATH_DEFDISK "/dev/rwd0d" 90 #endif 91 92 const char *disk = _PATH_DEFDISK; 93 94 struct disklabel disklabel; /* disk parameters */ 95 96 uint cylinders, sectors, heads; 97 daddr_t disksectors; 98 #define cylindersectors (heads * sectors) 99 100 struct mbr_sector mboot; 101 102 103 struct { 104 struct mbr_sector *ptn; /* array of pbrs */ 105 daddr_t base; /* first sector of ext. ptn */ 106 daddr_t limit; /* last sector of ext. ptn */ 107 int num_ptn; /* number of contained partitions */ 108 int ptn_id; /* entry in mbr */ 109 int is_corrupt; /* 1 if extended chain illegal */ 110 } ext; 111 112 char *boot_dir = DEFAULT_BOOTDIR; 113 char *boot_path = 0; /* name of file we actually opened */ 114 115 #ifdef BOOTSEL 116 117 #define DEFAULT_ACTIVE (~(daddr_t)0) 118 119 #define OPTIONS "0123BFSafiluvs:b:c:E:r:w:t:T:" 120 #else 121 #define change_part(e, p, id, st, sz, bm) change__part(e, p, id, st, sz) 122 #define OPTIONS "0123FSafiluvs:b:c:E:r:w:" 123 #endif 124 125 uint dos_cylinders; 126 uint dos_heads; 127 uint dos_sectors; 128 daddr_t dos_disksectors; 129 #define dos_cylindersectors (dos_heads * dos_sectors) 130 #define dos_totalsectors (dos_heads * dos_sectors * dos_cylinders) 131 132 #define DOSSECT(s,c) (((s) & 0x3f) | (((c) >> 2) & 0xc0)) 133 #define DOSCYL(c) ((c) & 0xff) 134 #define SEC_IN_1M (1024 * 1024 / 512) 135 #define SEC_TO_MB(sec) ((uint)(((sec) + SEC_IN_1M / 2) / SEC_IN_1M)) 136 #define SEC_TO_CYL(sec) (((sec) + dos_cylindersectors/2) / dos_cylindersectors) 137 138 #define MAXCYL 1024 /* Usual limit is 1023 */ 139 #define MAXHEAD 256 /* Usual limit is 255 */ 140 #define MAXSECTOR 63 141 int partition = -1; 142 143 int fd = -1, wfd = -1, *rfd = &fd; 144 char *disk_file; 145 char *disk_type = NULL; 146 147 int a_flag; /* set active partition */ 148 int i_flag; /* init bootcode */ 149 int u_flag; /* update partition data */ 150 int v_flag; /* more verbose */ 151 int sh_flag; /* Output data as shell defines */ 152 int f_flag; /* force --not interactive */ 153 int s_flag; /* set id,offset,size */ 154 int b_flag; /* Set cyl, heads, secs (as c/h/s) */ 155 int B_flag; /* Edit/install bootselect code */ 156 int E_flag; /* extended partition number */ 157 int b_cyl, b_head, b_sec; /* b_flag values. */ 158 int F_flag = 0; 159 160 struct mbr_sector bootcode[8192 / sizeof (struct mbr_sector)]; 161 int bootsize; /* actual size of bootcode */ 162 int boot_installed; /* 1 if we've copied code into the mbr */ 163 164 #if defined(__i386__) || defined(__x86_64__) 165 struct disklist *dl; 166 #endif 167 168 169 #define KNOWN_SYSIDS (sizeof(mbr_ptypes)/sizeof(mbr_ptypes[0])) 170 171 void usage(void); 172 void print_s0(int); 173 void print_part(struct mbr_sector *, int, daddr_t); 174 void print_mbr_partition(struct mbr_sector *, int, daddr_t, daddr_t, int); 175 int read_boot(const char *, void *, size_t, int); 176 void init_sector0(int); 177 void intuit_translated_geometry(void); 178 void get_geometry(void); 179 void get_extended_ptn(void); 180 void get_diskname(const char *, char *, size_t); 181 int change_part(int, int, int, daddr_t, daddr_t, char *); 182 void print_params(void); 183 void change_active(int); 184 void get_params_to_use(void); 185 void dos(int, unsigned char *, unsigned char *, unsigned char *); 186 int open_disk(int); 187 int read_disk(daddr_t, void *); 188 int write_disk(daddr_t, void *); 189 int get_params(void); 190 int read_s0(daddr_t, struct mbr_sector *); 191 int write_mbr(void); 192 int yesno(const char *, ...); 193 int decimal(const char *, int, int, int, int); 194 #define DEC_SEC 1 /* asking for a sector number */ 195 #define DEC_RND 2 /* round to end of first track */ 196 #define DEC_RND_0 4 /* round 0 to size of a track */ 197 #define DEC_RND_DOWN 8 /* subtract 1 track */ 198 #define DEC_RND_DOWN_2 16 /* subtract 2 tracks */ 199 void string(const char *, int, char *); 200 int ptn_id(const char *, int *); 201 int type_match(const void *, const void *); 202 const char *get_type(int); 203 int get_mapping(int, uint *, uint *, uint *, unsigned long *); 204 #ifdef BOOTSEL 205 daddr_t configure_bootsel(daddr_t); 206 void install_bootsel(int); 207 daddr_t get_default_boot(void); 208 void set_default_boot(daddr_t); 209 #endif 210 211 212 int main(int, char *[]); 213 214 int 215 main(int argc, char *argv[]) 216 { 217 struct stat sb; 218 int ch, mib[2]; 219 size_t len; 220 char *root_device; 221 char *cp; 222 int n; 223 #ifdef BOOTSEL 224 daddr_t default_ptn; /* start sector of default ptn */ 225 char *cbootmenu = 0; 226 #endif 227 228 int csysid, cstart, csize; /* For the b_flag. */ 229 230 mib[0] = CTL_KERN; 231 mib[1] = KERN_ROOT_DEVICE; 232 if (sysctl(mib, 2, NULL, &len, NULL, 0) != -1 && 233 (root_device = malloc(len)) != NULL && 234 sysctl(mib, 2, root_device, &len, NULL, 0) != -1) 235 disk = root_device; 236 237 a_flag = i_flag = u_flag = sh_flag = f_flag = s_flag = b_flag = 0; 238 v_flag = 0; 239 E_flag = 0; 240 csysid = cstart = csize = 0; 241 while ((ch = getopt(argc, argv, OPTIONS)) != -1) 242 switch (ch) { 243 case '0': 244 partition = 0; 245 break; 246 case '1': 247 partition = 1; 248 break; 249 case '2': 250 partition = 2; 251 break; 252 case '3': 253 partition = 3; 254 break; 255 case 'E': /* Extended partition number */ 256 E_flag = 1; 257 partition = strtoul(optarg, &cp, 0); 258 if (*cp || partition < 0) 259 errx(1, "Bad partition number -E %s.", optarg); 260 break; 261 #ifdef BOOTSEL 262 case 'B': /* Bootselect parameters */ 263 B_flag = 1; 264 break; 265 #endif 266 case 'F': /* device argument is really a file */ 267 F_flag = 1; 268 break; 269 case 'S': /* Output as shell variables */ 270 sh_flag = 1; 271 break; 272 case 'a': /* Set active partition */ 273 a_flag = 1; 274 break; 275 case 'f': /* Non interactive */ 276 f_flag = 1; 277 break; 278 case 'i': /* Always update bootcode */ 279 i_flag = 1; 280 break; 281 case 'l': /* List known partition types */ 282 for (len = 0; len < KNOWN_SYSIDS; len++) 283 printf("%03d %s\n", mbr_ptypes[len].id, 284 mbr_ptypes[len].name); 285 return 0; 286 case 'u': /* Update partition details */ 287 u_flag = 1; 288 break; 289 case 'v': /* Be verbose */ 290 v_flag++; 291 break; 292 case 's': /* Partition details */ 293 s_flag = 1; 294 if (sscanf(optarg, "%d/%d/%d%n", &csysid, &cstart, 295 &csize, &n) == 3) { 296 if (optarg[n] == 0) 297 break; 298 #ifdef BOOTSEL 299 if (optarg[n] == '/') { 300 cbootmenu = optarg + n + 1; 301 break; 302 } 303 #endif 304 } 305 errx(1, "Bad argument to the -s flag."); 306 break; 307 case 'b': /* BIOS geometry */ 308 b_flag = 1; 309 if (sscanf(optarg, "%d/%d/%d%n", &b_cyl, &b_head, 310 &b_sec, &n) != 3 || optarg[n] != 0) 311 errx(1, "Bad argument to the -b flag."); 312 if (b_cyl > MAXCYL) 313 b_cyl = MAXCYL; 314 break; 315 case 'c': /* file/directory containing boot code */ 316 if (strchr(optarg, '/') != NULL && 317 stat(optarg, &sb) == 0 && 318 (sb.st_mode & S_IFMT) == S_IFDIR) { 319 boot_dir = optarg; 320 break; 321 } 322 bootsize = read_boot(optarg, bootcode, 323 sizeof bootcode, 1); 324 i_flag = 1; 325 break; 326 case 'r': /* read data from disk_file (not raw disk) */ 327 rfd = &wfd; 328 /* FALLTHROUGH */ 329 case 'w': /* write data to disk_file */ 330 disk_file = optarg; 331 break; 332 case 't': 333 if (setdisktab(optarg) == -1) 334 errx(EXIT_FAILURE, "bad disktab"); 335 break; 336 case 'T': 337 disk_type = optarg; 338 break; 339 default: 340 usage(); 341 } 342 argc -= optind; 343 argv += optind; 344 345 if (disk_type != NULL && getdiskbyname(disk_type) == NULL) 346 errx(EXIT_FAILURE, "bad disktype"); 347 348 if (sh_flag && (a_flag || i_flag || u_flag || f_flag || s_flag)) 349 usage(); 350 351 if (B_flag && f_flag) { 352 warnx("Bootselector may only be configured interactively"); 353 usage(); 354 } 355 356 if (f_flag && u_flag && !s_flag) { 357 warnx("Partition data not specified"); 358 usage(); 359 } 360 361 if (s_flag && partition == -1) { 362 warnx("-s flag requires a partition selected."); 363 usage(); 364 } 365 366 if (argc > 0) 367 disk = argv[0]; 368 369 if (open_disk(B_flag || a_flag || i_flag || u_flag) < 0) 370 exit(1); 371 372 if (read_s0(0, &mboot)) 373 /* must have been a blank disk */ 374 init_sector0(1); 375 376 #if defined(__i386__) || defined(__x86_64__) 377 get_geometry(); 378 #else 379 intuit_translated_geometry(); 380 #endif 381 get_extended_ptn(); 382 383 #ifdef BOOTSEL 384 default_ptn = get_default_boot(); 385 #endif 386 387 if (E_flag && !u_flag && partition >= ext.num_ptn) 388 errx(1, "Extended partition %d is not defined.", partition); 389 390 if (u_flag && (!f_flag || b_flag)) 391 get_params_to_use(); 392 393 /* Do the update stuff! */ 394 if (u_flag) { 395 if (s_flag) 396 change_part(E_flag, partition, csysid, cstart, csize, 397 cbootmenu); 398 else { 399 int part = partition, chg_ext = E_flag, prompt = 1; 400 do { 401 if (prompt) { 402 printf("\n"); 403 print_s0(partition); 404 } 405 if (partition == -1) 406 part = ptn_id( 407 "Which partition do you want to change?", 408 &chg_ext); 409 if (part < 0) 410 break; 411 prompt = change_part(chg_ext, part, 0, 0, 0, 0); 412 } while (partition == -1); 413 } 414 } else 415 if (!i_flag && !B_flag) { 416 print_params(); 417 print_s0(partition); 418 } 419 420 if (a_flag && !E_flag) 421 change_active(partition); 422 423 #ifdef BOOTSEL 424 if (B_flag || u_flag || i_flag) 425 /* Ensure the mbr code supports this configuration */ 426 install_bootsel(0); 427 if (B_flag) 428 default_ptn = configure_bootsel(default_ptn); 429 set_default_boot(default_ptn); 430 #else 431 if (i_flag) 432 init_sector0(0); 433 #endif 434 435 if (u_flag || a_flag || i_flag || B_flag) { 436 if (!f_flag) { 437 printf("\nWe haven't written the MBR back to disk " 438 "yet. This is your last chance.\n"); 439 if (u_flag) 440 print_s0(-1); 441 if (yesno("Should we write new partition table?")) 442 write_mbr(); 443 } else 444 write_mbr(); 445 } 446 447 exit(0); 448 } 449 450 void 451 usage(void) 452 { 453 int indent = 7 + (int)strlen(getprogname()) + 1; 454 455 (void)fprintf(stderr, "usage: %s [-afiluvBS] " 456 "[-b cylinders/heads/sectors] \\\n" 457 "%*s[-0123 | -E num " 458 "[-s id/start/size[/bootmenu]]] \\\n" 459 "%*s[-t disktab] [-T disktype] \\\n" 460 "%*s[-c bootcode] [-r|-w file] [device]\n" 461 "\t-a change active partition\n" 462 "\t-f force - not interactive\n" 463 "\t-i initialise MBR code\n" 464 "\t-l list partition types\n" 465 "\t-u update partition data\n" 466 "\t-v verbose output, -v -v more verbose still\n" 467 "\t-B update bootselect options\n" 468 "\t-F treat device as a regular file\n" 469 "\t-S output as shell defines\n", 470 getprogname(), indent, "", indent, "", indent, ""); 471 exit(1); 472 } 473 474 static daddr_t 475 ext_offset(int part) 476 { 477 daddr_t offset = ext.base; 478 479 if (part != 0) 480 offset += le32toh(ext.ptn[part - 1].mbr_parts[1].mbrp_start); 481 return offset; 482 } 483 484 void 485 print_s0(int which) 486 { 487 int part; 488 489 if (which == -1) { 490 if (!sh_flag) 491 printf("Partition table:\n"); 492 for (part = 0; part < MBR_PART_COUNT; part++) { 493 if (!sh_flag) 494 printf("%d: ", part); 495 print_part(&mboot, part, 0); 496 } 497 if (!sh_flag) { 498 if (ext.is_corrupt) 499 printf("Extended partition table is currupt\n"); 500 else 501 if (ext.num_ptn != 0) 502 printf("Extended partition table:\n"); 503 } 504 for (part = 0; part < ext.num_ptn; part++) { 505 if (!sh_flag) 506 printf("E%d: ", part); 507 print_part(&ext.ptn[part], 0, ext_offset(part)); 508 if (!sh_flag && v_flag >= 2) { 509 printf("link: "); 510 print_mbr_partition(&ext.ptn[part], 1, 511 ext_offset(part), ext.base, 0); 512 } 513 } 514 #ifdef BOOTSEL 515 if (!sh_flag && 516 le16toh(mboot.mbr_bootsel_magic) == MBR_BS_MAGIC) { 517 int tmo; 518 519 printf("Bootselector "); 520 if (mboot.mbr_bootsel.mbrbs_flags & MBR_BS_ACTIVE) { 521 printf("enabled"); 522 tmo = le16toh(mboot.mbr_bootsel.mbrbs_timeo); 523 if (tmo == 0xffff) 524 printf(", infinite timeout"); 525 else 526 printf(", timeout %d seconds", 527 (10 * tmo + 9) / 182); 528 } else 529 printf("disabled"); 530 printf(".\n"); 531 } 532 #endif 533 return; 534 } 535 536 if (E_flag) { 537 if (!sh_flag) 538 printf("Extended partition E%d:\n", which); 539 if (which > ext.num_ptn) 540 printf("Undefined\n"); 541 else 542 print_part(&ext.ptn[which], 0, ext_offset(which)); 543 } else { 544 if (!sh_flag) 545 printf("Partition %d:\n", which); 546 print_part(&mboot, which, 0); 547 } 548 } 549 550 void 551 print_part(struct mbr_sector *boot, int part, daddr_t offset) 552 { 553 struct mbr_partition *partp; 554 char *e; 555 556 if (!sh_flag) { 557 print_mbr_partition(boot, part, offset, 0, 0); 558 return; 559 } 560 561 partp = &boot->mbr_parts[part]; 562 if (boot != &mboot) { 563 part = boot - ext.ptn; 564 e = "E"; 565 } else 566 e = ""; 567 568 if (partp->mbrp_type == 0) { 569 printf("PART%s%dSIZE=0\n", e, part); 570 return; 571 } 572 573 printf("PART%s%dID=%d\n", e, part, partp->mbrp_type); 574 printf("PART%s%dSIZE=%u\n", e, part, le32toh(partp->mbrp_size)); 575 printf("PART%s%dSTART=%"PRIdaddr"\n", e, part, 576 offset + le32toh(partp->mbrp_start)); 577 printf("PART%s%dFLAG=0x%x\n", e, part, partp->mbrp_flag); 578 printf("PART%s%dBCYL=%d\n", e, part, 579 MBR_PCYL(partp->mbrp_scyl, partp->mbrp_ssect)); 580 printf("PART%s%dBHEAD=%d\n", e, part, partp->mbrp_shd); 581 printf("PART%s%dBSEC=%d\n", e, part, MBR_PSECT(partp->mbrp_ssect)); 582 printf("PART%s%dECYL=%d\n", e, part, 583 MBR_PCYL(partp->mbrp_ecyl, partp->mbrp_esect)); 584 printf("PART%s%dEHEAD=%d\n", e, part, partp->mbrp_ehd); 585 printf("PART%s%dESEC=%d\n", e, part, MBR_PSECT(partp->mbrp_esect)); 586 } 587 588 static void 589 pr_cyls(daddr_t sector) 590 { 591 ulong cyl, head, sect; 592 cyl = sector / dos_cylindersectors; 593 sect = sector - cyl * dos_cylindersectors; 594 head = sect / dos_sectors; 595 sect -= head * dos_sectors; 596 597 printf("%lu", cyl); 598 if (head == 0 && sect == 0) 599 return; 600 printf("/%lu/%lu", head, sect + 1); 601 } 602 603 void 604 print_mbr_partition(struct mbr_sector *boot, int part, 605 daddr_t offset, daddr_t exoffset, int indent) 606 { 607 daddr_t start; 608 daddr_t size; 609 struct mbr_partition *partp = &boot->mbr_parts[part]; 610 struct mbr_sector eboot; 611 int p; 612 static int dumped = 0; 613 614 if (partp->mbrp_type == 0 && v_flag < 2) { 615 printf("<UNUSED>\n"); 616 return; 617 } 618 619 start = le32toh(partp->mbrp_start); 620 size = le32toh(partp->mbrp_size); 621 if (MBR_IS_EXTENDED(partp->mbrp_type)) 622 start += exoffset; 623 else 624 start += offset; 625 626 printf("%s (sysid %d)\n", get_type(partp->mbrp_type), partp->mbrp_type); 627 #ifdef BOOTSEL 628 if (le16toh(boot->mbr_bootsel_magic) == MBR_BS_MAGIC && 629 boot->mbr_bootsel.mbrbs_nametab[part][0]) 630 printf("%*s bootmenu: %s\n", indent, "", 631 boot->mbr_bootsel.mbrbs_nametab[part]); 632 #endif 633 634 printf("%*s start %"PRIdaddr", size %"PRIdaddr, 635 indent, "", start, size); 636 if (size != 0) { 637 printf(" (%u MB, Cyls ", SEC_TO_MB(size)); 638 if (v_flag == 0 && le32toh(partp->mbrp_start) == dos_sectors) 639 pr_cyls(start - dos_sectors); 640 else 641 pr_cyls(start); 642 printf("-"); 643 pr_cyls(start + size); 644 printf(")"); 645 } 646 647 switch (partp->mbrp_flag) { 648 case 0: 649 break; 650 case MBR_PFLAG_ACTIVE: 651 printf(", Active"); 652 break; 653 default: 654 printf(", flag 0x%x", partp->mbrp_flag); 655 break; 656 } 657 printf("\n"); 658 659 if (v_flag) { 660 printf("%*s beg: cylinder %4d, head %3d, sector %2d\n", 661 indent, "", 662 MBR_PCYL(partp->mbrp_scyl, partp->mbrp_ssect), 663 partp->mbrp_shd, MBR_PSECT(partp->mbrp_ssect)); 664 printf("%*s end: cylinder %4d, head %3d, sector %2d\n", 665 indent, "", 666 MBR_PCYL(partp->mbrp_ecyl, partp->mbrp_esect), 667 partp->mbrp_ehd, MBR_PSECT(partp->mbrp_esect)); 668 } 669 670 if (!MBR_IS_EXTENDED(partp->mbrp_type) || 671 (v_flag <= 2 && !ext.is_corrupt)) 672 return; 673 674 /* 675 * Recursive dump extended table, 676 * This is read from the disk - so is wrong during editing. 677 * Just ensure we only show it once. 678 */ 679 if (dumped) 680 return; 681 682 printf("%*s Extended partition table:\n", indent, ""); 683 indent += 4; 684 if (read_s0(start, &eboot) == -1) 685 return; 686 for (p = 0; p < MBR_PART_COUNT; p++) { 687 printf("%*s%d: ", indent, "", p); 688 print_mbr_partition(&eboot, p, start, 689 exoffset ? exoffset : start, indent); 690 } 691 692 if (exoffset == 0) 693 dumped = 1; 694 } 695 696 int 697 read_boot(const char *name, void *buf, size_t len, int err_exit) 698 { 699 int bfd, ret; 700 struct stat st; 701 702 if (boot_path != NULL) 703 free(boot_path); 704 if (strchr(name, '/') == 0) 705 asprintf(&boot_path, "%s/%s", boot_dir, name); 706 else 707 boot_path = strdup(name); 708 if (boot_path == NULL) 709 err(1, "Malloc failed"); 710 711 if ((bfd = open(boot_path, O_RDONLY)) < 0 || fstat(bfd, &st) == -1) { 712 warn("%s", boot_path); 713 goto fail; 714 } 715 716 if (st.st_size > (off_t)len) { 717 warnx("%s: bootcode too large", boot_path); 718 goto fail; 719 } 720 ret = st.st_size; 721 if (ret < 0x200) { 722 warnx("%s: bootcode too small", boot_path); 723 goto fail; 724 } 725 if (read(bfd, buf, len) != ret) { 726 warn("%s", boot_path); 727 goto fail; 728 } 729 730 /* 731 * Do some sanity checking here 732 */ 733 if (le16toh(((struct mbr_sector *)buf)->mbr_magic) != MBR_MAGIC) { 734 warnx("%s: invalid magic", boot_path); 735 goto fail; 736 } 737 738 close(bfd); 739 ret = (ret + 0x1ff) & ~0x1ff; 740 return ret; 741 742 fail: 743 close(bfd); 744 if (err_exit) 745 exit(1); 746 return 0; 747 } 748 749 void 750 init_sector0(int zappart) 751 { 752 int i; 753 int copy_size = MBR_PART_OFFSET; 754 755 #ifdef DEFAULT_BOOTCODE 756 if (bootsize == 0) 757 bootsize = read_boot(DEFAULT_BOOTCODE, bootcode, 758 sizeof bootcode, 1); 759 #endif 760 #ifdef BOOTSEL 761 if (le16toh(mboot.mbr_bootsel_magic) == MBR_BS_MAGIC 762 && le16toh(bootcode[0].mbr_bootsel_magic) == MBR_BS_MAGIC) 763 copy_size = MBR_BS_OFFSET; 764 #endif 765 766 if (bootsize != 0) { 767 boot_installed = 1; 768 memcpy(&mboot, bootcode, copy_size); 769 } 770 mboot.mbr_magic = htole16(MBR_MAGIC); 771 772 if (!zappart) 773 return; 774 for (i = 0; i < MBR_PART_COUNT; i++) 775 memset(&mboot.mbr_parts[i], 0, sizeof(mboot.mbr_parts[i])); 776 } 777 778 void 779 get_extended_ptn(void) 780 { 781 struct mbr_partition *mp; 782 struct mbr_sector *boot; 783 daddr_t offset; 784 struct mbr_sector *nptn; 785 786 /* find first (there should only be one) extended partition */ 787 for (mp = mboot.mbr_parts; !MBR_IS_EXTENDED(mp->mbrp_type); mp++) 788 if (mp >= &mboot.mbr_parts[MBR_PART_COUNT]) 789 return; 790 791 /* 792 * The extended partition should be structured as a linked list 793 * (even though it appears, at first glance, to be a tree). 794 */ 795 ext.base = le32toh(mp->mbrp_start); 796 ext.limit = ext.base + le32toh(mp->mbrp_size); 797 ext.ptn_id = mp - mboot.mbr_parts; 798 for (offset = 0;; offset = le32toh(boot->mbr_parts[1].mbrp_start)) { 799 nptn = realloc(ext.ptn, (ext.num_ptn + 1) * sizeof *ext.ptn); 800 if (nptn == NULL) 801 err(1, "Malloc failed"); 802 ext.ptn = nptn; 803 boot = ext.ptn + ext.num_ptn; 804 if (read_s0(offset + ext.base, boot) == -1) 805 break; 806 /* expect p0 to be valid and p1 to be another extended ptn */ 807 if (MBR_IS_EXTENDED(boot->mbr_parts[0].mbrp_type)) 808 break; 809 if (boot->mbr_parts[1].mbrp_type != 0 && 810 !MBR_IS_EXTENDED(boot->mbr_parts[1].mbrp_type)) 811 break; 812 /* p2 and p3 should be unallocated */ 813 if (boot->mbr_parts[2].mbrp_type != 0 || 814 boot->mbr_parts[3].mbrp_type != 0) 815 break; 816 /* data ptn inside extended one */ 817 if (boot->mbr_parts[0].mbrp_type != 0 && 818 offset + le32toh(boot->mbr_parts[0].mbrp_start) 819 + le32toh(boot->mbr_parts[0].mbrp_size) > ext.limit) 820 break; 821 822 ext.num_ptn++; 823 824 if (boot->mbr_parts[1].mbrp_type == 0) 825 /* end of extended partition chain */ 826 return; 827 /* must be in sector order */ 828 if (offset >= le32toh(boot->mbr_parts[1].mbrp_start)) 829 break; 830 } 831 832 warnx("Extended partition table is corrupt\n"); 833 ext.is_corrupt = 1; 834 ext.num_ptn = 0; 835 } 836 837 #if defined(__i386__) || defined(__x86_64__) 838 839 void 840 get_diskname(const char *fullname, char *diskname, size_t size) 841 { 842 const char *p, *p2; 843 size_t len; 844 845 p = strrchr(fullname, '/'); 846 if (p == NULL) 847 p = fullname; 848 else 849 p++; 850 851 if (*p == 0) { 852 strlcpy(diskname, fullname, size); 853 return; 854 } 855 856 if (*p == 'r') 857 p++; 858 859 for (p2 = p; *p2 != 0; p2++) 860 if (isdigit((unsigned char)*p2)) 861 break; 862 if (*p2 == 0) { 863 /* XXX invalid diskname? */ 864 strlcpy(diskname, fullname, size); 865 return; 866 } 867 while (isdigit((unsigned char)*p2)) 868 p2++; 869 870 len = p2 - p; 871 if (len > size) { 872 /* XXX */ 873 strlcpy(diskname, fullname, size); 874 return; 875 } 876 877 memcpy(diskname, p, len); 878 diskname[len] = 0; 879 } 880 881 void 882 get_geometry(void) 883 { 884 int mib[2], i; 885 size_t len; 886 struct biosdisk_info *bip; 887 struct nativedisk_info *nip; 888 char diskname[8]; 889 890 mib[0] = CTL_MACHDEP; 891 mib[1] = CPU_DISKINFO; 892 if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0) { 893 goto out; 894 } 895 dl = (struct disklist *) malloc(len); 896 if (sysctl(mib, 2, dl, &len, NULL, 0) < 0) { 897 free(dl); 898 dl = 0; 899 goto out; 900 } 901 902 get_diskname(disk, diskname, sizeof diskname); 903 904 for (i = 0; i < dl->dl_nnativedisks; i++) { 905 nip = &dl->dl_nativedisks[i]; 906 if (strcmp(diskname, nip->ni_devname)) 907 continue; 908 /* 909 * XXX listing possible matches is better. This is ok for 910 * now because the user has a chance to change it later. 911 * Also, if all the disks have the same parameters then we can 912 * just use them, we don't need to know which disk is which. 913 */ 914 if (nip->ni_nmatches != 0) { 915 bip = &dl->dl_biosdisks[nip->ni_biosmatches[0]]; 916 dos_cylinders = bip->bi_cyl; 917 dos_heads = bip->bi_head; 918 dos_sectors = bip->bi_sec; 919 if (bip->bi_lbasecs) 920 dos_disksectors = bip->bi_lbasecs; 921 return; 922 } 923 } 924 out: 925 /* Allright, allright, make a stupid guess.. */ 926 intuit_translated_geometry(); 927 } 928 #endif 929 930 #ifdef BOOTSEL 931 daddr_t 932 get_default_boot(void) 933 { 934 uint id; 935 int p; 936 937 if (le16toh(mboot.mbr_bootsel_magic) != MBR_BS_MAGIC) 938 /* default to first active partition */ 939 return DEFAULT_ACTIVE; 940 941 if (mboot.mbr_bootsel.mbrbs_defkey == SCAN_ENTER) 942 return DEFAULT_ACTIVE; 943 944 id = mboot.mbr_bootsel.mbrbs_defkey; 945 946 /* 1+ => allocated partition id, F1+ => disk 0+ */ 947 if (id >= SCAN_F1) 948 return id - SCAN_F1; 949 id -= SCAN_1; 950 951 for (p = 0; p < MBR_PART_COUNT; p++) { 952 if (mboot.mbr_parts[p].mbrp_type == 0) 953 continue; 954 if (mboot.mbr_bootsel.mbrbs_nametab[p][0] == 0) 955 continue; 956 if (id-- == 0) 957 return le32toh(mboot.mbr_parts[p].mbrp_start); 958 } 959 960 for (p = 0; p < ext.num_ptn; p++) { 961 if (ext.ptn[p].mbr_parts[0].mbrp_type == 0) 962 continue; 963 if (ext.ptn[p].mbr_bootsel.mbrbs_nametab[0][0] == 0) 964 continue; 965 if (id-- == 0) 966 return ext_offset(p) 967 + le32toh(ext.ptn[p].mbr_parts[0].mbrp_start); 968 } 969 970 return DEFAULT_ACTIVE; 971 } 972 973 void 974 set_default_boot(daddr_t default_ptn) 975 { 976 int p; 977 int key = SCAN_1; 978 979 if (le16toh(mboot.mbr_bootsel_magic) != MBR_BS_MAGIC) 980 /* sanity */ 981 return; 982 983 if (default_ptn == DEFAULT_ACTIVE) { 984 mboot.mbr_bootsel.mbrbs_defkey = SCAN_ENTER; 985 return; 986 } 987 988 for (p = 0; p < MBR_PART_COUNT; p++) { 989 if (mboot.mbr_parts[p].mbrp_type == 0) 990 continue; 991 if (mboot.mbr_bootsel.mbrbs_nametab[p][0] == 0) 992 continue; 993 if (le32toh(mboot.mbr_parts[p].mbrp_start) == default_ptn) { 994 mboot.mbr_bootsel.mbrbs_defkey = key; 995 return; 996 } 997 key++; 998 } 999 1000 if (mboot.mbr_bootsel.mbrbs_flags & MBR_BS_EXTLBA) { 1001 for (p = 0; p < ext.num_ptn; p++) { 1002 if (ext.ptn[p].mbr_parts[0].mbrp_type == 0) 1003 continue; 1004 if (ext.ptn[p].mbr_bootsel.mbrbs_nametab[0][0] == 0) 1005 continue; 1006 if (le32toh(ext.ptn[p].mbr_parts[0].mbrp_start) + 1007 ext_offset(p) == default_ptn) { 1008 mboot.mbr_bootsel.mbrbs_defkey = key; 1009 return; 1010 } 1011 key++; 1012 } 1013 } 1014 1015 if (default_ptn < 8) { 1016 key = SCAN_F1; 1017 mboot.mbr_bootsel.mbrbs_defkey = key + default_ptn; 1018 return; 1019 } 1020 1021 /* Default to first active partition */ 1022 mboot.mbr_bootsel.mbrbs_defkey = SCAN_ENTER; 1023 } 1024 1025 void 1026 install_bootsel(int needed) 1027 { 1028 struct mbr_bootsel *mbs = &mboot.mbr_bootsel; 1029 int p; 1030 int ext13 = 0; 1031 char *code; 1032 1033 needed |= MBR_BS_NEWMBR; /* need new bootsel code */ 1034 1035 /* Work out which boot code we need for this configuration */ 1036 for (p = 0; p < MBR_PART_COUNT; p++) { 1037 if (mboot.mbr_parts[p].mbrp_type == 0) 1038 continue; 1039 if (le16toh(mboot.mbr_bootsel_magic) != MBR_BS_MAGIC) 1040 break; 1041 if (mbs->mbrbs_nametab[p][0] == 0) 1042 continue; 1043 needed |= MBR_BS_ACTIVE; 1044 if (le32toh(mboot.mbr_parts[p].mbrp_start) >= dos_totalsectors) 1045 ext13 = MBR_BS_EXTINT13; 1046 } 1047 1048 for (p = 0; p < ext.num_ptn; p++) { 1049 if (le16toh(ext.ptn[p].mbr_bootsel_magic) != MBR_BS_MAGIC) 1050 continue; 1051 if (ext.ptn[p].mbr_parts[0].mbrp_type == 0) 1052 continue; 1053 if (ext.ptn[p].mbr_bootsel.mbrbs_nametab[p][0] == 0) 1054 continue; 1055 needed |= MBR_BS_EXTLBA | MBR_BS_ACTIVE; 1056 } 1057 1058 if (B_flag) 1059 needed |= MBR_BS_ACTIVE; 1060 1061 /* Is the installed code good enough ? */ 1062 if (!i_flag && (needed == 0 || 1063 (le16toh(mboot.mbr_bootsel_magic) == MBR_BS_MAGIC 1064 && (mbs->mbrbs_flags & needed) == needed))) { 1065 /* yes - just set flags */ 1066 mbs->mbrbs_flags |= ext13; 1067 return; 1068 } 1069 1070 /* ok - we need to replace the bootcode */ 1071 1072 if (f_flag && !(i_flag || B_flag)) { 1073 warnx("Installed bootfile doesn't support required options."); 1074 return; 1075 } 1076 1077 if (!f_flag && bootsize == 0 && !i_flag) 1078 /* Output an explanation for the 'update bootcode' prompt. */ 1079 printf("\n%s\n", 1080 "Installed bootfile doesn't support required options."); 1081 1082 /* Were we told a specific file ? (which we have already read) */ 1083 /* If so check that it supports what we need. */ 1084 if (bootsize != 0 && needed != 0 1085 && (le16toh(bootcode[0].mbr_bootsel_magic) != MBR_BS_MAGIC 1086 || ((bootcode[0].mbr_bootsel.mbrbs_flags & needed) != needed))) { 1087 /* No it doesn't... */ 1088 if (f_flag) 1089 warnx("Bootfile %s doesn't support " 1090 "required bootsel options", boot_path ); 1091 /* But install it anyway */ 1092 else 1093 if (yesno("Bootfile %s doesn't support the required " 1094 "options,\ninstall default bootfile instead?", 1095 boot_path)) 1096 bootsize = 0; 1097 } 1098 1099 if (bootsize == 0) { 1100 /* Get name of bootfile that supports the required facilities */ 1101 code = DEFAULT_BOOTCODE; 1102 if (needed & MBR_BS_ACTIVE) 1103 code = DEFAULT_BOOTSELCODE; 1104 #ifdef DEFAULT_BOOTEXTCODE 1105 if (needed & MBR_BS_EXTLBA) 1106 code = DEFAULT_BOOTEXTCODE; 1107 #endif 1108 1109 bootsize = read_boot(code, bootcode, sizeof bootcode, 0); 1110 if (bootsize == 0) 1111 /* The old bootcode is better than no bootcode at all */ 1112 return; 1113 if ((bootcode[0].mbr_bootsel.mbrbs_flags & needed) != needed) 1114 warnx("Default bootfile %s doesn't support required " 1115 "options. Got flags 0x%x, wanted 0x%x\n", 1116 boot_path, bootcode[0].mbr_bootsel.mbrbs_flags, 1117 needed); 1118 } 1119 1120 if (!f_flag && !yesno("Update the bootcode from %s?", boot_path)) 1121 return; 1122 1123 init_sector0(0); 1124 1125 if (le16toh(mboot.mbr_bootsel_magic) == MBR_BS_MAGIC) 1126 mbs->mbrbs_flags = bootcode[0].mbr_bootsel.mbrbs_flags | ext13; 1127 } 1128 1129 daddr_t 1130 configure_bootsel(daddr_t default_ptn) 1131 { 1132 struct mbr_bootsel *mbs = &mboot.mbr_bootsel; 1133 int i, item, opt; 1134 int tmo; 1135 daddr_t *off; 1136 int num_bios_disks; 1137 1138 if (dl != NULL) { 1139 num_bios_disks = dl->dl_nbiosdisks; 1140 if (num_bios_disks > 8) 1141 num_bios_disks = 8; 1142 } else 1143 num_bios_disks = 8; 1144 1145 printf("\nBoot selector configuration:\n"); 1146 1147 /* The timeout value is in ticks, ~18.2 Hz. Avoid using floats. 1148 * Ticks are nearly 64k/3600 - so our long timers are sligtly out! 1149 * Newer bootcode always waits for 1 tick, so treats 0xffff 1150 * as wait forever. 1151 */ 1152 tmo = le16toh(mbs->mbrbs_timeo); 1153 tmo = tmo == 0xffff ? -1 : (10 * tmo + 9) / 182; 1154 tmo = decimal("Timeout value (0 to 3600 seconds, -1 => never)", 1155 tmo, 0, -1, 3600); 1156 mbs->mbrbs_timeo = htole16(tmo == -1 ? 0xffff : (tmo * 182) / 10); 1157 1158 off = calloc(1 + MBR_PART_COUNT + ext.num_ptn + num_bios_disks, sizeof *off); 1159 if (off == NULL) 1160 err(1, "Malloc failed"); 1161 1162 printf("Select the default boot option. Options are:\n\n"); 1163 item = 0; 1164 opt = 0; 1165 off[opt] = DEFAULT_ACTIVE; 1166 printf("%d: The first active partition\n", opt); 1167 for (i = 0; i < MBR_PART_COUNT; i++) { 1168 if (mboot.mbr_parts[i].mbrp_type == 0) 1169 continue; 1170 if (mbs->mbrbs_nametab[i][0] == 0) 1171 continue; 1172 printf("%d: %s\n", ++opt, &mbs->mbrbs_nametab[i][0]); 1173 off[opt] = le32toh(mboot.mbr_parts[i].mbrp_start); 1174 if (off[opt] == default_ptn) 1175 item = opt; 1176 } 1177 if (mbs->mbrbs_flags & MBR_BS_EXTLBA) { 1178 for (i = 0; i < ext.num_ptn; i++) { 1179 if (ext.ptn[i].mbr_parts[0].mbrp_type == 0) 1180 continue; 1181 if (ext.ptn[i].mbr_bootsel.mbrbs_nametab[0][0] == 0) 1182 continue; 1183 printf("%d: %s\n", 1184 ++opt, ext.ptn[i].mbr_bootsel.mbrbs_nametab[0]); 1185 off[opt] = ext_offset(i) + 1186 le32toh(ext.ptn[i].mbr_parts[0].mbrp_start); 1187 if (off[opt] == default_ptn) 1188 item = opt; 1189 } 1190 } 1191 for (i = 0; i < num_bios_disks; i++) { 1192 printf("%d: Harddisk %d\n", ++opt, i); 1193 off[opt] = i; 1194 if (i == default_ptn) 1195 item = opt; 1196 } 1197 1198 item = decimal("Default boot option", item, 0, 0, opt); 1199 1200 default_ptn = off[item]; 1201 free(off); 1202 return default_ptn; 1203 } 1204 #endif /* BOOTSEL */ 1205 1206 1207 /* Prerequisite: the disklabel parameters and master boot record must 1208 * have been read (i.e. dos_* and mboot are meaningful). 1209 * Specification: modifies dos_cylinders, dos_heads, dos_sectors, and 1210 * dos_cylindersectors to be consistent with what the 1211 * partition table is using, if we can find a geometry 1212 * which is consistent with all partition table entries. 1213 * We may get the number of cylinders slightly wrong (in 1214 * the conservative direction). The idea is to be able 1215 * to create a NetBSD partition on a disk we don't know 1216 * the translated geometry of. 1217 * This routine is only used for non-x86 systems or when we fail to 1218 * get the BIOS geometry from the kernel. 1219 */ 1220 void 1221 intuit_translated_geometry(void) 1222 { 1223 int xcylinders = -1, xheads = -1, xsectors = -1, i, j; 1224 uint c1, h1, s1, c2, h2, s2; 1225 ulong a1, a2; 1226 uint64_t num, denom; 1227 1228 /* 1229 * The physical parameters may be invalid as bios geometry. 1230 * If we cannot determine the actual bios geometry, we are 1231 * better off picking a likely 'faked' geometry than leaving 1232 * the invalid physical one. 1233 */ 1234 1235 if (dos_cylinders > MAXCYL || dos_heads > MAXHEAD || 1236 dos_sectors > MAXSECTOR) { 1237 h1 = MAXHEAD - 1; 1238 c1 = MAXCYL - 1; 1239 #if defined(__i386__) || defined(__x86_64__) 1240 if (dl != NULL) { 1241 /* BIOS may use 256 heads or 1024 cylinders */ 1242 for (i = 0; i < dl->dl_nbiosdisks; i++) { 1243 if (h1 < dl->dl_biosdisks[i].bi_head) 1244 h1 = dl->dl_biosdisks[i].bi_head; 1245 if (c1 < dl->dl_biosdisks[i].bi_cyl) 1246 c1 = dl->dl_biosdisks[i].bi_cyl; 1247 } 1248 } 1249 #endif 1250 dos_sectors = MAXSECTOR; 1251 dos_heads = h1; 1252 dos_cylinders = disklabel.d_secperunit / (MAXSECTOR * h1); 1253 if (dos_cylinders > c1) 1254 dos_cylinders = c1; 1255 } 1256 1257 /* Try to deduce the number of heads from two different mappings. */ 1258 for (i = 0; i < MBR_PART_COUNT * 2 - 1; i++) { 1259 if (get_mapping(i, &c1, &h1, &s1, &a1) < 0) 1260 continue; 1261 a1 -= s1; 1262 for (j = i + 1; j < MBR_PART_COUNT * 2; j++) { 1263 if (get_mapping(j, &c2, &h2, &s2, &a2) < 0) 1264 continue; 1265 a2 -= s2; 1266 num = (uint64_t)h1 * a2 - (uint64_t)h2 * a1; 1267 denom = (uint64_t)c2 * a1 - (uint64_t)c1 * a2; 1268 if (denom != 0 && num % denom == 0) { 1269 xheads = num / denom; 1270 xsectors = a1 / (c1 * xheads + h1); 1271 break; 1272 } 1273 } 1274 if (xheads != -1) 1275 break; 1276 } 1277 1278 if (xheads == -1) 1279 return; 1280 1281 /* Estimate the number of cylinders. */ 1282 xcylinders = disklabel.d_secperunit / xheads / xsectors; 1283 if (disklabel.d_secperunit > xcylinders * xheads * xsectors) 1284 xcylinders++; 1285 1286 /* 1287 * Now verify consistency with each of the partition table entries. 1288 * Be willing to shove cylinders up a little bit to make things work, 1289 * but translation mismatches are fatal. 1290 */ 1291 for (i = 0; i < MBR_PART_COUNT * 2; i++) { 1292 if (get_mapping(i, &c1, &h1, &s1, &a1) < 0) 1293 continue; 1294 if (c1 >= MAXCYL - 2) 1295 continue; 1296 if (xsectors * (c1 * xheads + h1) + s1 != a1) 1297 return; 1298 } 1299 1300 1301 /* Everything checks out. 1302 * Reset the geometry to use for further calculations. 1303 * But cylinders cannot be > 1024. 1304 */ 1305 if (xcylinders > MAXCYL) 1306 dos_cylinders = MAXCYL; 1307 else 1308 dos_cylinders = xcylinders; 1309 dos_heads = xheads; 1310 dos_sectors = xsectors; 1311 } 1312 1313 /* 1314 * For the purposes of intuit_translated_geometry(), treat the partition 1315 * table as a list of eight mapping between (cylinder, head, sector) 1316 * triplets and absolute sectors. Get the relevant geometry triplet and 1317 * absolute sectors for a given entry, or return -1 if it isn't present. 1318 * Note: for simplicity, the returned sector is 0-based. 1319 */ 1320 int 1321 get_mapping(int i, uint *cylinder, uint *head, uint *sector, 1322 unsigned long *absolute) 1323 { 1324 struct mbr_partition *part = &mboot.mbr_parts[i / 2]; 1325 1326 if (part->mbrp_type == 0) 1327 return -1; 1328 if (i % 2 == 0) { 1329 *cylinder = MBR_PCYL(part->mbrp_scyl, part->mbrp_ssect); 1330 *head = part->mbrp_shd; 1331 *sector = MBR_PSECT(part->mbrp_ssect) - 1; 1332 *absolute = le32toh(part->mbrp_start); 1333 } else { 1334 *cylinder = MBR_PCYL(part->mbrp_ecyl, part->mbrp_esect); 1335 *head = part->mbrp_ehd; 1336 *sector = MBR_PSECT(part->mbrp_esect) - 1; 1337 *absolute = le32toh(part->mbrp_start) 1338 + le32toh(part->mbrp_size) - 1; 1339 } 1340 /* Sanity check the data against max values */ 1341 if ((((*cylinder * MAXHEAD) + *head) * MAXSECTOR + *sector) < *absolute) 1342 /* cannot be a CHS mapping */ 1343 return -1; 1344 return 0; 1345 } 1346 1347 static void 1348 delete_ptn(int part) 1349 { 1350 if (part == ext.ptn_id) { 1351 /* forget all about the extended partition */ 1352 free(ext.ptn); 1353 memset(&ext, 0, sizeof ext); 1354 } 1355 1356 mboot.mbr_parts[part].mbrp_type = 0; 1357 } 1358 1359 static void 1360 delete_ext_ptn(int part) 1361 { 1362 1363 if (part == 0) { 1364 ext.ptn[0].mbr_parts[0].mbrp_type = 0; 1365 return; 1366 } 1367 ext.ptn[part - 1].mbr_parts[1] = ext.ptn[part].mbr_parts[1]; 1368 memmove(&ext.ptn[part], &ext.ptn[part + 1], 1369 (ext.num_ptn - part - 1) * sizeof ext.ptn[0]); 1370 ext.num_ptn--; 1371 } 1372 1373 static int 1374 add_ext_ptn(daddr_t start, daddr_t size) 1375 { 1376 int part; 1377 struct mbr_partition *partp; 1378 struct mbr_sector *nptn; 1379 1380 nptn = realloc(ext.ptn, (ext.num_ptn + 1) * sizeof *ext.ptn); 1381 if (!nptn) 1382 err(1, "realloc"); 1383 ext.ptn = nptn; 1384 for (part = 0; part < ext.num_ptn; part++) 1385 if (ext_offset(part) > start) 1386 break; 1387 /* insert before 'part' - make space... */ 1388 memmove(&ext.ptn[part + 1], &ext.ptn[part], 1389 (ext.num_ptn - part) * sizeof ext.ptn[0]); 1390 memset(&ext.ptn[part], 0, sizeof ext.ptn[0]); 1391 ext.ptn[part].mbr_magic = htole16(MBR_MAGIC); 1392 /* we will be 'part' */ 1393 if (part == 0) { 1394 /* link us to 'next' */ 1395 partp = &ext.ptn[0].mbr_parts[1]; 1396 /* offset will be fixed by caller */ 1397 partp->mbrp_size = htole32( 1398 le32toh(ext.ptn[1].mbr_parts[0].mbrp_start) + 1399 le32toh(ext.ptn[1].mbr_parts[0].mbrp_size)); 1400 } else { 1401 /* link us to prev's next */ 1402 partp = &ext.ptn[part - 1].mbr_parts[1]; 1403 ext.ptn[part].mbr_parts[1] = *partp; 1404 /* and prev onto us */ 1405 partp->mbrp_start = htole32(start - dos_sectors - ext.base); 1406 partp->mbrp_size = htole32(size + dos_sectors); 1407 } 1408 partp->mbrp_type = 5; /* as used by win98 */ 1409 partp->mbrp_flag = 0; 1410 /* wallop in some CHS values - win98 doesn't saturate them */ 1411 dos(le32toh(partp->mbrp_start), 1412 &partp->mbrp_scyl, &partp->mbrp_shd, &partp->mbrp_ssect); 1413 dos(le32toh(partp->mbrp_start) + le32toh(partp->mbrp_size) - 1, 1414 &partp->mbrp_ecyl, &partp->mbrp_ehd, &partp->mbrp_esect); 1415 ext.num_ptn++; 1416 1417 return part; 1418 } 1419 1420 static const char * 1421 check_overlap(int part, int sysid, daddr_t start, daddr_t size, int fix) 1422 { 1423 int p; 1424 uint p_s, p_e; 1425 1426 if (sysid != 0) { 1427 if (start < dos_sectors) 1428 return "Track zero is reserved for the BIOS"; 1429 if (start + size > disksectors) 1430 return "Partition exceeds size of disk"; 1431 for (p = 0; p < MBR_PART_COUNT; p++) { 1432 if (p == part || mboot.mbr_parts[p].mbrp_type == 0) 1433 continue; 1434 p_s = le32toh(mboot.mbr_parts[p].mbrp_start); 1435 p_e = p_s + le32toh(mboot.mbr_parts[p].mbrp_size); 1436 if (start + size <= p_s || start >= p_e) 1437 continue; 1438 if (f_flag) { 1439 if (fix) 1440 delete_ptn(p); 1441 return 0; 1442 } 1443 return "Overlaps another partition"; 1444 } 1445 } 1446 1447 /* Are we trying to create an extended partition */ 1448 if (!MBR_IS_EXTENDED(mboot.mbr_parts[part].mbrp_type)) { 1449 /* this wasn't the extended partition */ 1450 if (!MBR_IS_EXTENDED(sysid)) 1451 return 0; 1452 /* making an extended partition */ 1453 if (ext.base != 0) { 1454 if (!f_flag) 1455 return "There cannot be 2 extended partitions"; 1456 if (fix) 1457 delete_ptn(ext.ptn_id); 1458 } 1459 if (fix) { 1460 /* allocate a new extended partition */ 1461 ext.ptn = calloc(1, sizeof ext.ptn[0]); 1462 if (ext.ptn == NULL) 1463 err(1, "Malloc failed"); 1464 ext.ptn[0].mbr_magic = htole16(MBR_MAGIC); 1465 ext.ptn_id = part; 1466 ext.base = start; 1467 ext.limit = start + size; 1468 ext.num_ptn = 1; 1469 } 1470 return 0; 1471 } 1472 1473 /* Check we haven't cut space allocated to an extended ptn */ 1474 1475 if (!MBR_IS_EXTENDED(sysid)) { 1476 /* no longer an extended partition */ 1477 if (fix) { 1478 /* Kill all memory of the extended partitions */ 1479 delete_ptn(part); 1480 return 0; 1481 } 1482 if (ext.num_ptn == 0 || 1483 (ext.num_ptn == 1 && ext.ptn[0].mbr_parts[0].mbrp_type == 0)) 1484 /* nothing in extended partition */ 1485 return 0; 1486 if (f_flag) 1487 return 0; 1488 if (yesno("Do you really want to delete all the extended partitions?")) 1489 return 0; 1490 return "Extended partition busy"; 1491 } 1492 1493 if (le32toh(mboot.mbr_parts[part].mbrp_start) != ext.base) 1494 /* maybe impossible, but an extra sanity check */ 1495 return 0; 1496 1497 for (p = ext.num_ptn; --p >= 0;) { 1498 if (ext.ptn[p].mbr_parts[0].mbrp_type == 0) 1499 continue; 1500 p_s = ext_offset(p); 1501 p_e = p_s + le32toh(ext.ptn[p].mbr_parts[0].mbrp_start) 1502 + le32toh(ext.ptn[p].mbr_parts[0].mbrp_size); 1503 if (p_s >= start && p_e <= start + size) 1504 continue; 1505 if (!f_flag) 1506 return "Extended partition outside main partition"; 1507 if (fix) 1508 delete_ext_ptn(p); 1509 } 1510 1511 if (fix && start != ext.base) { 1512 /* The internal offsets need to be fixed up */ 1513 for (p = 0; p < ext.num_ptn - 1; p++) 1514 ext.ptn[p].mbr_parts[1].mbrp_start = htole32( 1515 le32toh(ext.ptn[p].mbr_parts[1].mbrp_start) 1516 + ext.base - start); 1517 /* and maybe an empty partition at the start */ 1518 if (ext.ptn[0].mbr_parts[0].mbrp_type == 0) { 1519 if (le32toh(ext.ptn[0].mbr_parts[1].mbrp_start) == 0) { 1520 /* don't need the empty slot */ 1521 memmove(&ext.ptn[0], &ext.ptn[1], 1522 (ext.num_ptn - 1) * sizeof ext.ptn[0]); 1523 ext.num_ptn--; 1524 } 1525 } else { 1526 /* must create an empty slot */ 1527 add_ext_ptn(start, dos_sectors); 1528 ext.ptn[0].mbr_parts[1].mbrp_start = htole32(ext.base 1529 - start); 1530 } 1531 } 1532 if (fix) { 1533 ext.base = start; 1534 ext.limit = start + size; 1535 } 1536 return 0; 1537 } 1538 1539 static const char * 1540 check_ext_overlap(int part, int sysid, daddr_t start, daddr_t size, int fix) 1541 { 1542 int p; 1543 uint p_s, p_e; 1544 1545 if (sysid == 0) 1546 return 0; 1547 1548 if (MBR_IS_EXTENDED(sysid)) 1549 return "Nested extended partitions are not allowed"; 1550 1551 /* allow one track at start for extended partition header */ 1552 start -= dos_sectors; 1553 size += dos_sectors; 1554 if (start < ext.base || start + size > ext.limit) 1555 return "Outside bounds of extended partition"; 1556 1557 if (f_flag && !fix) 1558 return 0; 1559 1560 for (p = ext.num_ptn; --p >= 0;) { 1561 if (p == part || ext.ptn[p].mbr_parts[0].mbrp_type == 0) 1562 continue; 1563 p_s = ext_offset(p); 1564 p_e = p_s + le32toh(ext.ptn[p].mbr_parts[0].mbrp_start) 1565 + le32toh(ext.ptn[p].mbr_parts[0].mbrp_size); 1566 if (p == 0) 1567 p_s += le32toh(ext.ptn[p].mbr_parts[0].mbrp_start) 1568 - dos_sectors; 1569 if (start < p_e && start + size > p_s) { 1570 if (!f_flag) 1571 return "Overlaps another extended partition"; 1572 if (fix) { 1573 if (part == -1) 1574 delete_ext_ptn(p); 1575 else 1576 /* must not change numbering yet */ 1577 ext.ptn[p].mbr_parts[0].mbrp_type = 0; 1578 } 1579 } 1580 } 1581 return 0; 1582 } 1583 1584 int 1585 change_part(int extended, int part, int sysid, daddr_t start, daddr_t size, 1586 char *bootmenu) 1587 { 1588 struct mbr_partition *partp; 1589 struct mbr_sector *boot; 1590 daddr_t offset; 1591 char *e; 1592 int upart = part; 1593 int p; 1594 int fl; 1595 daddr_t n_s, n_e; 1596 const char *errtext; 1597 #ifdef BOOTSEL 1598 char tmp_bootmenu[MBR_PART_COUNT * (MBR_BS_PARTNAMESIZE + 1)]; 1599 int bootmenu_len = (extended ? MBR_PART_COUNT : 1) * (MBR_BS_PARTNAMESIZE + 1); 1600 #endif 1601 1602 if (extended) { 1603 if (part != -1 && part < ext.num_ptn) { 1604 boot = &ext.ptn[part]; 1605 partp = &boot->mbr_parts[0]; 1606 offset = ext_offset(part); 1607 } else { 1608 part = -1; 1609 boot = 0; 1610 partp = 0; 1611 offset = 0; 1612 } 1613 upart = 0; 1614 e = "E"; 1615 } else { 1616 boot = &mboot; 1617 partp = &boot->mbr_parts[part]; 1618 offset = 0; 1619 e = ""; 1620 } 1621 1622 if (!f_flag && part != -1) { 1623 printf("The data for partition %s%d is:\n", e, part); 1624 print_part(boot, upart, offset); 1625 } 1626 1627 #ifdef BOOTSEL 1628 if (bootmenu != NULL) 1629 strlcpy(tmp_bootmenu, bootmenu, bootmenu_len); 1630 else 1631 if (boot != NULL && 1632 le16toh(boot->mbr_bootsel_magic) == MBR_BS_MAGIC) 1633 strlcpy(tmp_bootmenu, 1634 boot->mbr_bootsel.mbrbs_nametab[upart], 1635 bootmenu_len); 1636 else 1637 tmp_bootmenu[0] = 0; 1638 #endif 1639 1640 if (!s_flag && partp != NULL) { 1641 /* values not specified, default to current ones */ 1642 sysid = partp->mbrp_type; 1643 start = offset + le32toh(partp->mbrp_start); 1644 size = le32toh(partp->mbrp_size); 1645 } 1646 1647 /* creating a new partition, default to free space */ 1648 if (!s_flag && sysid == 0 && extended) { 1649 /* non-extended partition */ 1650 start = ext.base; 1651 for (p = 0; p < ext.num_ptn; p++) { 1652 if (ext.ptn[p].mbr_parts[0].mbrp_type == 0) 1653 continue; 1654 n_s = ext_offset(p); 1655 if (n_s > start + dos_sectors) 1656 break; 1657 start = ext_offset(p) 1658 + le32toh(ext.ptn[p].mbr_parts[0].mbrp_start) 1659 + le32toh(ext.ptn[p].mbr_parts[0].mbrp_size); 1660 } 1661 if (ext.limit - start <= dos_sectors) { 1662 printf("No space in extended partition\n"); 1663 return 0; 1664 } 1665 start += dos_sectors; 1666 } 1667 1668 if (!s_flag && sysid == 0 && !extended) { 1669 /* same for non-extended partition */ 1670 /* first see if old start is free */ 1671 if (start < dos_sectors) 1672 start = 0; 1673 for (p = 0; start != 0 && p < MBR_PART_COUNT; p++) { 1674 if (mboot.mbr_parts[p].mbrp_type == 0) 1675 continue; 1676 n_s = le32toh(mboot.mbr_parts[p].mbrp_start); 1677 if (start >= n_s && 1678 start < n_s + le32toh(mboot.mbr_parts[p].mbrp_size)) 1679 start = 0; 1680 } 1681 if (start == 0) { 1682 /* Look for first gap */ 1683 start = dos_sectors; 1684 for (p = 0; p < MBR_PART_COUNT; p++) { 1685 if (mboot.mbr_parts[p].mbrp_type == 0) 1686 continue; 1687 n_s = le32toh(mboot.mbr_parts[p].mbrp_start); 1688 n_e = n_s + le32toh(mboot.mbr_parts[p].mbrp_size); 1689 if (start >= n_s && start < n_e) { 1690 start = n_e; 1691 p = -1; 1692 } 1693 } 1694 if (start >= disksectors) { 1695 printf("No free space\n"); 1696 return 0; 1697 } 1698 } 1699 } 1700 1701 if (!f_flag) { 1702 /* request new values from user */ 1703 if (sysid == 0) 1704 sysid = 169; 1705 sysid = decimal("sysid", sysid, 0, 0, 255); 1706 if (sysid == 0 && !v_flag) { 1707 start = 0; 1708 size = 0; 1709 #ifdef BOOTSEL 1710 tmp_bootmenu[0] = 0; 1711 #endif 1712 } else { 1713 daddr_t old = start; 1714 daddr_t lim = extended ? ext.limit : disksectors; 1715 start = decimal("start", start, 1716 DEC_SEC | DEC_RND_0 | (extended ? DEC_RND : 0), 1717 extended ? ext.base : 0, lim); 1718 /* Adjust 'size' so that end doesn't move when 'start' 1719 * is only changed slightly. 1720 */ 1721 if (size > start - old) 1722 size -= start - old; 1723 else 1724 size = 0; 1725 /* Find end of available space from this start point */ 1726 if (extended) { 1727 for (p = 0; p < ext.num_ptn; p++) { 1728 if (p == part) 1729 continue; 1730 if (ext.ptn[p].mbr_parts[0].mbrp_type == 0) 1731 continue; 1732 n_s = ext_offset(p); 1733 if (n_s > start && n_s < lim) 1734 lim = n_s; 1735 if (start >= n_s && start < n_s 1736 + le32toh(ext.ptn[p].mbr_parts[0].mbrp_start) 1737 + le32toh(ext.ptn[p].mbr_parts[0].mbrp_size)) { 1738 lim = start; 1739 break; 1740 } 1741 } 1742 } else { 1743 for (p = 0; p < MBR_PART_COUNT; p++) { 1744 if (p == part) 1745 continue; 1746 if (mboot.mbr_parts[p].mbrp_type == 0) 1747 continue; 1748 n_s = le32toh(mboot.mbr_parts[p].mbrp_start); 1749 if (n_s > start && n_s < lim) 1750 lim = n_s; 1751 if (start >= n_s && start < n_s 1752 + le32toh(mboot.mbr_parts[p].mbrp_size)) { 1753 lim = start; 1754 break; 1755 } 1756 } 1757 } 1758 lim -= start; 1759 if (lim == 0) { 1760 printf("Start sector already allocated\n"); 1761 return 0; 1762 } 1763 if (size == 0 || size > lim) 1764 size = lim; 1765 fl = DEC_SEC; 1766 if (start % dos_cylindersectors == dos_sectors) 1767 fl |= DEC_RND_DOWN; 1768 if (start == 2 * dos_sectors) 1769 fl |= DEC_RND_DOWN | DEC_RND_DOWN_2; 1770 size = decimal("size", size, fl, 0, lim); 1771 #ifdef BOOTSEL 1772 #ifndef DEFAULT_BOOTEXTCODE 1773 if (!extended) 1774 #endif 1775 string("bootmenu", bootmenu_len, tmp_bootmenu); 1776 #endif 1777 } 1778 } 1779 1780 /* 1781 * Before we write these away, we must verify that nothing 1782 * untoward has been requested. 1783 */ 1784 1785 if (extended) 1786 errtext = check_ext_overlap(part, sysid, start, size, 0); 1787 else 1788 errtext = check_overlap(part, sysid, start, size, 0); 1789 if (errtext != NULL) { 1790 if (f_flag) 1791 errx(2, "%s\n", errtext); 1792 printf("%s\n", errtext); 1793 return 0; 1794 } 1795 1796 /* 1797 * Before proceeding, delete any overlapped partitions. 1798 * This can only happen if '-f' was supplied on the command line. 1799 * Just hope the caller knows what they are doing. 1800 * This also fixes the base of each extended partition if the 1801 * partition itself has moved. 1802 */ 1803 1804 if (extended) 1805 errtext = check_ext_overlap(part, sysid, start, size, 1); 1806 else 1807 errtext = check_overlap(part, sysid, start, size, 1); 1808 1809 if (errtext) 1810 errx(1, "%s\n", errtext); 1811 1812 if (sysid == 0) { 1813 /* delete this partition - save info though */ 1814 if (partp == NULL) 1815 /* must have been trying to create an extended ptn */ 1816 return 0; 1817 if (start == 0 && size == 0) 1818 memset(partp, 0, sizeof *partp); 1819 #ifdef BOOTSEL 1820 if (le16toh(boot->mbr_bootsel_magic) == MBR_BS_MAGIC) 1821 memset(boot->mbr_bootsel.mbrbs_nametab[upart], 0, 1822 sizeof boot->mbr_bootsel.mbrbs_nametab[0]); 1823 #endif 1824 if (extended) 1825 delete_ext_ptn(part); 1826 else 1827 delete_ptn(part); 1828 return 1; 1829 } 1830 1831 1832 if (extended) { 1833 if (part != -1) 1834 delete_ext_ptn(part); 1835 if (start == ext.base + dos_sectors) 1836 /* First one must have been free */ 1837 part = 0; 1838 else 1839 part = add_ext_ptn(start, size); 1840 1841 /* These must be re-calculated because of the realloc */ 1842 boot = &ext.ptn[part]; 1843 partp = &boot->mbr_parts[0]; 1844 offset = ext_offset(part); 1845 } 1846 1847 partp->mbrp_type = sysid; 1848 partp->mbrp_start = htole32( start - offset); 1849 partp->mbrp_size = htole32( size); 1850 dos(start, &partp->mbrp_scyl, &partp->mbrp_shd, &partp->mbrp_ssect); 1851 dos(start + size - 1, 1852 &partp->mbrp_ecyl, &partp->mbrp_ehd, &partp->mbrp_esect); 1853 #ifdef BOOTSEL 1854 if (extended) { 1855 boot->mbr_bootsel_magic = htole16(MBR_BS_MAGIC); 1856 strncpy(boot->mbr_bootsel.mbrbs_nametab[upart], tmp_bootmenu, 1857 bootmenu_len); 1858 } else { 1859 /* We need to bootselect code installed in order to have 1860 * somewhere to safely write the menu tag. 1861 */ 1862 if (le16toh(boot->mbr_bootsel_magic) != MBR_BS_MAGIC) { 1863 if (yesno("The bootselect code is not installed, " 1864 "do you want to install it now?")) 1865 install_bootsel(MBR_BS_ACTIVE); 1866 } 1867 if (le16toh(boot->mbr_bootsel_magic) == MBR_BS_MAGIC) { 1868 strncpy(boot->mbr_bootsel.mbrbs_nametab[upart], 1869 tmp_bootmenu, bootmenu_len); 1870 } 1871 } 1872 #endif 1873 1874 if (v_flag && !f_flag && yesno("Explicitly specify beg/end address?")) { 1875 /* this really isn't a good idea.... */ 1876 int tsector, tcylinder, thead; 1877 1878 tcylinder = MBR_PCYL(partp->mbrp_scyl, partp->mbrp_ssect); 1879 thead = partp->mbrp_shd; 1880 tsector = MBR_PSECT(partp->mbrp_ssect); 1881 tcylinder = decimal("beginning cylinder", 1882 tcylinder, 0, 0, dos_cylinders - 1); 1883 thead = decimal("beginning head", 1884 thead, 0, 0, dos_heads - 1); 1885 tsector = decimal("beginning sector", 1886 tsector, 0, 1, dos_sectors); 1887 partp->mbrp_scyl = DOSCYL(tcylinder); 1888 partp->mbrp_shd = thead; 1889 partp->mbrp_ssect = DOSSECT(tsector, tcylinder); 1890 1891 tcylinder = MBR_PCYL(partp->mbrp_ecyl, partp->mbrp_esect); 1892 thead = partp->mbrp_ehd; 1893 tsector = MBR_PSECT(partp->mbrp_esect); 1894 tcylinder = decimal("ending cylinder", 1895 tcylinder, 0, 0, dos_cylinders - 1); 1896 thead = decimal("ending head", 1897 thead, 0, 0, dos_heads - 1); 1898 tsector = decimal("ending sector", 1899 tsector, 0, 1, dos_sectors); 1900 partp->mbrp_ecyl = DOSCYL(tcylinder); 1901 partp->mbrp_ehd = thead; 1902 partp->mbrp_esect = DOSSECT(tsector, tcylinder); 1903 } 1904 1905 /* If we had to mark an extended partition as deleted because 1906 * another request would have overlapped it, now is the time 1907 * to do the actual delete. 1908 */ 1909 if (extended && f_flag) { 1910 for (p = ext.num_ptn; --p >= 0;) 1911 if (ext.ptn[p].mbr_parts[0].mbrp_type == 0) 1912 delete_ext_ptn(p); 1913 } 1914 return 1; 1915 } 1916 1917 void 1918 print_params(void) 1919 { 1920 1921 if (sh_flag) { 1922 printf("DISK=%s\n", disk); 1923 printf("DLCYL=%d\nDLHEAD=%d\nDLSEC=%d\nDLSIZE=%"PRIdaddr"\n", 1924 cylinders, heads, sectors, disksectors); 1925 printf("BCYL=%d\nBHEAD=%d\nBSEC=%d\nBDLSIZE=%"PRIdaddr"\n", 1926 dos_cylinders, dos_heads, dos_sectors, dos_disksectors); 1927 printf("NUMEXTPTN=%d\n", ext.num_ptn); 1928 return; 1929 } 1930 1931 /* Not sh_flag */ 1932 printf("Disk: %s\n", disk); 1933 printf("NetBSD disklabel disk geometry:\n"); 1934 printf("cylinders: %d, heads: %d, sectors/track: %d " 1935 "(%d sectors/cylinder)\ntotal sectors: %"PRIdaddr"\n\n", 1936 cylinders, heads, sectors, cylindersectors, disksectors); 1937 printf("BIOS disk geometry:\n"); 1938 printf("cylinders: %d, heads: %d, sectors/track: %d " 1939 "(%d sectors/cylinder)\ntotal sectors: %"PRIdaddr"\n\n", 1940 dos_cylinders, dos_heads, dos_sectors, dos_cylindersectors, 1941 dos_disksectors); 1942 } 1943 1944 void 1945 change_active(int which) 1946 { 1947 struct mbr_partition *partp; 1948 int part; 1949 int active = MBR_PART_COUNT; 1950 1951 partp = &mboot.mbr_parts[0]; 1952 1953 if (a_flag && which != -1) 1954 active = which; 1955 else { 1956 for (part = 0; part < MBR_PART_COUNT; part++) 1957 if (partp[part].mbrp_flag & MBR_PFLAG_ACTIVE) 1958 active = part; 1959 } 1960 if (!f_flag) { 1961 if (yesno("Do you want to change the active partition?")) { 1962 printf ("Choosing %d will make no partition active.\n", 1963 MBR_PART_COUNT); 1964 do { 1965 active = decimal("active partition", 1966 active, 0, 0, MBR_PART_COUNT); 1967 } while (!yesno("Are you happy with this choice?")); 1968 } else 1969 return; 1970 } else 1971 if (active != MBR_PART_COUNT) 1972 printf ("Making partition %d active.\n", active); 1973 1974 for (part = 0; part < MBR_PART_COUNT; part++) 1975 partp[part].mbrp_flag &= ~MBR_PFLAG_ACTIVE; 1976 if (active < MBR_PART_COUNT) 1977 partp[active].mbrp_flag |= MBR_PFLAG_ACTIVE; 1978 } 1979 1980 void 1981 get_params_to_use(void) 1982 { 1983 #if defined(__i386__) || defined(__x86_64__) 1984 struct biosdisk_info *bip; 1985 int i; 1986 #endif 1987 1988 if (b_flag) { 1989 dos_cylinders = b_cyl; 1990 dos_heads = b_head; 1991 dos_sectors = b_sec; 1992 return; 1993 } 1994 1995 print_params(); 1996 if (!yesno("Do you want to change our idea of what BIOS thinks?")) 1997 return; 1998 1999 #if defined(__i386__) || defined(__x86_64__) 2000 if (dl != NULL) { 2001 for (i = 0; i < dl->dl_nbiosdisks; i++) { 2002 if (i == 0) 2003 printf("\nGeometries of known disks:\n"); 2004 bip = &dl->dl_biosdisks[i]; 2005 printf("Disk %d: cylinders %u, heads %u, sectors %u" 2006 " (%"PRIdaddr" sectors, %dMB)\n", 2007 i, bip->bi_cyl, bip->bi_head, bip->bi_sec, 2008 bip->bi_lbasecs, SEC_TO_MB(bip->bi_lbasecs)); 2009 2010 } 2011 printf("\n"); 2012 } 2013 #endif 2014 do { 2015 dos_cylinders = decimal("BIOS's idea of #cylinders", 2016 dos_cylinders, 0, 0, MAXCYL); 2017 dos_heads = decimal("BIOS's idea of #heads", 2018 dos_heads, 0, 0, MAXHEAD); 2019 dos_sectors = decimal("BIOS's idea of #sectors", 2020 dos_sectors, 0, 1, MAXSECTOR); 2021 print_params(); 2022 } while (!yesno("Are you happy with this choice?")); 2023 } 2024 2025 2026 /***********************************************\ 2027 * Change real numbers into strange dos numbers * 2028 \***********************************************/ 2029 void 2030 dos(int sector, unsigned char *cylinderp, unsigned char *headp, 2031 unsigned char *sectorp) 2032 { 2033 int cylinder, head; 2034 2035 cylinder = sector / dos_cylindersectors; 2036 sector -= cylinder * dos_cylindersectors; 2037 2038 head = sector / dos_sectors; 2039 sector -= head * dos_sectors; 2040 if (cylinder > 1023) 2041 cylinder = 1023; 2042 2043 *cylinderp = DOSCYL(cylinder); 2044 *headp = head; 2045 *sectorp = DOSSECT(sector + 1, cylinder); 2046 } 2047 2048 int 2049 open_disk(int update) 2050 { 2051 static char namebuf[MAXPATHLEN + 1]; 2052 2053 fd = opendisk(disk, update && disk_file == NULL ? O_RDWR : O_RDONLY, 2054 namebuf, sizeof(namebuf), 0); 2055 if (fd < 0) { 2056 if (errno == ENODEV) 2057 warnx("%s is not a character device", namebuf); 2058 else 2059 warn("%s", namebuf); 2060 return (-1); 2061 } 2062 disk = namebuf; 2063 if (get_params() == -1) { 2064 close(fd); 2065 fd = -1; 2066 return (-1); 2067 } 2068 if (disk_file != NULL) { 2069 /* for testing: read/write data from a disk file */ 2070 wfd = open(disk_file, update ? O_RDWR|O_CREAT : O_RDONLY, 0777); 2071 if (wfd == -1) { 2072 warn("%s", disk_file); 2073 close(fd); 2074 fd = -1; 2075 return -1; 2076 } 2077 } else 2078 wfd = fd; 2079 return (0); 2080 } 2081 2082 int 2083 read_disk(daddr_t sector, void *buf) 2084 { 2085 2086 if (*rfd == -1) 2087 errx(1, "read_disk(); fd == -1"); 2088 if (lseek(*rfd, sector * (off_t)512, 0) == -1) 2089 return (-1); 2090 return (read(*rfd, buf, 512)); 2091 } 2092 2093 int 2094 write_disk(daddr_t sector, void *buf) 2095 { 2096 2097 if (wfd == -1) 2098 errx(1, "write_disk(); wfd == -1"); 2099 if (lseek(wfd, sector * (off_t)512, 0) == -1) 2100 return (-1); 2101 return (write(wfd, buf, 512)); 2102 } 2103 2104 static void 2105 guess_geometry(daddr_t _sectors) 2106 { 2107 dos_sectors = MAXSECTOR; 2108 dos_heads = MAXHEAD - 1; /* some BIOS might use 256 */ 2109 dos_cylinders = _sectors / (MAXSECTOR * (MAXHEAD - 1)); 2110 if (dos_cylinders < 1) 2111 dos_cylinders = 1; 2112 else if (dos_cylinders > MAXCYL - 1) 2113 dos_cylinders = MAXCYL - 1; 2114 } 2115 2116 int 2117 get_params(void) 2118 { 2119 if (disk_type != NULL) { 2120 struct disklabel *tmplabel; 2121 2122 if ((tmplabel = getdiskbyname(disk_type)) == NULL) { 2123 warn("bad disktype"); 2124 return (-1); 2125 } 2126 disklabel = *tmplabel; 2127 } else if (F_flag) { 2128 struct stat st; 2129 if (fstat(fd, &st) == -1) { 2130 warn("fstat"); 2131 return (-1); 2132 } 2133 if (st.st_size % 512 != 0) { 2134 warnx("%s size (%lld) is not divisible " 2135 "by sector size (%d)", disk, (long long)st.st_size, 2136 512); 2137 } 2138 disklabel.d_secperunit = st.st_size / 512; 2139 guess_geometry(disklabel.d_secperunit); 2140 disklabel.d_ncylinders = dos_cylinders; 2141 disklabel.d_ntracks = dos_heads; 2142 disklabel.d_nsectors = dos_sectors; 2143 } else if (ioctl(fd, DIOCGDEFLABEL, &disklabel) == -1) { 2144 warn("DIOCGDEFLABEL"); 2145 if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) { 2146 warn("DIOCGDINFO"); 2147 return (-1); 2148 } 2149 } 2150 disksectors = disklabel.d_secperunit; 2151 cylinders = disklabel.d_ncylinders; 2152 heads = disklabel.d_ntracks; 2153 sectors = disklabel.d_nsectors; 2154 2155 /* pick up some defaults for the BIOS sizes */ 2156 if (sectors <= MAXSECTOR) { 2157 dos_cylinders = cylinders; 2158 dos_heads = heads; 2159 dos_sectors = sectors; 2160 } else { 2161 /* guess - has to better than the above */ 2162 guess_geometry(disksectors); 2163 } 2164 dos_disksectors = disksectors; 2165 2166 return (0); 2167 } 2168 2169 #ifdef BOOTSEL 2170 /* 2171 * Rather unfortunately the bootsel 'magic' number is at the end of the 2172 * the structure, and there is no checksum. So when other operating 2173 * systems install mbr code by only writing the length of their code they 2174 * can overwrite part of the structure but keeping the magic number intact. 2175 * This code attempts to empirically detect this problem. 2176 */ 2177 static int 2178 validate_bootsel(struct mbr_bootsel *mbs) 2179 { 2180 uint key = mbs->mbrbs_defkey; 2181 uint tmo; 2182 int i; 2183 2184 if (v_flag) 2185 return 0; 2186 2187 /* 2188 * Check default key is sane 2189 * - this is the most likely field to be stuffed 2190 * 12 disks and 12 bootable partitions seems enough! 2191 * (the keymap decode starts falling apart at that point) 2192 */ 2193 if (key != 0 && !(key == SCAN_ENTER 2194 || (key >= SCAN_1 && key < SCAN_1 + 12) 2195 || (key >= SCAN_F1 && key < SCAN_F1 + 12))) 2196 return 1; 2197 2198 /* Checking the flags will lead to breakage... */ 2199 2200 /* Timeout value is expecyed to be a multiple of a second */ 2201 tmo = htole16(mbs->mbrbs_timeo); 2202 if (tmo != 0 && tmo != 0xffff && tmo != (10 * tmo + 9) / 182 * 182 / 10) 2203 return 2; 2204 2205 /* Check the menu strings are printable */ 2206 /* Unfortunately they aren't zero filled... */ 2207 for (i = 0; i < sizeof(mbs->mbrbs_nametab); i++) { 2208 int c = (uint8_t)mbs->mbrbs_nametab[0][i]; 2209 if (c == 0 || isprint(c)) 2210 continue; 2211 return 3; 2212 } 2213 2214 return 0; 2215 } 2216 #endif 2217 2218 int 2219 read_s0(daddr_t offset, struct mbr_sector *boot) 2220 { 2221 const char *tabletype = offset ? "extended" : "primary"; 2222 #ifdef BOOTSEL 2223 static int reported; 2224 #endif 2225 2226 if (read_disk(offset, boot) == -1) { 2227 warn("Can't read %s partition table", tabletype); 2228 return -1; 2229 } 2230 if (le16toh(boot->mbr_magic) != MBR_MAGIC) { 2231 warnx("%s partition table invalid, " 2232 "no magic in sector %"PRIdaddr, tabletype, offset); 2233 return -1; 2234 2235 } 2236 #ifdef BOOTSEL 2237 if (le16toh(boot->mbr_bootsel_magic) == MBR_BS_MAGIC) { 2238 /* mbr_bootsel in new location */ 2239 if (validate_bootsel(&boot->mbr_bootsel)) { 2240 warnx("removing corrupt bootsel information"); 2241 boot->mbr_bootsel_magic = 0; 2242 } 2243 return 0; 2244 } 2245 if (le16toh(boot->mbr_bootsel_magic) != MBR_MAGIC) 2246 return 0; 2247 2248 /* mbr_bootsel in old location */ 2249 if (!reported) 2250 warnx("%s partition table: using old-style bootsel information", 2251 tabletype); 2252 reported = 1; 2253 if (validate_bootsel((void *)((uint8_t *)boot + MBR_BS_OFFSET + 4))) { 2254 warnx("%s bootsel information corrupt - ignoring", tabletype); 2255 return 0; 2256 } 2257 memmove((u_int8_t *)boot + MBR_BS_OFFSET, 2258 (u_int8_t *)boot + MBR_BS_OFFSET + 4, 2259 sizeof(struct mbr_bootsel)); 2260 if ( ! (boot->mbr_bootsel.mbrbs_flags & MBR_BS_NEWMBR)) { 2261 /* old style default key */ 2262 int id; 2263 /* F1..F4 => ptn 0..3, F5+ => disk 0+ */ 2264 id = boot->mbr_bootsel.mbrbs_defkey; 2265 id -= SCAN_F1; 2266 if (id >= MBR_PART_COUNT) 2267 id -= MBR_PART_COUNT; /* Use number of disk */ 2268 else if (mboot.mbr_parts[id].mbrp_type != 0) 2269 id = le32toh(boot->mbr_parts[id].mbrp_start); 2270 else 2271 id = DEFAULT_ACTIVE; 2272 boot->mbr_bootsel.mbrbs_defkey = id; 2273 } 2274 boot->mbr_bootsel_magic = htole16(MBR_BS_MAGIC); 2275 /* highlight that new bootsel code is necessary */ 2276 boot->mbr_bootsel.mbrbs_flags &= ~MBR_BS_NEWMBR; 2277 #endif /* BOOTSEL */ 2278 return 0; 2279 } 2280 2281 int 2282 write_mbr(void) 2283 { 2284 int flag, i; 2285 daddr_t offset; 2286 int rval = -1; 2287 2288 /* 2289 * write enable label sector before write (if necessary), 2290 * disable after writing. 2291 * needed if the disklabel protected area also protects 2292 * sector 0. (e.g. empty disk) 2293 */ 2294 flag = 1; 2295 if (wfd == fd && F_flag == 0 && ioctl(wfd, DIOCWLABEL, &flag) < 0) 2296 warn("DIOCWLABEL"); 2297 if (write_disk(0, &mboot) == -1) { 2298 warn("Can't write fdisk partition table"); 2299 goto protect_label; 2300 } 2301 if (boot_installed) 2302 for (i = bootsize; (i -= 0x200) > 0;) 2303 if (write_disk(i / 0x200, &bootcode[i / 0x200]) == -1) { 2304 warn("Can't write bootcode"); 2305 goto protect_label; 2306 } 2307 for (offset = 0, i = 0; i < ext.num_ptn; i++) { 2308 if (write_disk(ext.base + offset, ext.ptn + i) == -1) { 2309 warn("Can't write %dth extended partition", i); 2310 goto protect_label; 2311 } 2312 offset = le32toh(ext.ptn[i].mbr_parts[1].mbrp_start); 2313 } 2314 rval = 0; 2315 protect_label: 2316 flag = 0; 2317 if (wfd == fd && F_flag == 0 && ioctl(wfd, DIOCWLABEL, &flag) < 0) 2318 warn("DIOCWLABEL"); 2319 return rval; 2320 } 2321 2322 int 2323 yesno(const char *str, ...) 2324 { 2325 int ch, first; 2326 va_list ap; 2327 2328 va_start(ap, str); 2329 2330 vprintf(str, ap); 2331 printf(" [n] "); 2332 2333 first = ch = getchar(); 2334 while (ch != '\n' && ch != EOF) 2335 ch = getchar(); 2336 if (ch == EOF) 2337 errx(1, "EOF"); 2338 return (first == 'y' || first == 'Y'); 2339 } 2340 2341 int 2342 decimal(const char *prompt, int dflt, int flags, int minval, int maxval) 2343 { 2344 int acc = 0; 2345 char *cp; 2346 2347 for (;;) { 2348 if (flags & DEC_SEC) { 2349 printf("%s: [%d..%dcyl default: %d, %dcyl, %uMB] ", 2350 prompt, SEC_TO_CYL(minval), SEC_TO_CYL(maxval), 2351 dflt, SEC_TO_CYL(dflt), SEC_TO_MB(dflt)); 2352 } else 2353 printf("%s: [%d..%d default: %d] ", 2354 prompt, minval, maxval, dflt); 2355 2356 if (!fgets(lbuf, LBUF, stdin)) 2357 errx(1, "EOF"); 2358 lbuf[strlen(lbuf)-1] = '\0'; 2359 cp = lbuf; 2360 2361 cp += strspn(cp, " \t"); 2362 if (*cp == '\0') 2363 return dflt; 2364 2365 if (cp[0] == '$' && cp[1] == 0) 2366 return maxval; 2367 2368 if (isdigit((unsigned char)*cp) || *cp == '-') { 2369 acc = strtol(lbuf, &cp, 10); 2370 if (flags & DEC_SEC) { 2371 if (*cp == 'm' || *cp == 'M') { 2372 acc *= SEC_IN_1M; 2373 /* round to whole number of cylinders */ 2374 acc += dos_cylindersectors / 2; 2375 acc /= dos_cylindersectors; 2376 cp = "c"; 2377 } 2378 if (*cp == 'c' || *cp == 'C') { 2379 cp = ""; 2380 acc *= dos_cylindersectors; 2381 /* adjustments for cylinder boundary */ 2382 if (acc == 0 && flags & DEC_RND_0) 2383 acc += dos_sectors; 2384 if (flags & DEC_RND) 2385 acc += dos_sectors; 2386 if (flags & DEC_RND_DOWN) 2387 acc -= dos_sectors; 2388 if (flags & DEC_RND_DOWN_2) 2389 acc -= dos_sectors; 2390 } 2391 } 2392 } 2393 2394 cp += strspn(cp, " \t"); 2395 if (*cp != '\0') { 2396 printf("%s is not a valid %s number.\n", lbuf, 2397 flags & DEC_SEC ? "sector" : "decimal"); 2398 continue; 2399 } 2400 2401 if (acc >= minval && acc <= maxval) 2402 return acc; 2403 printf("%d is not between %d and %d.\n", acc, minval, maxval); 2404 } 2405 } 2406 2407 int 2408 ptn_id(const char *prompt, int *extended) 2409 { 2410 uint acc = 0; 2411 char *cp; 2412 2413 for (;; printf("%s is not a valid partition number.\n", lbuf)) { 2414 printf("%s: [none] ", prompt); 2415 2416 if (!fgets(lbuf, LBUF, stdin)) 2417 errx(1, "EOF"); 2418 lbuf[strlen(lbuf)-1] = '\0'; 2419 cp = lbuf; 2420 2421 cp += strspn(cp, " \t"); 2422 *extended = 0; 2423 if (*cp == 0) 2424 return -1; 2425 2426 if (*cp == 'E' || *cp == 'e') { 2427 cp++; 2428 *extended = 1; 2429 } 2430 2431 acc = strtoul(cp, &cp, 10); 2432 2433 cp += strspn(cp, " \t"); 2434 if (*cp != '\0') 2435 continue; 2436 2437 if (*extended || acc < MBR_PART_COUNT) 2438 return acc; 2439 } 2440 } 2441 2442 #ifdef BOOTSEL 2443 void 2444 string(const char *prompt, int length, char *buf) 2445 { 2446 int len; 2447 2448 for (;;) { 2449 printf("%s: [%.*s] ", prompt, length, buf); 2450 2451 if (!fgets(lbuf, LBUF, stdin)) 2452 errx(1, "EOF"); 2453 len = strlen(lbuf); 2454 if (len <= 1) 2455 /* unchanged if just <enter> */ 2456 return; 2457 /* now strip trailing spaces, <space><enter> deletes string */ 2458 do 2459 lbuf[--len] = 0; 2460 while (len != 0 && lbuf[len - 1] == ' '); 2461 if (len < length) 2462 break; 2463 printf("'%s' is longer than %d characters.\n", 2464 lbuf, length - 1); 2465 } 2466 strncpy(buf, lbuf, length); 2467 } 2468 #endif 2469 2470 int 2471 type_match(const void *key, const void *item) 2472 { 2473 const int *idp = key; 2474 const struct mbr_ptype *ptr = item; 2475 2476 if (*idp < ptr->id) 2477 return (-1); 2478 if (*idp > ptr->id) 2479 return (1); 2480 return (0); 2481 } 2482 2483 const char * 2484 get_type(int type) 2485 { 2486 struct mbr_ptype *ptr; 2487 2488 ptr = bsearch(&type, mbr_ptypes, KNOWN_SYSIDS, 2489 sizeof(struct mbr_ptypes[0]), type_match); 2490 if (ptr == 0) 2491 return ("unknown"); 2492 return (ptr->name); 2493 } 2494