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