1 /* $NetBSD: fdisk.c,v 1.58 2002/11/30 13:47:19 fvdl 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.58 2002/11/30 13:47:19 fvdl 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 #if defined(__i386__) || defined(__x86_64__) 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 #if defined(__i386__) || defined(__x86_64__) 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 {0xaf, "Apple HFS"}, 236 {0xb1, reserved}, 237 {0xb3, reserved}, 238 {0xb4, reserved}, 239 {0xb6, reserved}, 240 {0xB7, "BSDI BSD/386 filesystem"}, 241 {0xB8, "BSDI BSD/386 swap"}, 242 {0xc0, "CTOS"}, 243 {0xC1, "DRDOS/sec (FAT-12)"}, 244 {0xC4, "DRDOS/sec (FAT-16, < 32M)"}, 245 {0xC6, "DRDOS/sec (FAT-16, >= 32M)"}, 246 {0xC7, "Syrinx (Cyrnix?) or HPFS disabled"}, 247 {0xd8, "CP/M 86"}, 248 {0xDB, "CP/M or Concurrent CP/M or Concurrent DOS or CTOS"}, 249 {0xE1, "DOS access or SpeedStor 12-bit FAT extended partition"}, 250 {0xE3, "DOS R/O or SpeedStor or Storage Dimensions"}, 251 {0xE4, "SpeedStor 16-bit FAT extended partition < 1024 cyl."}, 252 {0xe5, reserved}, 253 {0xe6, reserved}, 254 {0xeb, "BeOS"}, 255 {0xF1, "SpeedStor or Storage Dimensions"}, 256 {0xF2, "DOS 3.3+ Secondary"}, 257 {0xf3, reserved}, 258 {0xF4, "SpeedStor large partition or Storage Dimensions"}, 259 {0xf6, reserved}, 260 {0xFE, "SpeedStor >1024 cyl. or LANstep or IBM PS/2 IML"}, 261 {0xFF, "Xenix Bad Block Table"}, 262 }; 263 264 #define KNOWN_SYSIDS (sizeof(part_types)/sizeof(part_types[0])) 265 266 void usage(void); 267 void print_s0(int); 268 void print_part(int); 269 void print_mbr_partition(struct mbr_partition *, off_t, off_t, int); 270 int read_boot(const char *, void *, size_t); 271 void init_sector0(int, int); 272 void intuit_translated_geometry(void); 273 void get_geometry(void); 274 void get_diskname(const char *, char *, size_t); 275 int try_heads(quad_t, quad_t, quad_t, quad_t, quad_t, quad_t, quad_t, 276 quad_t); 277 int try_sectors(quad_t, quad_t, quad_t, quad_t, quad_t); 278 void change_part(int, int, int, int); 279 void print_params(void); 280 void change_active(int); 281 void get_params_to_use(void); 282 void dos(int, unsigned char *, unsigned char *, unsigned char *); 283 int open_disk(int); 284 int read_disk(off_t, void *); 285 int write_disk(off_t, void *); 286 int get_params(void); 287 int read_s0(off_t, struct mboot *); 288 int write_s0(void); 289 int yesno(const char *); 290 void decimal(const char *, int *); 291 int type_match(const void *, const void *); 292 const char *get_type(int); 293 int get_mapping(int, int *, int *, int *, unsigned long *); 294 #if defined(__i386__) || defined(__x86_64__) 295 void configure_bootsel(void); 296 #endif 297 298 static unsigned short getshort(void *); 299 static void putshort(void *p, unsigned short); 300 static unsigned long getlong(void *); 301 static void putlong(void *, unsigned long); 302 303 304 int main(int, char *[]); 305 306 int 307 main(int argc, char *argv[]) 308 { 309 int ch, part, mib[2]; 310 size_t len; 311 char *root_device; 312 313 int csysid, cstart, csize; /* For the b_flag. */ 314 315 mib[0] = CTL_KERN; 316 mib[1] = KERN_ROOT_DEVICE; 317 if (sysctl(mib, 2, NULL, &len, NULL, 0) != -1 && 318 (root_device = malloc(len)) != NULL && 319 sysctl(mib, 2, root_device, &len, NULL, 0) != -1) 320 disk = root_device; 321 322 a_flag = i_flag = u_flag = sh_flag = f_flag = s_flag = b_flag = 0; 323 csysid = cstart = csize = 0; 324 while ((ch = getopt(argc, argv, OPTIONS)) != -1) 325 switch (ch) { 326 case '0': 327 partition = 0; 328 break; 329 case '1': 330 partition = 1; 331 break; 332 case '2': 333 partition = 2; 334 break; 335 case '3': 336 partition = 3; 337 break; 338 #if defined(__i386__) || defined(__x86_64__) 339 case 'B': 340 B_flag = 1; 341 break; 342 #endif 343 case 'S': 344 sh_flag = 1; 345 break; 346 case 'a': 347 a_flag = 1; 348 break; 349 case 'f': 350 f_flag = 1; 351 break; 352 case 'i': 353 i_flag = 1; 354 break; 355 case 'l': 356 for (len = 0; len < KNOWN_SYSIDS; len++) 357 printf("%03d %s\n", part_types[len].type, 358 part_types[len].name); 359 return 0; 360 case 'u': 361 u_flag = 1; 362 break; 363 case 's': 364 s_flag = 1; 365 if (sscanf(optarg, "%d/%d/%d", &csysid, &cstart, 366 &csize) != 3) 367 errx(1, "Bad argument to the -s flag."); 368 break; 369 case 'b': 370 b_flag = 1; 371 if (sscanf(optarg, "%d/%d/%d", &b_cyl, &b_head, 372 &b_sec) != 3) 373 errx(1, "Bad argument to the -b flag."); 374 if (b_cyl > MAXCYL) 375 b_cyl = MAXCYL; 376 break; 377 case 'c': 378 bootsize = read_boot(optarg, bootcode, sizeof bootcode); 379 break; 380 default: 381 usage(); 382 } 383 argc -= optind; 384 argv += optind; 385 386 if (sh_flag && (a_flag || i_flag || u_flag || f_flag || s_flag)) 387 usage(); 388 389 if (B_flag && (a_flag || i_flag || u_flag || f_flag || s_flag)) 390 usage(); 391 392 if (partition == -1 && s_flag) { 393 warnx("-s flag requires a partition selected."); 394 usage(); 395 } 396 397 if (argc > 0) 398 disk = argv[0]; 399 400 if (open_disk(B_flag || a_flag || i_flag || u_flag) < 0) 401 exit(1); 402 403 if (read_s0(0, &mboot)) 404 init_sector0(sectors > 63 ? 63 : sectors, 1); 405 406 #if defined(__i386__) || defined(__x86_64__) 407 get_geometry(); 408 #else 409 intuit_translated_geometry(); 410 #endif 411 412 413 if ((i_flag || u_flag) && (!f_flag || b_flag)) 414 get_params_to_use(); 415 416 if (i_flag) 417 init_sector0(dos_sectors > 63 ? 63 : dos_sectors, 0); 418 419 /* Do the update stuff! */ 420 if (u_flag) { 421 if (!f_flag) 422 printf("Partition table:\n"); 423 if (partition == -1) 424 for (part = 0; part < NMBRPART; part++) 425 change_part(part,-1, -1, -1); 426 else 427 change_part(partition, csysid, cstart, csize); 428 } else 429 if (!i_flag) 430 print_s0(partition); 431 432 if (a_flag) 433 change_active(partition); 434 435 #if defined(__i386__) || defined(__x86_64__) 436 if (B_flag) { 437 configure_bootsel(); 438 if (B_flag && bootsel_modified) 439 write_s0(); 440 } 441 #endif 442 443 if (u_flag || a_flag || i_flag) { 444 if (!f_flag) { 445 printf("\nWe haven't written the MBR back to disk " 446 "yet. This is your last chance.\n"); 447 print_s0(-1); 448 if (yesno("Should we write new partition table?")) 449 write_s0(); 450 } else 451 write_s0(); 452 } 453 454 exit(0); 455 } 456 457 void 458 usage(void) 459 { 460 461 (void)fprintf(stderr, "Usage: %s [-aiufBS] [-0|-1|-2|-3] " 462 "[-b cylinders/heads/sectors]\n" 463 "\t%s [-s id/start/size] [-c bootcode] [device]\n", 464 getprogname(), getprogname()); 465 exit(1); 466 } 467 468 void 469 print_s0(int which) 470 { 471 int part; 472 473 print_params(); 474 if (!sh_flag) 475 printf("Partition table:\n"); 476 if (which == -1) { 477 for (part = 0; part < NMBRPART; part++) { 478 if (!sh_flag) 479 printf("%d: ", part); 480 print_part(part); 481 } 482 } else 483 print_part(which); 484 } 485 486 static unsigned short 487 getshort(void *p) 488 { 489 unsigned char *cp = p; 490 491 return cp[0] | (cp[1] << 8); 492 } 493 494 static void 495 putshort(void *p, unsigned short l) 496 { 497 unsigned char *cp = p; 498 499 *cp++ = l; 500 *cp++ = l >> 8; 501 } 502 503 static unsigned long 504 getlong(void *p) 505 { 506 unsigned char *cp = p; 507 508 return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24); 509 } 510 511 static void 512 putlong(void *p, unsigned long l) 513 { 514 unsigned char *cp = p; 515 516 *cp++ = l; 517 *cp++ = l >> 8; 518 *cp++ = l >> 16; 519 *cp++ = l >> 24; 520 } 521 522 void 523 print_part(int part) 524 { 525 struct mbr_partition *partp; 526 int empty; 527 528 partp = &mboot.parts[part]; 529 empty = (partp->mbrp_typ == 0); 530 531 if (sh_flag) { 532 if (empty) { 533 printf("PART%dSIZE=0\n", part); 534 return; 535 } 536 537 printf("PART%dID=%d\n", part, partp->mbrp_typ); 538 printf("PART%dSIZE=%ld\n", part, getlong(&partp->mbrp_size)); 539 printf("PART%dSTART=%ld\n", part, getlong(&partp->mbrp_start)); 540 printf("PART%dFLAG=0x%x\n", part, partp->mbrp_flag); 541 printf("PART%dBCYL=%d\n", part, MBR_PCYL(partp->mbrp_scyl, 542 partp->mbrp_ssect)); 543 printf("PART%dBHEAD=%d\n", part, partp->mbrp_shd); 544 printf("PART%dBSEC=%d\n", part, MBR_PSECT(partp->mbrp_ssect)); 545 printf("PART%dECYL=%d\n", part, MBR_PCYL(partp->mbrp_ecyl, 546 partp->mbrp_esect)); 547 printf("PART%dEHEAD=%d\n", part, partp->mbrp_ehd); 548 printf("PART%dESEC=%d\n", part, MBR_PSECT(partp->mbrp_esect)); 549 return; 550 } 551 print_mbr_partition(partp, 0, 0, 0); 552 } 553 554 void 555 print_mbr_partition(struct mbr_partition *partp, 556 off_t offset, off_t exoffset, int indent) 557 { 558 int empty; 559 off_t start; 560 561 empty = (partp->mbrp_typ == 0); 562 if (MBR_IS_EXTENDED(partp->mbrp_typ)) 563 start = (off_t)getlong(&partp->mbrp_start) + exoffset; 564 else 565 start = (off_t)getlong(&partp->mbrp_start) + offset; 566 if (empty) { 567 printf("<UNUSED>\n"); 568 return; 569 } 570 printf("sysid %d (%s)\n", 571 partp->mbrp_typ, get_type(partp->mbrp_typ)); 572 printf("%*s start %lld, size %ld (%ld MB), flag 0x%x\n", 573 indent, "", 574 (long long)start, getlong(&partp->mbrp_size), 575 getlong(&partp->mbrp_size) * 512 / (1024 * 1024), partp->mbrp_flag); 576 printf("%*s beg: cylinder %4d, head %3d, sector %2d\n", 577 indent, "", 578 MBR_PCYL(partp->mbrp_scyl, partp->mbrp_ssect), 579 partp->mbrp_shd, MBR_PSECT(partp->mbrp_ssect)); 580 printf("%*s end: cylinder %4d, head %3d, sector %2d\n", 581 indent, "", 582 MBR_PCYL(partp->mbrp_ecyl, partp->mbrp_esect), 583 partp->mbrp_ehd, MBR_PSECT(partp->mbrp_esect)); 584 585 if (MBR_IS_EXTENDED(partp->mbrp_typ)) { 586 struct mboot eboot; 587 int part; 588 589 printf("%*s Extended partition table:\n", indent, ""); 590 if (read_s0(start, &eboot) == -1) 591 return; 592 indent += 8; 593 if (exoffset == 0) 594 exoffset = start; 595 for (part = 0; part < NMBRPART; part++) { 596 printf("%*s%d: ", indent, "", part); 597 print_mbr_partition(&eboot.parts[part], 598 start, exoffset, indent); 599 } 600 } 601 } 602 603 int 604 read_boot(const char *name, void *buf, size_t len) 605 { 606 int bfd, ret; 607 struct stat st; 608 609 if ((bfd = open(name, O_RDONLY)) < 0) 610 err(1, "%s", name); 611 if (fstat(bfd, &st) == -1) 612 err(1, "%s", name); 613 if (st.st_size > (off_t)len) 614 errx(1, "%s: bootcode too large", name); 615 ret = st.st_size; 616 if (ret < 0x200) 617 errx(1, "%s: bootcode too small", name); 618 if (read(bfd, buf, len) != ret) 619 err(1, "%s", name); 620 close(bfd); 621 622 /* 623 * Do some sanity checking here 624 */ 625 if (getshort(bootcode + MBR_MAGICOFF) != MBR_MAGIC) 626 errx(1, "%s: invalid magic", name); 627 ret = (ret + 0x1ff) / 0x200; 628 ret *= 0x200; 629 return ret; 630 } 631 632 void 633 init_sector0(int start, int dopart) 634 { 635 int i; 636 637 #ifdef DEFAULT_BOOTCODE 638 if (!bootsize) 639 bootsize = read_boot(DEFAULT_BOOTCODE, bootcode, 640 sizeof bootcode); 641 #endif 642 643 memcpy(mboot.bootinst, bootcode, sizeof(mboot.bootinst)); 644 putshort(&mboot.signature, MBR_MAGIC); 645 646 if (dopart) 647 for (i = 0; i < 4; i++) 648 memset(&mboot.parts[i], 0, sizeof(mboot.parts[i])); 649 650 } 651 652 #if defined(__i386__) || defined(__x86_64__) 653 654 void 655 get_diskname(const char *fullname, char *diskname, size_t size) 656 { 657 const char *p, *p2; 658 size_t len; 659 660 p = strrchr(fullname, '/'); 661 if (p == NULL) 662 p = fullname; 663 else 664 p++; 665 666 if (*p == 0) { 667 strncpy(diskname, fullname, size - 1); 668 diskname[size - 1] = '\0'; 669 return; 670 } 671 672 if (*p == 'r') 673 p++; 674 675 for (p2 = p; *p2 != 0; p2++) 676 if (isdigit(*p2)) 677 break; 678 if (*p2 == 0) { 679 /* XXX invalid diskname? */ 680 strncpy(diskname, fullname, size - 1); 681 diskname[size - 1] = '\0'; 682 return; 683 } 684 while (isdigit(*p2)) 685 p2++; 686 687 len = p2 - p; 688 if (len > size) { 689 /* XXX */ 690 strncpy(diskname, fullname, size - 1); 691 diskname[size - 1] = '\0'; 692 return; 693 } 694 695 strncpy(diskname, p, len); 696 diskname[len] = 0; 697 } 698 699 void 700 get_geometry(void) 701 { 702 int mib[2], i; 703 size_t len; 704 struct disklist *dl; 705 struct biosdisk_info *bip; 706 struct nativedisk_info *nip; 707 char diskname[8]; 708 709 mib[0] = CTL_MACHDEP; 710 mib[1] = CPU_DISKINFO; 711 if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0) { 712 intuit_translated_geometry(); 713 return; 714 } 715 dl = (struct disklist *) malloc(len); 716 sysctl(mib, 2, dl, &len, NULL, 0); 717 718 get_diskname(disk, diskname, sizeof diskname); 719 720 for (i = 0; i < dl->dl_nnativedisks; i++) { 721 nip = &dl->dl_nativedisks[i]; 722 if (strcmp(diskname, nip->ni_devname)) 723 continue; 724 /* 725 * XXX listing possible matches is better. This is ok 726 * for now because the user has a chance to change 727 * it later. 728 */ 729 if (nip->ni_nmatches != 0) { 730 bip = &dl->dl_biosdisks[nip->ni_biosmatches[0]]; 731 dos_cylinders = bip->bi_cyl; 732 dos_heads = bip->bi_head; 733 dos_sectors = bip->bi_sec; 734 dos_cylindersectors = bip->bi_head * bip->bi_sec; 735 return; 736 } 737 } 738 /* Allright, allright, make a stupid guess.. */ 739 intuit_translated_geometry(); 740 } 741 742 void 743 configure_bootsel(void) 744 { 745 struct mbr_bootsel *mbs = 746 (struct mbr_bootsel *)&mboot.bootinst[MBR_BOOTSELOFF]; 747 int i, nused, firstpart = -1, item; 748 char desc[PARTNAMESIZE + 2], *p; 749 int timo, entry_changed = 0; 750 751 for (i = nused = 0; i < NMBRPART; ++i) { 752 if (mboot.parts[i].mbrp_typ != 0) { 753 if (firstpart == -1) 754 firstpart = i; 755 nused++; 756 } 757 } 758 759 if (nused == 0) { 760 warnx("No used partitions found. Partition the disk first."); 761 return; 762 } 763 764 if (mbs->magic != MBR_MAGIC) { 765 if (!yesno("Bootselector not yet installed. Install it now?")) { 766 warnx("Bootselector not installed."); 767 return; 768 } 769 bootsize = read_boot(DEFAULT_BOOTSELCODE, bootcode, 770 sizeof bootcode); 771 memcpy(mboot.bootinst, bootcode, sizeof(mboot.bootinst)); 772 bootsel_modified = 1; 773 mbs->flags |= BFL_SELACTIVE; 774 } else { 775 if (mbs->flags & BFL_SELACTIVE) { 776 printf("The bootselector is installed and active."); 777 if (!yesno("Do you want to change its settings?")) { 778 if (yesno("Do you want to deactivate it?")) { 779 mbs->flags &= ~BFL_SELACTIVE; 780 bootsel_modified = 1; 781 goto done; 782 } 783 return; 784 } 785 } else { 786 printf("The bootselector is installed but not active."); 787 if (yesno("Do you want to activate it?")) { 788 mbs->flags |= BFL_SELACTIVE; 789 bootsel_modified = 1; 790 } 791 if (!yesno("Do you want to change its settings?")) 792 goto done; 793 } 794 } 795 796 printf("\n\nPartition table:\n"); 797 for (i = 0; i < NMBRPART; i++) { 798 printf("%d: ", i); 799 print_part(i); 800 } 801 802 printf("\n\nCurrent boot selection menu option names:\n"); 803 for (i = 0; i < NMBRPART; i++) { 804 if (mbs->nametab[i][0] != 0) 805 printf("%d: %s\n", i, &mbs->nametab[i][0]); 806 else 807 printf("%d: <UNUSED>\n", i); 808 } 809 printf("\n"); 810 811 item = firstpart; 812 813 editentries: 814 while (1) { 815 decimal("Change which entry (-1 quits)?", &item); 816 if (item == -1) 817 break; 818 if (item < 0 || item >= NMBRPART) { 819 printf("Invalid entry number\n"); 820 item = -1; 821 continue; 822 } 823 if (mboot.parts[item].mbrp_typ == 0) { 824 printf("The partition entry is unused\n"); 825 item = -1; 826 continue; 827 } 828 829 printf("Enter descriptions (max. 8 characters): "); 830 rewind(stdin); 831 if (!fgets(desc, PARTNAMESIZE + 1, stdin)) 832 errx(1, "EOF"); 833 fpurge(stdin); 834 p = strchr(desc, '\n'); 835 if (p != NULL) 836 *p = 0; 837 strcpy(&mbs->nametab[item][0], desc); 838 entry_changed = bootsel_modified = 1; 839 840 item++; 841 } 842 843 if (entry_changed) 844 printf("Boot selection menu option names are now:\n"); 845 846 firstpart = -1; 847 for (i = 0; i < NMBRPART; i++) { 848 if (mbs->nametab[i][0] != 0) { 849 firstpart = i; 850 if (entry_changed) 851 printf("%d: %s\n", i, &mbs->nametab[i][0]); 852 } else { 853 if (entry_changed) 854 printf("%d: <UNUSED>\n", i); 855 } 856 } 857 if (entry_changed) 858 printf("\n"); 859 860 if (firstpart == -1) { 861 printf("All menu entries are now inactive.\n"); 862 if (!yesno("Are you sure about this?")) 863 goto editentries; 864 } else { 865 if (!(mbs->flags & BFL_SELACTIVE)) { 866 printf("The bootselector is not yet active.\n"); 867 if (yesno("Activate it now?")) 868 mbs->flags |= BFL_SELACTIVE; 869 } 870 } 871 872 /* bootsel is dirty from here on out. */ 873 bootsel_modified = 1; 874 875 /* The timeout value is in ticks, 18.2 Hz. Avoid using floats. */ 876 timo = ((1000 * mbs->timeo) / 18200); 877 do { 878 decimal("Timeout value", &timo); 879 } while (timo < 0 || timo > 3600); 880 mbs->timeo = (u_int16_t)((timo * 18200) / 1000); 881 882 printf("Select the default boot option. Options are:\n\n"); 883 for (i = 0; i < NMBRPART; i++) { 884 if (mbs->nametab[i][0] != 0) 885 printf("%d: %s\n", i, &mbs->nametab[i][0]); 886 } 887 for (i = 4; i < 10; i++) 888 printf("%d: Harddisk %d\n", i, i - 4); 889 printf("10: The first active partition\n"); 890 891 if (mbs->defkey == SCAN_ENTER) 892 item = 10; 893 else 894 item = mbs->defkey - SCAN_F1; 895 896 if (item < 0 || item > 10 || mbs->nametab[item][0] == 0) 897 item = 10; 898 899 do { 900 decimal("Default boot option", &item); 901 } while (item < 0 || item > 10 || 902 (item <= 3 && mbs->nametab[item][0] == 0)); 903 904 if (item == 10) 905 mbs->defkey = SCAN_ENTER; 906 else 907 mbs->defkey = SCAN_F1 + item; 908 909 done: 910 for (i = 0; i < NMBRPART; i++) { 911 if (mboot.parts[i].mbrp_typ != 0 && 912 mboot.parts[i].mbrp_start >= 913 (unsigned)(dos_cylinders * dos_heads * dos_sectors)) { 914 mbs->flags |= BFL_EXTINT13; 915 break; 916 } 917 } 918 919 if (bootsel_modified != 0 && !yesno("Update the bootselector?")) 920 bootsel_modified = 0; 921 } 922 #endif 923 924 925 /* Prerequisite: the disklabel parameters and master boot record must 926 * have been read (i.e. dos_* and mboot are meaningful). 927 * Specification: modifies dos_cylinders, dos_heads, dos_sectors, and 928 * dos_cylindersectors to be consistent with what the 929 * partition table is using, if we can find a geometry 930 * which is consistent with all partition table entries. 931 * We may get the number of cylinders slightly wrong (in 932 * the conservative direction). The idea is to be able 933 * to create a NetBSD partition on a disk we don't know 934 * the translated geometry of. 935 * This whole routine should be replaced with a kernel interface to get 936 * the BIOS geometry (which in turn requires modifications to the i386 937 * boot loader to pass in the BIOS geometry for each disk). */ 938 void 939 intuit_translated_geometry(void) 940 { 941 942 int xcylinders = -1, xheads = -1, xsectors = -1, i, j; 943 int c1, h1, s1, c2, h2, s2; 944 long a1, a2; 945 quad_t num, denom; 946 947 /* Try to deduce the number of heads from two different mappings. */ 948 for (i = 0; i < NMBRPART * 2; i++) { 949 if (get_mapping(i, &c1, &h1, &s1, &a1) < 0) 950 continue; 951 for (j = 0; j < 8; j++) { 952 if (get_mapping(j, &c2, &h2, &s2, &a2) < 0) 953 continue; 954 num = (quad_t)h1*(a2-s2) - (quad_t)h2*(a1-s1); 955 denom = (quad_t)c2*(a1-s1) - (quad_t)c1*(a2-s2); 956 if (denom != 0 && num % denom == 0) { 957 xheads = num / denom; 958 break; 959 } 960 } 961 if (xheads != -1) 962 break; 963 } 964 965 if (xheads == -1) 966 return; 967 968 /* Now figure out the number of sectors from a single mapping. */ 969 for (i = 0; i < NMBRPART * 2; i++) { 970 if (get_mapping(i, &c1, &h1, &s1, &a1) < 0) 971 continue; 972 num = a1 - s1; 973 denom = c1 * xheads + h1; 974 if (denom != 0 && num % denom == 0) { 975 xsectors = num / denom; 976 break; 977 } 978 } 979 980 if (xsectors == -1) 981 return; 982 983 /* Estimate the number of cylinders. */ 984 xcylinders = disklabel.d_secperunit / xheads / xsectors; 985 986 /* Now verify consistency with each of the partition table entries. 987 * Be willing to shove cylinders up a little bit to make things work, 988 * but translation mismatches are fatal. */ 989 for (i = 0; i < NMBRPART * 2; i++) { 990 if (get_mapping(i, &c1, &h1, &s1, &a1) < 0) 991 continue; 992 if (xsectors * (c1 * xheads + h1) + s1 != a1) 993 return; 994 if (c1 >= xcylinders) 995 xcylinders = c1 + 1; 996 } 997 998 /* Everything checks out. Reset the geometry to use for further 999 * calculations. */ 1000 dos_cylinders = xcylinders; 1001 dos_heads = xheads; 1002 dos_sectors = xsectors; 1003 dos_cylindersectors = xheads * xsectors; 1004 } 1005 1006 /* For the purposes of intuit_translated_geometry(), treat the partition 1007 * table as a list of eight mapping between (cylinder, head, sector) 1008 * triplets and absolute sectors. Get the relevant geometry triplet and 1009 * absolute sectors for a given entry, or return -1 if it isn't present. 1010 * Note: for simplicity, the returned sector is 0-based. */ 1011 int 1012 get_mapping(int i, int *cylinder, int *head, int *sector, 1013 unsigned long *absolute) 1014 { 1015 struct mbr_partition *part = &mboot.parts[i / 2]; 1016 1017 if (part->mbrp_typ == 0) 1018 return -1; 1019 if (i % 2 == 0) { 1020 *cylinder = MBR_PCYL(part->mbrp_scyl, part->mbrp_ssect); 1021 *head = part->mbrp_shd; 1022 *sector = MBR_PSECT(part->mbrp_ssect) - 1; 1023 *absolute = getlong(&part->mbrp_start); 1024 } else { 1025 *cylinder = MBR_PCYL(part->mbrp_ecyl, part->mbrp_esect); 1026 *head = part->mbrp_ehd; 1027 *sector = MBR_PSECT(part->mbrp_esect) - 1; 1028 *absolute = getlong(&part->mbrp_start) 1029 + getlong(&part->mbrp_size) - 1; 1030 } 1031 return 0; 1032 } 1033 1034 void 1035 change_part(int part, int csysid, int cstart, int csize) 1036 { 1037 struct mbr_partition *partp; 1038 1039 partp = &mboot.parts[part]; 1040 1041 if (s_flag) { 1042 if (csysid == 0 && cstart == 0 && csize == 0) 1043 memset(partp, 0, sizeof *partp); 1044 else { 1045 partp->mbrp_typ = csysid; 1046 #if 0 1047 checkcyl(cstart / dos_cylindersectors); 1048 #endif 1049 putlong(&partp->mbrp_start, cstart); 1050 putlong(&partp->mbrp_size, csize); 1051 dos(getlong(&partp->mbrp_start), 1052 &partp->mbrp_scyl, &partp->mbrp_shd, &partp->mbrp_ssect); 1053 dos(getlong(&partp->mbrp_start) 1054 + getlong(&partp->mbrp_size) - 1, 1055 &partp->mbrp_ecyl, &partp->mbrp_ehd, &partp->mbrp_esect); 1056 } 1057 if (f_flag) 1058 return; 1059 } 1060 1061 printf("The data for partition %d is:\n", part); 1062 print_part(part); 1063 if (!u_flag || !yesno("Do you want to change it?")) 1064 return; 1065 1066 do { 1067 { 1068 int sysid, start, size; 1069 1070 sysid = partp->mbrp_typ, 1071 start = getlong(&partp->mbrp_start), 1072 size = getlong(&partp->mbrp_size); 1073 decimal("sysid", &sysid); 1074 decimal("start", &start); 1075 decimal("size", &size); 1076 partp->mbrp_typ = sysid; 1077 putlong(&partp->mbrp_start, start); 1078 putlong(&partp->mbrp_size, size); 1079 } 1080 1081 if (yesno("Explicitly specify beg/end address?")) { 1082 int tsector, tcylinder, thead; 1083 1084 tcylinder = MBR_PCYL(partp->mbrp_scyl, 1085 partp->mbrp_ssect); 1086 thead = partp->mbrp_shd; 1087 tsector = MBR_PSECT(partp->mbrp_ssect); 1088 decimal("beginning cylinder", &tcylinder); 1089 #if 0 1090 checkcyl(tcylinder); 1091 #endif 1092 decimal("beginning head", &thead); 1093 decimal("beginning sector", &tsector); 1094 partp->mbrp_scyl = DOSCYL(tcylinder); 1095 partp->mbrp_shd = thead; 1096 partp->mbrp_ssect = DOSSECT(tsector, tcylinder); 1097 1098 tcylinder = MBR_PCYL(partp->mbrp_ecyl, 1099 partp->mbrp_esect); 1100 thead = partp->mbrp_ehd; 1101 tsector = MBR_PSECT(partp->mbrp_esect); 1102 decimal("ending cylinder", &tcylinder); 1103 decimal("ending head", &thead); 1104 decimal("ending sector", &tsector); 1105 partp->mbrp_ecyl = DOSCYL(tcylinder); 1106 partp->mbrp_ehd = thead; 1107 partp->mbrp_esect = DOSSECT(tsector, tcylinder); 1108 } else { 1109 1110 if (partp->mbrp_typ == 0 1111 && getlong(&partp->mbrp_start) == 0 1112 && getlong(&partp->mbrp_size) == 0) 1113 memset(partp, 0, sizeof *partp); 1114 else { 1115 #if 0 1116 checkcyl(getlong(&partp->mbrp_start) 1117 / dos_cylindersectors); 1118 #endif 1119 dos(getlong(&partp->mbrp_start), 1120 &partp->mbrp_scyl, 1121 &partp->mbrp_shd, &partp->mbrp_ssect); 1122 dos(getlong(&partp->mbrp_start) 1123 + getlong(&partp->mbrp_size) - 1, 1124 &partp->mbrp_ecyl, &partp->mbrp_ehd, 1125 &partp->mbrp_esect); 1126 } 1127 } 1128 1129 print_part(part); 1130 } while (!yesno("Is this entry okay?")); 1131 } 1132 1133 void 1134 print_params(void) 1135 { 1136 1137 if (sh_flag) { 1138 printf ("DLCYL=%d\nDLHEAD=%d\nDLSEC=%d\nDLSIZE=%d\n", 1139 cylinders, heads, sectors, disksectors); 1140 printf ("BCYL=%d\nBHEAD=%d\nBSEC=%d\n", 1141 dos_cylinders, dos_heads, dos_sectors); 1142 return; 1143 } 1144 1145 /* Not sh_flag */ 1146 printf("Disk: %s\n", disk); 1147 printf("NetBSD disklabel disk geometry:\n"); 1148 printf("cylinders: %d heads: %d sectors/track: %d (%d sectors/cylinder)\n\n", 1149 cylinders, heads, sectors, cylindersectors); 1150 printf("BIOS disk geometry:\n"); 1151 printf("cylinders: %d heads: %d sectors/track: %d (%d sectors/cylinder)\n\n", 1152 dos_cylinders, dos_heads, dos_sectors, dos_cylindersectors); 1153 } 1154 1155 void 1156 change_active(int which) 1157 { 1158 struct mbr_partition *partp; 1159 int part; 1160 int active = 4; 1161 1162 partp = &mboot.parts[0]; 1163 1164 if (a_flag && which != -1) 1165 active = which; 1166 else { 1167 for (part = 0; part < NMBRPART; part++) 1168 if (partp[part].mbrp_flag & ACTIVE) 1169 active = part; 1170 } 1171 if (!f_flag) { 1172 if (yesno("Do you want to change the active partition?")) { 1173 printf ("Choosing 4 will make no partition active.\n"); 1174 do { 1175 decimal("active partition", &active); 1176 } while (!yesno("Are you happy with this choice?")); 1177 } else 1178 return; 1179 } else 1180 if (active != 4) 1181 printf ("Making partition %d active.\n", active); 1182 1183 for (part = 0; part < NMBRPART; part++) 1184 partp[part].mbrp_flag &= ~ACTIVE; 1185 if (active < 4) 1186 partp[active].mbrp_flag |= ACTIVE; 1187 } 1188 1189 void 1190 get_params_to_use(void) 1191 { 1192 1193 if (b_flag) { 1194 dos_cylinders = b_cyl; 1195 dos_heads = b_head; 1196 dos_sectors = b_sec; 1197 dos_cylindersectors = dos_heads * dos_sectors; 1198 return; 1199 } 1200 1201 print_params(); 1202 if (yesno("Do you want to change our idea of what BIOS thinks?")) { 1203 do { 1204 decimal("BIOS's idea of #cylinders", &dos_cylinders); 1205 decimal("BIOS's idea of #heads", &dos_heads); 1206 decimal("BIOS's idea of #sectors", &dos_sectors); 1207 dos_cylindersectors = dos_heads * dos_sectors; 1208 print_params(); 1209 } while (!yesno("Are you happy with this choice?")); 1210 } 1211 } 1212 1213 /***********************************************\ 1214 * Change real numbers into strange dos numbers * 1215 \***********************************************/ 1216 void 1217 dos(int sector, unsigned char *cylinderp, unsigned char *headp, 1218 unsigned char *sectorp) 1219 { 1220 int cylinder, head; 1221 int biosmaxsec; 1222 1223 biosmaxsec = dos_cylinders * dos_heads * dos_sectors - 1; 1224 if (sector > biosmaxsec) 1225 sector = biosmaxsec; 1226 1227 cylinder = sector / dos_cylindersectors; 1228 1229 sector -= cylinder * dos_cylindersectors; 1230 1231 head = sector / dos_sectors; 1232 sector -= head * dos_sectors; 1233 1234 *cylinderp = DOSCYL(cylinder); 1235 *headp = head; 1236 *sectorp = DOSSECT(sector + 1, cylinder); 1237 } 1238 1239 #if 0 1240 void 1241 checkcyl(int cyl) 1242 { 1243 1244 if (cyl >= MAXCYL) 1245 warnx("partition start beyond BIOS limit"); 1246 } 1247 #endif 1248 1249 int fd = -1; 1250 1251 int 1252 open_disk(int update) 1253 { 1254 static char namebuf[MAXPATHLEN + 1]; 1255 1256 fd = opendisk(disk, update ? O_RDWR : O_RDONLY, namebuf, 1257 sizeof(namebuf), 0); 1258 if (fd < 0) { 1259 if (errno == ENODEV) 1260 warnx("%s is not a character device", namebuf); 1261 else 1262 warn("%s", namebuf); 1263 return (-1); 1264 } 1265 disk = namebuf; 1266 if (get_params() == -1) { 1267 close(fd); 1268 return (-1); 1269 } 1270 return (0); 1271 } 1272 1273 int 1274 read_disk(off_t sector, void *buf) 1275 { 1276 1277 if (fd == -1) 1278 errx(1, "read_disk(); fd == -1"); 1279 if (lseek(fd, sector * 512, 0) == -1) 1280 return (-1); 1281 return (read(fd, buf, 512)); 1282 } 1283 1284 int 1285 write_disk(off_t sector, void *buf) 1286 { 1287 1288 if (fd == -1) 1289 errx(1, "write_disk(); fd == -1"); 1290 if (lseek(fd, sector * 512, 0) == -1) 1291 return (-1); 1292 return (write(fd, buf, 512)); 1293 } 1294 1295 int 1296 get_params(void) 1297 { 1298 1299 if (ioctl(fd, DIOCGDEFLABEL, &disklabel) == -1) { 1300 warn("DIOCGDEFLABEL"); 1301 if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) { 1302 warn("DIOCGDINFO"); 1303 return (-1); 1304 } 1305 } 1306 1307 dos_cylinders = cylinders = disklabel.d_ncylinders; 1308 dos_heads = heads = disklabel.d_ntracks; 1309 dos_sectors = sectors = disklabel.d_nsectors; 1310 dos_cylindersectors = cylindersectors = heads * sectors; 1311 disksectors = disklabel.d_secperunit; 1312 1313 return (0); 1314 } 1315 1316 int 1317 read_s0(off_t offset, struct mboot *boot) 1318 { 1319 1320 if (read_disk(offset, boot->bootinst) == -1) { 1321 warn("can't read %s partition table", 1322 offset ? "extended" : "fdisk"); 1323 return (-1); 1324 } 1325 if (getshort(&boot->signature) != MBR_MAGIC) { 1326 warnx("invalid %s partition table found", 1327 offset ? "extended" : "fdisk"); 1328 return (-1); 1329 } 1330 return (0); 1331 } 1332 1333 int 1334 write_s0(void) 1335 { 1336 int flag, i; 1337 1338 /* 1339 * write enable label sector before write (if necessary), 1340 * disable after writing. 1341 * needed if the disklabel protected area also protects 1342 * sector 0. (e.g. empty disk) 1343 */ 1344 flag = 1; 1345 if (ioctl(fd, DIOCWLABEL, &flag) < 0) 1346 warn("DIOCWLABEL"); 1347 if (write_disk(0, mboot.bootinst) == -1) { 1348 warn("can't write fdisk partition table"); 1349 return -1; 1350 } 1351 for (i = bootsize; (i -= 0x200) > 0;) 1352 if (write_disk(i / 0x200, bootcode + i) == -1) { 1353 warn("can't write bootcode"); 1354 return -1; 1355 } 1356 flag = 0; 1357 if (ioctl(fd, DIOCWLABEL, &flag) < 0) 1358 warn("DIOCWLABEL"); 1359 return 0; 1360 } 1361 1362 int 1363 yesno(const char *str) 1364 { 1365 int ch, first; 1366 1367 printf("%s [n] ", str); 1368 1369 first = ch = getchar(); 1370 while (ch != '\n' && ch != EOF) 1371 ch = getchar(); 1372 if (ch == EOF) 1373 errx(1, "EOF"); 1374 return (first == 'y' || first == 'Y'); 1375 } 1376 1377 void 1378 decimal(const char *str, int *num) 1379 { 1380 int acc = 0; 1381 char *cp; 1382 1383 for (;; printf("%s is not a valid decimal number.\n", lbuf)) { 1384 printf("%s: [%d] ", str, *num); 1385 1386 if (!fgets(lbuf, LBUF, stdin)) 1387 errx(1, "EOF"); 1388 lbuf[strlen(lbuf)-1] = '\0'; 1389 cp = lbuf; 1390 1391 cp += strspn(cp, " \t"); 1392 if (*cp == '\0') 1393 return; 1394 1395 if (!isdigit(*cp) && *cp != '-') 1396 continue; 1397 acc = strtol(lbuf, &cp, 10); 1398 1399 cp += strspn(cp, " \t"); 1400 if (*cp != '\0') 1401 continue; 1402 1403 *num = acc; 1404 return; 1405 } 1406 1407 } 1408 1409 int 1410 type_match(const void *key, const void *item) 1411 { 1412 const int *typep = key; 1413 const struct part_type *ptr = item; 1414 1415 if (*typep < ptr->type) 1416 return (-1); 1417 if (*typep > ptr->type) 1418 return (1); 1419 return (0); 1420 } 1421 1422 const char * 1423 get_type(int type) 1424 { 1425 struct part_type *ptr; 1426 1427 ptr = bsearch(&type, part_types, 1428 sizeof(part_types) / sizeof(struct part_type), 1429 sizeof(struct part_type), type_match); 1430 if (ptr == 0) 1431 return ("unknown"); 1432 return (ptr->name); 1433 } 1434