1 /* $NetBSD: fdisk.c,v 1.52 2002/04/03 03:17:36 thorpej 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 #include <sys/cdefs.h> 30 31 #ifndef lint 32 __RCSID("$NetBSD: fdisk.c,v 1.52 2002/04/03 03:17:36 thorpej Exp $"); 33 #endif /* not lint */ 34 35 #include <sys/types.h> 36 #include <sys/disklabel.h> 37 #include <sys/disklabel_mbr.h> 38 #include <sys/ioctl.h> 39 #include <sys/param.h> 40 #include <sys/stat.h> 41 #include <sys/sysctl.h> 42 43 #include <ctype.h> 44 #include <err.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <paths.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 #include <util.h> 53 54 #ifdef __i386__ 55 #include <machine/cpu.h> 56 #endif 57 58 #define LBUF 100 59 static char lbuf[LBUF]; 60 61 /* 62 * 14-Dec-89 Robert Baron (rvb) at Carnegie-Mellon University 63 * Copyright (c) 1989 Robert. V. Baron 64 * Created. 65 */ 66 67 #ifndef _PATH_DEFDISK 68 #define _PATH_DEFDISK "/dev/rwd0d" 69 #endif 70 71 const char *disk = _PATH_DEFDISK; 72 73 struct disklabel disklabel; /* disk parameters */ 74 75 int cylinders, sectors, heads, cylindersectors, disksectors; 76 77 struct mboot { 78 u_int8_t padding[2]; /* force the longs to be long alligned */ 79 u_int8_t bootinst[MBR_PARTOFF]; 80 struct mbr_partition parts[NMBRPART]; 81 u_int16_t signature; 82 }; 83 struct mboot mboot; 84 85 #ifdef __i386__ 86 87 #define PARTNAMESIZE 8 /* From mbr_bootsel.S */ 88 89 struct mbr_bootsel { 90 u_int8_t defkey; 91 u_int8_t flags; 92 u_int16_t timeo; 93 char nametab[4][PARTNAMESIZE + 1]; 94 u_int16_t magic; 95 } __attribute__((packed)); 96 97 #define BFL_SELACTIVE 0x01 98 #define BFL_EXTINT13 0x02 99 100 #define SCAN_ENTER 0x1c 101 #define SCAN_F1 0x3b 102 103 #define MBR_BOOTSELOFF (MBR_PARTOFF - sizeof (struct mbr_bootsel)) 104 105 #define DEFAULT_BOOTCODE "/usr/mdec/mbr" 106 #define DEFAULT_BOOTSELCODE "/usr/mdec/mbr_bootsel" 107 #define OPTIONS "0123BSafilus:b:c:" 108 #else 109 #define OPTIONS "0123Safilus:b:c:" 110 #endif 111 112 #define ACTIVE 0x80 113 114 int dos_cylinders; 115 int dos_heads; 116 int dos_sectors; 117 int dos_cylindersectors; 118 119 #define DOSSECT(s,c) (((s) & 0x3f) | (((c) >> 2) & 0xc0)) 120 #define DOSCYL(c) ((c) & 0xff) 121 122 #define MAXCYL 1024 123 int partition = -1; 124 125 int a_flag; /* set active partition */ 126 int i_flag; /* init bootcode */ 127 int u_flag; /* update partition data */ 128 int sh_flag; /* Output data as shell defines */ 129 int f_flag; /* force --not interactive */ 130 int s_flag; /* set id,offset,size */ 131 int b_flag; /* Set cyl, heads, secs (as c/h/s) */ 132 int B_flag; /* Edit/install bootselect code */ 133 int b_cyl, b_head, b_sec; /* b_flag values. */ 134 int bootsel_modified; 135 136 unsigned char bootcode[8192]; /* maximum size of bootcode */ 137 unsigned char tempcode[8192]; 138 int bootsize; /* actual size of bootcode */ 139 140 141 static char reserved[] = "reserved"; 142 143 struct part_type { 144 int type; 145 const char *name; 146 } part_types[] = { 147 {0x00, "unused"}, 148 {0x01, "Primary DOS with 12 bit FAT"}, 149 {0x02, "XENIX / filesystem"}, 150 {0x03, "XENIX /usr filesystem"}, 151 {0x04, "Primary DOS with 16 bit FAT <32M"}, 152 {0x05, "Extended partition"}, 153 {0x06, "Primary 'big' DOS, 16-bit FAT (> 32MB)"}, 154 {0x07, "OS/2 HPFS or NTFS or QNX2 or Advanced UNIX"}, 155 {0x08, "AIX filesystem or OS/2 (thru v1.3) or DELL multiple drives" 156 "or Commodore DOS or SplitDrive"}, 157 {0x09, "AIX boot partition or Coherent"}, 158 {0x0A, "OS/2 Boot Manager or Coherent swap or OPUS"}, 159 {0x0b, "Primary DOS with 32 bit FAT"}, 160 {0x0c, "Primary DOS with 32 bit FAT - LBA"}, 161 {0x0d, "Type 7??? - LBA"}, 162 {0x0E, "DOS (16-bit FAT) - LBA"}, 163 {0x0F, "Ext. partition - LBA"}, 164 {0x10, "OPUS"}, 165 {0x11, "OS/2 BM: hidden DOS 12-bit FAT"}, 166 {0x12, "Compaq diagnostics"}, 167 {0x14, "OS/2 BM: hidden DOS 16-bit FAT <32M or Novell DOS 7.0 bug"}, 168 {0x16, "OS/2 BM: hidden DOS 16-bit FAT >=32M"}, 169 {0x17, "OS/2 BM: hidden IFS"}, 170 {0x18, "AST Windows swapfile"}, 171 {0x19, "Willowtech Photon coS"}, 172 {0x1e, "hidden FAT95"}, 173 {0x20, "Willowsoft OFS1"}, 174 {0x21, reserved}, 175 {0x23, reserved}, 176 {0x24, "NEC DOS"}, 177 {0x26, reserved}, 178 {0x31, reserved}, 179 {0x33, reserved}, 180 {0x34, reserved}, 181 {0x36, reserved}, 182 {0x38, "Theos"}, 183 {0x3C, "PartitionMagic recovery"}, 184 {0x40, "VENIX 286 or LynxOS"}, 185 {0x41, "Linux/MINIX (sharing disk with DRDOS) or Personal RISC boot"}, 186 {0x42, "SFS or Linux swap (sharing disk with DRDOS)"}, 187 {0x43, "Linux native (sharing disk with DRDOS)"}, 188 {0x4D, "QNX4.x"}, 189 {0x4E, "QNX4.x 2nd part"}, 190 {0x4F, "QNX4.x 3rd part"}, 191 {0x50, "DM (disk manager)"}, 192 {0x51, "DM6 Aux1 (or Novell)"}, 193 {0x52, "CP/M or Microport SysV/AT"}, 194 {0x53, "DM6 Aux3"}, 195 {0x54, "DM6 DDO"}, 196 {0x55, "EZ-Drive (disk manager)"}, 197 {0x56, "Golden Bow (disk manager)"}, 198 {0x5C, "Priam Edisk (disk manager)"}, 199 {0x61, "SpeedStor"}, 200 {0x63, "GNU HURD or Mach or Sys V/386 (such as ISC UNIX) or MtXinu"}, 201 {0x64, "Novell Netware 2.xx or Speedstore"}, 202 {0x65, "Novell Netware 3.xx"}, 203 {0x66, "Novell 386 Netware"}, 204 {0x67, "Novell"}, 205 {0x68, "Novell"}, 206 {0x69, "Novell"}, 207 {0x70, "DiskSecure Multi-Boot"}, 208 {0x71, reserved}, 209 {0x73, reserved}, 210 {0x74, reserved}, 211 {0x75, "PC/IX"}, 212 {0x76, reserved}, 213 {0x80, "MINIX until 1.4a"}, 214 {0x81, "MINIX since 1.4b, early Linux, Mitac dmgr"}, 215 {0x82, "Linux swap or Prime or Solaris"}, 216 {0x83, "Linux native"}, 217 {0x84, "OS/2 hidden C: drive"}, 218 {0x85, "Linux extended"}, 219 {0x86, "NT FAT volume set"}, 220 {0x87, "NTFS volume set or HPFS mirrored"}, 221 {0x93, "Amoeba filesystem"}, 222 {0x94, "Amoeba bad block table"}, 223 {0x99, "Mylex EISA SCSI"}, 224 {0x9f, "BSDI?"}, 225 {0xA0, "IBM Thinkpad hibernation"}, 226 {0xa1, reserved}, 227 {0xa3, reserved}, 228 {0xa4, reserved}, 229 {0xA5, "FreeBSD or 386BSD or old NetBSD"}, 230 {0xA6, "OpenBSD"}, 231 {0xA7, "NeXTSTEP 486"}, 232 {0xa8, "Apple UFS"}, 233 {0xa9, "NetBSD"}, 234 {0xab, "Apple Boot"}, 235 {0xb1, reserved}, 236 {0xb3, reserved}, 237 {0xb4, reserved}, 238 {0xb6, reserved}, 239 {0xB7, "BSDI BSD/386 filesystem"}, 240 {0xB8, "BSDI BSD/386 swap"}, 241 {0xc0, "CTOS"}, 242 {0xC1, "DRDOS/sec (FAT-12)"}, 243 {0xC4, "DRDOS/sec (FAT-16, < 32M)"}, 244 {0xC6, "DRDOS/sec (FAT-16, >= 32M)"}, 245 {0xC7, "Syrinx (Cyrnix?) or HPFS disabled"}, 246 {0xd8, "CP/M 86"}, 247 {0xDB, "CP/M or Concurrent CP/M or Concurrent DOS or CTOS"}, 248 {0xE1, "DOS access or SpeedStor 12-bit FAT extended partition"}, 249 {0xE3, "DOS R/O or SpeedStor or Storage Dimensions"}, 250 {0xE4, "SpeedStor 16-bit FAT extended partition < 1024 cyl."}, 251 {0xe5, reserved}, 252 {0xe6, reserved}, 253 {0xeb, "BeOS"}, 254 {0xF1, "SpeedStor or Storage Dimensions"}, 255 {0xF2, "DOS 3.3+ Secondary"}, 256 {0xf3, reserved}, 257 {0xF4, "SpeedStor large partition or Storage Dimensions"}, 258 {0xf6, reserved}, 259 {0xFE, "SpeedStor >1024 cyl. or LANstep or IBM PS/2 IML"}, 260 {0xFF, "Xenix Bad Block Table"}, 261 }; 262 263 #define KNOWN_SYSIDS (sizeof(part_types)/sizeof(part_types[0])) 264 265 void usage(void); 266 void print_s0(int); 267 void print_part(int); 268 void print_mbr_partition(struct mbr_partition *, off_t, off_t, int); 269 int read_boot(const char *, void *, size_t); 270 void init_sector0(int, int); 271 void intuit_translated_geometry(void); 272 void get_geometry(void); 273 void get_diskname(const char *, char *, size_t); 274 int try_heads(quad_t, quad_t, quad_t, quad_t, quad_t, quad_t, quad_t, 275 quad_t); 276 int try_sectors(quad_t, quad_t, quad_t, quad_t, quad_t); 277 void change_part(int, int, int, int); 278 void print_params(void); 279 void change_active(int); 280 void get_params_to_use(void); 281 void dos(int, unsigned char *, unsigned char *, unsigned char *); 282 int open_disk(int); 283 int read_disk(off_t, void *); 284 int write_disk(off_t, void *); 285 int get_params(void); 286 int read_s0(off_t, struct mboot *); 287 int write_s0(void); 288 int yesno(const char *); 289 void decimal(const char *, int *); 290 int type_match(const void *, const void *); 291 const char *get_type(int); 292 int get_mapping(int, int *, int *, int *, unsigned long *); 293 #ifdef __i386__ 294 void configure_bootsel(void); 295 #endif 296 297 static unsigned short getshort(void *); 298 static void putshort(void *p, unsigned short); 299 static unsigned long getlong(void *); 300 static void putlong(void *, unsigned long); 301 302 303 int main(int, char *[]); 304 305 int 306 main(int argc, char *argv[]) 307 { 308 int ch, part, mib[2]; 309 size_t len; 310 char *root_device; 311 312 int csysid, cstart, csize; /* For the b_flag. */ 313 314 mib[0] = CTL_KERN; 315 mib[1] = KERN_ROOT_DEVICE; 316 if (sysctl(mib, 2, NULL, &len, NULL, 0) != -1 && 317 (root_device = malloc(len)) != NULL && 318 sysctl(mib, 2, root_device, &len, NULL, 0) != -1) 319 disk = root_device; 320 321 a_flag = i_flag = u_flag = sh_flag = f_flag = s_flag = b_flag = 0; 322 csysid = cstart = csize = 0; 323 while ((ch = getopt(argc, argv, OPTIONS)) != -1) 324 switch (ch) { 325 case '0': 326 partition = 0; 327 break; 328 case '1': 329 partition = 1; 330 break; 331 case '2': 332 partition = 2; 333 break; 334 case '3': 335 partition = 3; 336 break; 337 #ifdef __i386__ 338 case 'B': 339 B_flag = 1; 340 break; 341 #endif 342 case 'S': 343 sh_flag = 1; 344 break; 345 case 'a': 346 a_flag = 1; 347 break; 348 case 'f': 349 f_flag = 1; 350 break; 351 case 'i': 352 i_flag = 1; 353 break; 354 case 'l': 355 for (len = 0; len < KNOWN_SYSIDS; len++) 356 printf("%03lu %s\n", (u_long) len, 357 part_types[len].name); 358 return 0; 359 case 'u': 360 u_flag = 1; 361 break; 362 case 's': 363 s_flag = 1; 364 if (sscanf(optarg, "%d/%d/%d", &csysid, &cstart, 365 &csize) != 3) 366 err(1, "Bad argument to the -s flag.\n"); 367 break; 368 case 'b': 369 b_flag = 1; 370 if (sscanf(optarg, "%d/%d/%d", &b_cyl, &b_head, 371 &b_sec) != 3) 372 err(1, "Bad argument to the -s flag.\n"); 373 if (b_cyl > MAXCYL) 374 b_cyl = MAXCYL; 375 break; 376 case 'c': 377 bootsize = read_boot(optarg, bootcode, sizeof bootcode); 378 break; 379 default: 380 usage(); 381 } 382 argc -= optind; 383 argv += optind; 384 385 if (sh_flag && (a_flag || i_flag || u_flag || f_flag || s_flag)) 386 usage(); 387 388 if (B_flag && (a_flag || i_flag || u_flag || f_flag || s_flag)) 389 usage(); 390 391 if (partition == -1 && s_flag) { 392 warnx("-s flag requires a partition selected."); 393 usage(); 394 } 395 396 if (argc > 0) 397 disk = argv[0]; 398 399 if (open_disk(B_flag || a_flag || i_flag || u_flag) < 0) 400 exit(1); 401 402 if (read_s0(0, &mboot)) 403 init_sector0(sectors > 63 ? 63 : sectors, 1); 404 405 #ifdef __i386__ 406 get_geometry(); 407 #else 408 intuit_translated_geometry(); 409 #endif 410 411 412 if ((i_flag || u_flag) && (!f_flag || b_flag)) 413 get_params_to_use(); 414 415 if (i_flag) 416 init_sector0(dos_sectors > 63 ? 63 : dos_sectors, 0); 417 418 /* Do the update stuff! */ 419 if (u_flag) { 420 if (!f_flag) 421 printf("Partition table:\n"); 422 if (partition == -1) 423 for (part = 0; part < NMBRPART; part++) 424 change_part(part,-1, -1, -1); 425 else 426 change_part(partition, csysid, cstart, csize); 427 } else 428 if (!i_flag) 429 print_s0(partition); 430 431 if (a_flag) 432 change_active(partition); 433 434 #ifdef __i386__ 435 if (B_flag) { 436 configure_bootsel(); 437 if (B_flag && bootsel_modified) 438 write_s0(); 439 } 440 #endif 441 442 if (u_flag || a_flag || i_flag) { 443 if (!f_flag) { 444 printf("\nWe haven't written the MBR back to disk " 445 "yet. This is your last chance.\n"); 446 print_s0(-1); 447 if (yesno("Should we write new partition table?")) 448 write_s0(); 449 } else 450 write_s0(); 451 } 452 453 exit(0); 454 } 455 456 void 457 usage(void) 458 { 459 460 (void)fprintf(stderr, "Usage: %s [-aiufBS] [-0|-1|-2|-3] " 461 "[-b cylinders/heads/sectors]\n" 462 "\t%s [-s id/start/size] [-c bootcode] [device]\n", 463 getprogname(), getprogname()); 464 exit(1); 465 } 466 467 void 468 print_s0(int which) 469 { 470 int part; 471 472 print_params(); 473 if (!sh_flag) 474 printf("Partition table:\n"); 475 if (which == -1) { 476 for (part = 0; part < NMBRPART; part++) { 477 if (!sh_flag) 478 printf("%d: ", part); 479 print_part(part); 480 } 481 } else 482 print_part(which); 483 } 484 485 static unsigned short 486 getshort(void *p) 487 { 488 unsigned char *cp = p; 489 490 return cp[0] | (cp[1] << 8); 491 } 492 493 static void 494 putshort(void *p, unsigned short l) 495 { 496 unsigned char *cp = p; 497 498 *cp++ = l; 499 *cp++ = l >> 8; 500 } 501 502 static unsigned long 503 getlong(void *p) 504 { 505 unsigned char *cp = p; 506 507 return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24); 508 } 509 510 static void 511 putlong(void *p, unsigned long l) 512 { 513 unsigned char *cp = p; 514 515 *cp++ = l; 516 *cp++ = l >> 8; 517 *cp++ = l >> 16; 518 *cp++ = l >> 24; 519 } 520 521 void 522 print_part(int part) 523 { 524 struct mbr_partition *partp; 525 int empty; 526 527 partp = &mboot.parts[part]; 528 empty = (partp->mbrp_typ == 0); 529 530 if (sh_flag) { 531 if (empty) { 532 printf("PART%dSIZE=0\n", part); 533 return; 534 } 535 536 printf("PART%dID=%d\n", part, partp->mbrp_typ); 537 printf("PART%dSIZE=%ld\n", part, getlong(&partp->mbrp_size)); 538 printf("PART%dSTART=%ld\n", part, getlong(&partp->mbrp_start)); 539 printf("PART%dFLAG=0x%x\n", part, partp->mbrp_flag); 540 printf("PART%dBCYL=%d\n", part, MBR_PCYL(partp->mbrp_scyl, 541 partp->mbrp_ssect)); 542 printf("PART%dBHEAD=%d\n", part, partp->mbrp_shd); 543 printf("PART%dBSEC=%d\n", part, MBR_PSECT(partp->mbrp_ssect)); 544 printf("PART%dECYL=%d\n", part, MBR_PCYL(partp->mbrp_ecyl, 545 partp->mbrp_esect)); 546 printf("PART%dEHEAD=%d\n", part, partp->mbrp_ehd); 547 printf("PART%dESEC=%d\n", part, MBR_PSECT(partp->mbrp_esect)); 548 return; 549 } 550 print_mbr_partition(partp, 0, 0, 0); 551 } 552 553 void 554 print_mbr_partition(struct mbr_partition *partp, 555 off_t offset, off_t exoffset, int indent) 556 { 557 int empty; 558 off_t start; 559 560 empty = (partp->mbrp_typ == 0); 561 if (MBR_IS_EXTENDED(partp->mbrp_typ)) 562 start = (off_t)getlong(&partp->mbrp_start) + exoffset; 563 else 564 start = (off_t)getlong(&partp->mbrp_start) + offset; 565 if (empty) { 566 printf("<UNUSED>\n"); 567 return; 568 } 569 printf("sysid %d (%s)\n", 570 partp->mbrp_typ, get_type(partp->mbrp_typ)); 571 printf("%*s start %lld, size %ld (%ld MB), flag 0x%x\n", 572 indent, "", 573 start, getlong(&partp->mbrp_size), 574 getlong(&partp->mbrp_size) * 512 / (1024 * 1024), partp->mbrp_flag); 575 printf("%*s beg: cylinder %4d, head %3d, sector %2d\n", 576 indent, "", 577 MBR_PCYL(partp->mbrp_scyl, partp->mbrp_ssect), 578 partp->mbrp_shd, MBR_PSECT(partp->mbrp_ssect)); 579 printf("%*s end: cylinder %4d, head %3d, sector %2d\n", 580 indent, "", 581 MBR_PCYL(partp->mbrp_ecyl, partp->mbrp_esect), 582 partp->mbrp_ehd, MBR_PSECT(partp->mbrp_esect)); 583 584 if (MBR_IS_EXTENDED(partp->mbrp_typ)) { 585 struct mboot eboot; 586 int part; 587 588 printf("%*s Extended partition table:\n", indent, ""); 589 if (read_s0(start, &eboot) == -1) 590 return; 591 indent += 8; 592 if (exoffset == 0) 593 exoffset = start; 594 for (part = 0; part < NMBRPART; part++) { 595 printf("%*s%d: ", indent, "", part); 596 print_mbr_partition(&eboot.parts[part], 597 start, exoffset, indent); 598 } 599 } 600 } 601 602 int 603 read_boot(const char *name, void *buf, size_t len) 604 { 605 int bfd, ret; 606 struct stat st; 607 608 if ((bfd = open(name, O_RDONLY)) < 0) 609 err(1, "%s", name); 610 if (fstat(bfd, &st) == -1) 611 err(1, "%s", name); 612 if (st.st_size > len) 613 errx(1, "%s: bootcode too large", name); 614 ret = st.st_size; 615 if (ret < 0x200) 616 errx(1, "%s: bootcode too small", name); 617 if (read(bfd, buf, len) != ret) 618 err(1, "%s", name); 619 close(bfd); 620 621 /* 622 * Do some sanity checking here 623 */ 624 if (getshort(bootcode + MBR_MAGICOFF) != MBR_MAGIC) 625 errx(1, "%s: invalid magic", name); 626 ret = (ret + 0x1ff) / 0x200; 627 ret *= 0x200; 628 return ret; 629 } 630 631 void 632 init_sector0(int start, int dopart) 633 { 634 int i; 635 636 #ifdef DEFAULT_BOOTCODE 637 if (!bootsize) 638 bootsize = read_boot(DEFAULT_BOOTCODE, bootcode, 639 sizeof bootcode); 640 #endif 641 642 memcpy(mboot.bootinst, bootcode, sizeof(mboot.bootinst)); 643 putshort(&mboot.signature, MBR_MAGIC); 644 645 if (dopart) 646 for (i = 0; i < 4; i++) 647 memset(&mboot.parts[i], 0, sizeof(mboot.parts[i])); 648 649 } 650 651 #ifdef __i386__ 652 653 void 654 get_diskname(const char *fullname, char *diskname, size_t size) 655 { 656 const char *p, *p2; 657 size_t len; 658 659 p = strrchr(fullname, '/'); 660 if (p == NULL) 661 p = fullname; 662 else 663 p++; 664 665 if (*p == 0) { 666 strncpy(diskname, fullname, size - 1); 667 diskname[size - 1] = '\0'; 668 return; 669 } 670 671 if (*p == 'r') 672 p++; 673 674 for (p2 = p; *p2 != 0; p2++) 675 if (isdigit(*p2)) 676 break; 677 if (*p2 == 0) { 678 /* XXX invalid diskname? */ 679 strncpy(diskname, fullname, size - 1); 680 diskname[size - 1] = '\0'; 681 return; 682 } 683 while (isdigit(*p2)) 684 p2++; 685 686 len = p2 - p; 687 if (len > size) { 688 /* XXX */ 689 strncpy(diskname, fullname, size - 1); 690 diskname[size - 1] = '\0'; 691 return; 692 } 693 694 strncpy(diskname, p, len); 695 diskname[len] = 0; 696 } 697 698 void 699 get_geometry(void) 700 { 701 int mib[2], i; 702 size_t len; 703 struct disklist *dl; 704 struct biosdisk_info *bip; 705 struct nativedisk_info *nip; 706 char diskname[8]; 707 708 mib[0] = CTL_MACHDEP; 709 mib[1] = CPU_DISKINFO; 710 if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0) { 711 intuit_translated_geometry(); 712 return; 713 } 714 dl = (struct disklist *) malloc(len); 715 sysctl(mib, 2, dl, &len, NULL, 0); 716 717 get_diskname(disk, diskname, sizeof diskname); 718 719 for (i = 0; i < dl->dl_nnativedisks; i++) { 720 nip = &dl->dl_nativedisks[i]; 721 if (strcmp(diskname, nip->ni_devname)) 722 continue; 723 /* 724 * XXX listing possible matches is better. This is ok 725 * for now because the user has a chance to change 726 * it later. 727 */ 728 if (nip->ni_nmatches != 0) { 729 bip = &dl->dl_biosdisks[nip->ni_biosmatches[0]]; 730 dos_cylinders = bip->bi_cyl; 731 dos_heads = bip->bi_head; 732 dos_sectors = bip->bi_sec; 733 dos_cylindersectors = bip->bi_head * bip->bi_sec; 734 return; 735 } 736 } 737 /* Allright, allright, make a stupid guess.. */ 738 intuit_translated_geometry(); 739 } 740 741 void 742 configure_bootsel(void) 743 { 744 struct mbr_bootsel *mbs = 745 (struct mbr_bootsel *)&mboot.bootinst[MBR_BOOTSELOFF]; 746 int i, nused, firstpart = -1, item; 747 char desc[PARTNAMESIZE + 2], *p; 748 int timo, entry_changed = 0; 749 750 for (i = nused = 0; i < NMBRPART; ++i) { 751 if (mboot.parts[i].mbrp_typ != 0) { 752 if (firstpart == -1) 753 firstpart = i; 754 nused++; 755 } 756 } 757 758 if (nused == 0) { 759 warnx("No used partitions found. Partition the disk first."); 760 return; 761 } 762 763 if (mbs->magic != MBR_MAGIC) { 764 if (!yesno("Bootselector not yet installed. Install it now?")) { 765 warnx("Bootselector not installed."); 766 return; 767 } 768 bootsize = read_boot(DEFAULT_BOOTSELCODE, bootcode, 769 sizeof bootcode); 770 memcpy(mboot.bootinst, bootcode, sizeof(mboot.bootinst)); 771 bootsel_modified = 1; 772 mbs->flags |= BFL_SELACTIVE; 773 } else { 774 if (mbs->flags & BFL_SELACTIVE) { 775 printf("The bootselector is installed and active."); 776 if (!yesno("Do you want to change its settings?")) { 777 if (yesno("Do you want to deactivate it?")) { 778 mbs->flags &= ~BFL_SELACTIVE; 779 bootsel_modified = 1; 780 goto done; 781 } 782 return; 783 } 784 } else { 785 printf("The bootselector is installed but not active."); 786 if (yesno("Do you want to activate it?")) { 787 mbs->flags |= BFL_SELACTIVE; 788 bootsel_modified = 1; 789 } 790 if (!yesno("Do you want to change its settings?")) 791 goto done; 792 } 793 } 794 795 printf("\n\nPartition table:\n"); 796 for (i = 0; i < NMBRPART; i++) { 797 printf("%d: ", i); 798 print_part(i); 799 } 800 801 printf("\n\nCurrent boot selection menu option names:\n"); 802 for (i = 0; i < NMBRPART; i++) { 803 if (mbs->nametab[i][0] != 0) 804 printf("%d: %s\n", i, &mbs->nametab[i][0]); 805 else 806 printf("%d: <UNUSED>\n", i); 807 } 808 printf("\n"); 809 810 item = firstpart; 811 812 editentries: 813 while (1) { 814 decimal("Change which entry (-1 quits)?", &item); 815 if (item == -1) 816 break; 817 if (item < 0 || item >= NMBRPART) { 818 printf("Invalid entry number\n"); 819 item = -1; 820 continue; 821 } 822 if (mboot.parts[item].mbrp_typ == 0) { 823 printf("The partition entry is unused\n"); 824 item = -1; 825 continue; 826 } 827 828 printf("Enter descriptions (max. 8 characters): "); 829 rewind(stdin); 830 fgets(desc, PARTNAMESIZE + 1, stdin); 831 fpurge(stdin); 832 p = strchr(desc, '\n'); 833 if (p != NULL) 834 *p = 0; 835 strcpy(&mbs->nametab[item][0], desc); 836 entry_changed = bootsel_modified = 1; 837 838 item++; 839 } 840 841 if (entry_changed) 842 printf("Boot selection menu option names are now:\n"); 843 844 firstpart = -1; 845 for (i = 0; i < NMBRPART; i++) { 846 if (mbs->nametab[i][0] != 0) { 847 firstpart = i; 848 if (entry_changed) 849 printf("%d: %s\n", i, &mbs->nametab[i][0]); 850 } else { 851 if (entry_changed) 852 printf("%d: <UNUSED>\n", i); 853 } 854 } 855 if (entry_changed) 856 printf("\n"); 857 858 if (firstpart == -1) { 859 printf("All menu entries are now inactive.\n"); 860 if (!yesno("Are you sure about this?")) 861 goto editentries; 862 } else { 863 if (!(mbs->flags & BFL_SELACTIVE)) { 864 printf("The bootselector is not yet active.\n"); 865 if (yesno("Activate it now?")) 866 mbs->flags |= BFL_SELACTIVE; 867 } 868 } 869 870 /* bootsel is dirty from here on out. */ 871 bootsel_modified = 1; 872 873 /* The timeout value is in ticks, 18.2 Hz. Avoid using floats. */ 874 timo = ((1000 * mbs->timeo) / 18200); 875 do { 876 decimal("Timeout value", &timo); 877 } while (timo < 0 || timo > 3600); 878 mbs->timeo = (u_int16_t)((timo * 18200) / 1000); 879 880 printf("Select the default boot option. Options are:\n\n"); 881 for (i = 0; i < NMBRPART; i++) { 882 if (mbs->nametab[i][0] != 0) 883 printf("%d: %s\n", i, &mbs->nametab[i][0]); 884 } 885 for (i = 4; i < 10; i++) 886 printf("%d: Harddisk %d\n", i, i - 4); 887 printf("10: The first active partition\n"); 888 889 if (mbs->defkey == SCAN_ENTER) 890 item = 10; 891 else 892 item = mbs->defkey - SCAN_F1; 893 894 if (item < 0 || item > 10 || mbs->nametab[item][0] == 0) 895 item = 10; 896 897 do { 898 decimal("Default boot option", &item); 899 } while (item < 0 || item > 10 || 900 (item <= 3 && mbs->nametab[item][0] == 0)); 901 902 if (item == 10) 903 mbs->defkey = SCAN_ENTER; 904 else 905 mbs->defkey = SCAN_F1 + item; 906 907 done: 908 for (i = 0; i < NMBRPART; i++) { 909 if (mboot.parts[i].mbrp_typ != 0 && 910 mboot.parts[i].mbrp_start >= 911 (dos_cylinders * dos_heads * dos_sectors)) { 912 mbs->flags |= BFL_EXTINT13; 913 break; 914 } 915 } 916 917 if (bootsel_modified != 0 && !yesno("Update the bootselector?")) 918 bootsel_modified = 0; 919 } 920 #endif 921 922 923 /* Prerequisite: the disklabel parameters and master boot record must 924 * have been read (i.e. dos_* and mboot are meaningful). 925 * Specification: modifies dos_cylinders, dos_heads, dos_sectors, and 926 * dos_cylindersectors to be consistent with what the 927 * partition table is using, if we can find a geometry 928 * which is consistent with all partition table entries. 929 * We may get the number of cylinders slightly wrong (in 930 * the conservative direction). The idea is to be able 931 * to create a NetBSD partition on a disk we don't know 932 * the translated geometry of. 933 * This whole routine should be replaced with a kernel interface to get 934 * the BIOS geometry (which in turn requires modifications to the i386 935 * boot loader to pass in the BIOS geometry for each disk). */ 936 void 937 intuit_translated_geometry(void) 938 { 939 940 int xcylinders = -1, xheads = -1, xsectors = -1, i, j; 941 int c1, h1, s1, c2, h2, s2; 942 long a1, a2; 943 quad_t num, denom; 944 945 /* Try to deduce the number of heads from two different mappings. */ 946 for (i = 0; i < NMBRPART * 2; i++) { 947 if (get_mapping(i, &c1, &h1, &s1, &a1) < 0) 948 continue; 949 for (j = 0; j < 8; j++) { 950 if (get_mapping(j, &c2, &h2, &s2, &a2) < 0) 951 continue; 952 num = (quad_t)h1*(a2-s2) - (quad_t)h2*(a1-s1); 953 denom = (quad_t)c2*(a1-s1) - (quad_t)c1*(a2-s2); 954 if (denom != 0 && num % denom == 0) { 955 xheads = num / denom; 956 break; 957 } 958 } 959 if (xheads != -1) 960 break; 961 } 962 963 if (xheads == -1) 964 return; 965 966 /* Now figure out the number of sectors from a single mapping. */ 967 for (i = 0; i < NMBRPART * 2; i++) { 968 if (get_mapping(i, &c1, &h1, &s1, &a1) < 0) 969 continue; 970 num = a1 - s1; 971 denom = c1 * xheads + h1; 972 if (denom != 0 && num % denom == 0) { 973 xsectors = num / denom; 974 break; 975 } 976 } 977 978 if (xsectors == -1) 979 return; 980 981 /* Estimate the number of cylinders. */ 982 xcylinders = disklabel.d_secperunit / xheads / xsectors; 983 984 /* Now verify consistency with each of the partition table entries. 985 * Be willing to shove cylinders up a little bit to make things work, 986 * but translation mismatches are fatal. */ 987 for (i = 0; i < NMBRPART * 2; i++) { 988 if (get_mapping(i, &c1, &h1, &s1, &a1) < 0) 989 continue; 990 if (xsectors * (c1 * xheads + h1) + s1 != a1) 991 return; 992 if (c1 >= xcylinders) 993 xcylinders = c1 + 1; 994 } 995 996 /* Everything checks out. Reset the geometry to use for further 997 * calculations. */ 998 dos_cylinders = xcylinders; 999 dos_heads = xheads; 1000 dos_sectors = xsectors; 1001 dos_cylindersectors = xheads * xsectors; 1002 } 1003 1004 /* For the purposes of intuit_translated_geometry(), treat the partition 1005 * table as a list of eight mapping between (cylinder, head, sector) 1006 * triplets and absolute sectors. Get the relevant geometry triplet and 1007 * absolute sectors for a given entry, or return -1 if it isn't present. 1008 * Note: for simplicity, the returned sector is 0-based. */ 1009 int 1010 get_mapping(int i, int *cylinder, int *head, int *sector, 1011 unsigned long *absolute) 1012 { 1013 struct mbr_partition *part = &mboot.parts[i / 2]; 1014 1015 if (part->mbrp_typ == 0) 1016 return -1; 1017 if (i % 2 == 0) { 1018 *cylinder = MBR_PCYL(part->mbrp_scyl, part->mbrp_ssect); 1019 *head = part->mbrp_shd; 1020 *sector = MBR_PSECT(part->mbrp_ssect) - 1; 1021 *absolute = getlong(&part->mbrp_start); 1022 } else { 1023 *cylinder = MBR_PCYL(part->mbrp_ecyl, part->mbrp_esect); 1024 *head = part->mbrp_ehd; 1025 *sector = MBR_PSECT(part->mbrp_esect) - 1; 1026 *absolute = getlong(&part->mbrp_start) 1027 + getlong(&part->mbrp_size) - 1; 1028 } 1029 return 0; 1030 } 1031 1032 void 1033 change_part(int part, int csysid, int cstart, int csize) 1034 { 1035 struct mbr_partition *partp; 1036 1037 partp = &mboot.parts[part]; 1038 1039 if (s_flag) { 1040 if (csysid == 0 && cstart == 0 && csize == 0) 1041 memset(partp, 0, sizeof *partp); 1042 else { 1043 partp->mbrp_typ = csysid; 1044 #if 0 1045 checkcyl(cstart / dos_cylindersectors); 1046 #endif 1047 putlong(&partp->mbrp_start, cstart); 1048 putlong(&partp->mbrp_size, csize); 1049 dos(getlong(&partp->mbrp_start), 1050 &partp->mbrp_scyl, &partp->mbrp_shd, &partp->mbrp_ssect); 1051 dos(getlong(&partp->mbrp_start) 1052 + getlong(&partp->mbrp_size) - 1, 1053 &partp->mbrp_ecyl, &partp->mbrp_ehd, &partp->mbrp_esect); 1054 } 1055 if (f_flag) 1056 return; 1057 } 1058 1059 printf("The data for partition %d is:\n", part); 1060 print_part(part); 1061 if (!u_flag || !yesno("Do you want to change it?")) 1062 return; 1063 1064 do { 1065 { 1066 int sysid, start, size; 1067 1068 sysid = partp->mbrp_typ, 1069 start = getlong(&partp->mbrp_start), 1070 size = getlong(&partp->mbrp_size); 1071 decimal("sysid", &sysid); 1072 decimal("start", &start); 1073 decimal("size", &size); 1074 partp->mbrp_typ = sysid; 1075 putlong(&partp->mbrp_start, start); 1076 putlong(&partp->mbrp_size, size); 1077 } 1078 1079 if (yesno("Explicitly specify beg/end address?")) { 1080 int tsector, tcylinder, thead; 1081 1082 tcylinder = MBR_PCYL(partp->mbrp_scyl, 1083 partp->mbrp_ssect); 1084 thead = partp->mbrp_shd; 1085 tsector = MBR_PSECT(partp->mbrp_ssect); 1086 decimal("beginning cylinder", &tcylinder); 1087 #if 0 1088 checkcyl(tcylinder); 1089 #endif 1090 decimal("beginning head", &thead); 1091 decimal("beginning sector", &tsector); 1092 partp->mbrp_scyl = DOSCYL(tcylinder); 1093 partp->mbrp_shd = thead; 1094 partp->mbrp_ssect = DOSSECT(tsector, tcylinder); 1095 1096 tcylinder = MBR_PCYL(partp->mbrp_ecyl, 1097 partp->mbrp_esect); 1098 thead = partp->mbrp_ehd; 1099 tsector = MBR_PSECT(partp->mbrp_esect); 1100 decimal("ending cylinder", &tcylinder); 1101 decimal("ending head", &thead); 1102 decimal("ending sector", &tsector); 1103 partp->mbrp_ecyl = DOSCYL(tcylinder); 1104 partp->mbrp_ehd = thead; 1105 partp->mbrp_esect = DOSSECT(tsector, tcylinder); 1106 } else { 1107 1108 if (partp->mbrp_typ == 0 1109 && getlong(&partp->mbrp_start) == 0 1110 && getlong(&partp->mbrp_size) == 0) 1111 memset(partp, 0, sizeof *partp); 1112 else { 1113 #if 0 1114 checkcyl(getlong(&partp->mbrp_start) 1115 / dos_cylindersectors); 1116 #endif 1117 dos(getlong(&partp->mbrp_start), 1118 &partp->mbrp_scyl, 1119 &partp->mbrp_shd, &partp->mbrp_ssect); 1120 dos(getlong(&partp->mbrp_start) 1121 + getlong(&partp->mbrp_size) - 1, 1122 &partp->mbrp_ecyl, &partp->mbrp_ehd, 1123 &partp->mbrp_esect); 1124 } 1125 } 1126 1127 print_part(part); 1128 } while (!yesno("Is this entry okay?")); 1129 } 1130 1131 void 1132 print_params(void) 1133 { 1134 1135 if (sh_flag) { 1136 printf ("DLCYL=%d\nDLHEAD=%d\nDLSEC=%d\nDLSIZE=%d\n", 1137 cylinders, heads, sectors, disksectors); 1138 printf ("BCYL=%d\nBHEAD=%d\nBSEC=%d\n", 1139 dos_cylinders, dos_heads, dos_sectors); 1140 return; 1141 } 1142 1143 /* Not sh_flag */ 1144 printf("Disk: %s\n", disk); 1145 printf("NetBSD disklabel disk geometry:\n"); 1146 printf("cylinders: %d heads: %d sectors/track: %d (%d sectors/cylinder)\n\n", 1147 cylinders, heads, sectors, cylindersectors); 1148 printf("BIOS disk geometry:\n"); 1149 printf("cylinders: %d heads: %d sectors/track: %d (%d sectors/cylinder)\n\n", 1150 dos_cylinders, dos_heads, dos_sectors, dos_cylindersectors); 1151 } 1152 1153 void 1154 change_active(int which) 1155 { 1156 struct mbr_partition *partp; 1157 int part; 1158 int active = 4; 1159 1160 partp = &mboot.parts[0]; 1161 1162 if (a_flag && which != -1) 1163 active = which; 1164 else { 1165 for (part = 0; part < NMBRPART; part++) 1166 if (partp[part].mbrp_flag & ACTIVE) 1167 active = part; 1168 } 1169 if (!f_flag) { 1170 if (yesno("Do you want to change the active partition?")) { 1171 printf ("Choosing 4 will make no partition active.\n"); 1172 do { 1173 decimal("active partition", &active); 1174 } while (!yesno("Are you happy with this choice?")); 1175 } else 1176 return; 1177 } else 1178 if (active != 4) 1179 printf ("Making partition %d active.\n", active); 1180 1181 for (part = 0; part < NMBRPART; part++) 1182 partp[part].mbrp_flag &= ~ACTIVE; 1183 if (active < 4) 1184 partp[active].mbrp_flag |= ACTIVE; 1185 } 1186 1187 void 1188 get_params_to_use(void) 1189 { 1190 1191 if (b_flag) { 1192 dos_cylinders = b_cyl; 1193 dos_heads = b_head; 1194 dos_sectors = b_sec; 1195 dos_cylindersectors = dos_heads * dos_sectors; 1196 return; 1197 } 1198 1199 print_params(); 1200 if (yesno("Do you want to change our idea of what BIOS thinks?")) { 1201 do { 1202 decimal("BIOS's idea of #cylinders", &dos_cylinders); 1203 decimal("BIOS's idea of #heads", &dos_heads); 1204 decimal("BIOS's idea of #sectors", &dos_sectors); 1205 dos_cylindersectors = dos_heads * dos_sectors; 1206 print_params(); 1207 } while (!yesno("Are you happy with this choice?")); 1208 } 1209 } 1210 1211 /***********************************************\ 1212 * Change real numbers into strange dos numbers * 1213 \***********************************************/ 1214 void 1215 dos(int sector, unsigned char *cylinderp, unsigned char *headp, 1216 unsigned char *sectorp) 1217 { 1218 int cylinder, head; 1219 int biosmaxsec; 1220 1221 biosmaxsec = dos_cylinders * dos_heads * dos_sectors - 1; 1222 if (sector > biosmaxsec) 1223 sector = biosmaxsec; 1224 1225 cylinder = sector / dos_cylindersectors; 1226 1227 sector -= cylinder * dos_cylindersectors; 1228 1229 head = sector / dos_sectors; 1230 sector -= head * dos_sectors; 1231 1232 *cylinderp = DOSCYL(cylinder); 1233 *headp = head; 1234 *sectorp = DOSSECT(sector + 1, cylinder); 1235 } 1236 1237 #if 0 1238 void 1239 checkcyl(int cyl) 1240 { 1241 1242 if (cyl >= MAXCYL) 1243 warnx("partition start beyond BIOS limit"); 1244 } 1245 #endif 1246 1247 int fd = -1; 1248 1249 int 1250 open_disk(int update) 1251 { 1252 static char namebuf[MAXPATHLEN + 1]; 1253 1254 fd = opendisk(disk, update ? O_RDWR : O_RDONLY, namebuf, 1255 sizeof(namebuf), 0); 1256 if (fd < 0) { 1257 if (errno == ENODEV) 1258 warnx("%s is not a character device", namebuf); 1259 else 1260 warn("%s", namebuf); 1261 return (-1); 1262 } 1263 disk = namebuf; 1264 if (get_params() == -1) { 1265 close(fd); 1266 return (-1); 1267 } 1268 return (0); 1269 } 1270 1271 int 1272 read_disk(off_t sector, void *buf) 1273 { 1274 1275 if (fd == -1) 1276 errx(1, "read_disk(); fd == -1"); 1277 if (lseek(fd, sector * 512, 0) == -1) 1278 return (-1); 1279 return (read(fd, buf, 512)); 1280 } 1281 1282 int 1283 write_disk(off_t sector, void *buf) 1284 { 1285 1286 if (fd == -1) 1287 errx(1, "write_disk(); fd == -1"); 1288 if (lseek(fd, sector * 512, 0) == -1) 1289 return (-1); 1290 return (write(fd, buf, 512)); 1291 } 1292 1293 int 1294 get_params(void) 1295 { 1296 1297 if (ioctl(fd, DIOCGDEFLABEL, &disklabel) == -1) { 1298 warn("DIOCGDEFLABEL"); 1299 if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) { 1300 warn("DIOCGDINFO"); 1301 return (-1); 1302 } 1303 } 1304 1305 dos_cylinders = cylinders = disklabel.d_ncylinders; 1306 dos_heads = heads = disklabel.d_ntracks; 1307 dos_sectors = sectors = disklabel.d_nsectors; 1308 dos_cylindersectors = cylindersectors = heads * sectors; 1309 disksectors = disklabel.d_secperunit; 1310 1311 return (0); 1312 } 1313 1314 int 1315 read_s0(off_t offset, struct mboot *boot) 1316 { 1317 1318 if (read_disk(offset, boot->bootinst) == -1) { 1319 warn("can't read %s partition table", 1320 offset ? "extended" : "fdisk"); 1321 return (-1); 1322 } 1323 if (getshort(&boot->signature) != MBR_MAGIC) { 1324 warnx("invalid %s partition table found", 1325 offset ? "extended" : "fdisk"); 1326 return (-1); 1327 } 1328 return (0); 1329 } 1330 1331 int 1332 write_s0(void) 1333 { 1334 int flag, i; 1335 1336 /* 1337 * write enable label sector before write (if necessary), 1338 * disable after writing. 1339 * needed if the disklabel protected area also protects 1340 * sector 0. (e.g. empty disk) 1341 */ 1342 flag = 1; 1343 if (ioctl(fd, DIOCWLABEL, &flag) < 0) 1344 warn("DIOCWLABEL"); 1345 if (write_disk(0, mboot.bootinst) == -1) { 1346 warn("can't write fdisk partition table"); 1347 return -1; 1348 } 1349 for (i = bootsize; (i -= 0x200) > 0;) 1350 if (write_disk(i / 0x200, bootcode + i) == -1) { 1351 warn("can't write bootcode"); 1352 return -1; 1353 } 1354 flag = 0; 1355 if (ioctl(fd, DIOCWLABEL, &flag) < 0) 1356 warn("DIOCWLABEL"); 1357 return 0; 1358 } 1359 1360 int 1361 yesno(const char *str) 1362 { 1363 int ch, first; 1364 1365 printf("%s [n] ", str); 1366 1367 first = ch = getchar(); 1368 while (ch != '\n' && ch != EOF) 1369 ch = getchar(); 1370 return (first == 'y' || first == 'Y'); 1371 } 1372 1373 void 1374 decimal(const char *str, int *num) 1375 { 1376 int acc = 0; 1377 char *cp; 1378 1379 for (;; printf("%s is not a valid decimal number.\n", lbuf)) { 1380 printf("%s: [%d] ", str, *num); 1381 1382 fgets(lbuf, LBUF, stdin); 1383 lbuf[strlen(lbuf)-1] = '\0'; 1384 cp = lbuf; 1385 1386 cp += strspn(cp, " \t"); 1387 if (*cp == '\0') 1388 return; 1389 1390 if (!isdigit(*cp) && *cp != '-') 1391 continue; 1392 acc = strtol(lbuf, &cp, 10); 1393 1394 cp += strspn(cp, " \t"); 1395 if (*cp != '\0') 1396 continue; 1397 1398 *num = acc; 1399 return; 1400 } 1401 1402 } 1403 1404 int 1405 type_match(const void *key, const void *item) 1406 { 1407 const int *typep = key; 1408 const struct part_type *ptr = item; 1409 1410 if (*typep < ptr->type) 1411 return (-1); 1412 if (*typep > ptr->type) 1413 return (1); 1414 return (0); 1415 } 1416 1417 const char * 1418 get_type(int type) 1419 { 1420 struct part_type *ptr; 1421 1422 ptr = bsearch(&type, part_types, 1423 sizeof(part_types) / sizeof(struct part_type), 1424 sizeof(struct part_type), type_match); 1425 if (ptr == 0) 1426 return ("unknown"); 1427 return (ptr->name); 1428 } 1429