1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * fsck_pcfs -- common.c 28 * All the routines in this file are being swiped directly from 29 * mkfs_pcfs. Eventually this file should only exist in one place 30 * and be part of a library that both mkfs and fsck link against. 31 */ 32 #include <stdio.h> 33 #include <string.h> 34 #include <unistd.h> 35 #include <stdlib.h> 36 #include <libintl.h> 37 #include <sys/isa_defs.h> 38 #include <sys/types.h> 39 #include <sys/stat.h> 40 #include <sys/fcntl.h> 41 #include <sys/dktp/fdisk.h> 42 #include <sys/fs/pc_fs.h> 43 #include <sys/fs/pc_dir.h> 44 #include <sys/fs/pc_label.h> 45 #include "fsck_pcfs.h" 46 #include "pcfs_common.h" 47 #include "pcfs_bpb.h" 48 49 /* 50 * The assumption here is that _BIG_ENDIAN implies sparc, and 51 * so in addition to swapping bytes we also have to construct 52 * packed structures by hand to avoid bus errors due to improperly 53 * aligned pointers. 54 */ 55 #ifdef _BIG_ENDIAN 56 void swap_pack_grab32bpb(bpb_t *wbpb, struct _boot_sector *bsp); 57 void swap_pack_grabbpb(bpb_t *wbpb, struct _boot_sector *bsp); 58 #endif /* _BIG_ENDIAN */ 59 60 /* 61 * Global variables related to input questions 62 */ 63 extern int AlwaysYes; 64 extern int AlwaysNo; 65 66 int 67 is_z_a_power_of_x_le_y(int x, int y, int z) 68 { 69 int ispower = 0; 70 int pow = 1; 71 72 do { 73 if (pow == z) { 74 ispower = 1; 75 break; 76 } 77 pow *= x; 78 } while (pow <= y); 79 80 return (ispower); 81 } 82 83 /* 84 * store_16_bits 85 * Save the lower 16 bits of a 32 bit value (v) into the provided 86 * buffer (pointed at by *bp), and increment the buffer pointer 87 * as well. This way the routine can be called multiple times in 88 * succession to fill buffers. The value is stored in little-endian 89 * order. 90 */ 91 void 92 store_16_bits(uchar_t **bp, uint32_t v) 93 { 94 uchar_t *l = *bp; 95 96 *l++ = v & 0xff; 97 *l = (v >> 8) & 0xff; 98 *bp += 2; 99 } 100 101 void 102 read_16_bits(uchar_t *bp, uint32_t *value) 103 { 104 *value = *bp++; 105 *value += *bp << 8; 106 } 107 108 /* 109 * store_32_bits 110 * Save the 32 bit value (v) into the provided buffer (pointed 111 * at by *bp), and increment the buffer pointer as well. This way 112 * the routine can be called multiple times in succession to fill 113 * buffers. The value is stored in little-endian order. 114 */ 115 void 116 store_32_bits(uchar_t **bp, uint32_t v) 117 { 118 uchar_t *l = *bp; 119 int b; 120 121 for (b = 0; b < 4; b++) { 122 *l++ = v & 0xff; 123 v = v >> 8; 124 } 125 *bp += 4; 126 } 127 128 void 129 read_32_bits(uchar_t *bp, uint32_t *value) 130 { 131 *value = *bp++; 132 *value += *bp++ << 8; 133 *value += *bp++ << 16; 134 *value += *bp++ << 24; 135 } 136 137 /* 138 * dump_bytes -- display bytes as hex numbers. 139 * b is the pointer to the byte buffer 140 * n is the number of bytes in the buffer 141 */ 142 /* Note: BPL = bytes to display per line */ 143 #define BPL 16 144 145 void 146 dump_bytes(uchar_t *buf, int n) 147 { 148 int printedCount; 149 int countdown = n; 150 int countup = 0; 151 int offset = 0; 152 int byte; 153 154 /* Display offset, 16 bytes per line, and printable ascii version */ 155 while (countdown > 0) { 156 printedCount = 0; 157 (void) fprintf(stderr, "\n%06x: ", offset); 158 /* 159 * Print Hex value of characters in columns on left 160 */ 161 for (byte = 0; byte < BPL; byte++) { 162 if (countup + byte < n) { 163 (void) fprintf(stderr, 164 "%02x ", (buf[countup + byte] & 0xff)); 165 printedCount++; 166 } else { 167 (void) fprintf(stderr, " "); 168 } 169 } 170 /* 171 * Right side has the printable character or '.' for 172 * unprintable for each column of the left. 173 */ 174 for (byte = 0; byte < BPL; byte++) { 175 if ((countup + byte < n) && 176 ((buf[countup + byte] >= ' ') && 177 (buf[countup + byte] <= '~'))) { 178 (void) fprintf(stderr, "%c", 179 buf[countup + byte]); 180 } else { 181 (void) fprintf(stderr, "."); 182 } 183 } 184 countup += printedCount; 185 offset += printedCount; 186 countdown -= printedCount; 187 } 188 (void) fprintf(stderr, "\n\n"); 189 } 190 191 /* 192 * header_for_dump -- display simple header over what will be output. 193 */ 194 void 195 header_for_dump(void) 196 { 197 int byte; 198 199 (void) fprintf(stderr, "\n "); 200 for (byte = 0; byte < BPL; byte++) 201 (void) fprintf(stderr, "%02x ", byte); 202 (void) fprintf(stderr, "\n "); 203 byte = 3*BPL; 204 while (byte-- > 0) 205 (void) fprintf(stderr, "-"); 206 } 207 208 /* 209 * We are basically (incorrectly) assuming that if you aren't running 210 * on x86 the BPB has to be packed by hand AND that the bytes must 211 * be swapped. One or both of these assumptions may one day be invalid. 212 * (if they aren't already :-)) 213 */ 214 #ifdef _BIG_ENDIAN 215 /* 216 * swap_pack_grab{32}bpb 217 * If not on an x86 we assume the structures making up the bpb 218 * were not packed and that longs and shorts need to be byte swapped 219 * (we've kept everything in host order up until now). A new architecture 220 * might not need to swap or might not need to pack, in which case 221 * new routines will have to be written. Of course if an architecture 222 * supports both packing and little-endian host order, it can follow the 223 * same path as the x86 code. 224 */ 225 void 226 swap_pack_grabbpb(bpb_t *wbpb, struct _boot_sector *bsp) 227 { 228 uchar_t *grabp; 229 230 grabp = (uchar_t *)&(bsp->bs_filler[ORIG_BPB_START_INDEX]); 231 232 ((uchar_t *)&(wbpb->bpb.bytes_per_sector))[1] = *grabp++; 233 ((uchar_t *)&(wbpb->bpb.bytes_per_sector))[0] = *grabp++; 234 wbpb->bpb.sectors_per_cluster = *grabp++; 235 ((uchar_t *)&(wbpb->bpb.resv_sectors))[1] = *grabp++; 236 ((uchar_t *)&(wbpb->bpb.resv_sectors))[0] = *grabp++; 237 wbpb->bpb.num_fats = *grabp++; 238 ((uchar_t *)&(wbpb->bpb.num_root_entries))[1] = *grabp++; 239 ((uchar_t *)&(wbpb->bpb.num_root_entries))[0] = *grabp++; 240 ((uchar_t *)&(wbpb->bpb.sectors_in_volume))[1] = *grabp++; 241 ((uchar_t *)&(wbpb->bpb.sectors_in_volume))[0] = *grabp++; 242 wbpb->bpb.media = *grabp++; 243 ((uchar_t *)&(wbpb->bpb.sectors_per_fat))[1] = *grabp++; 244 ((uchar_t *)&(wbpb->bpb.sectors_per_fat))[0] = *grabp++; 245 ((uchar_t *)&(wbpb->bpb.sectors_per_track))[1] = *grabp++; 246 ((uchar_t *)&(wbpb->bpb.sectors_per_track))[0] = *grabp++; 247 ((uchar_t *)&(wbpb->bpb.heads))[1] = *grabp++; 248 ((uchar_t *)&(wbpb->bpb.heads))[0] = *grabp++; 249 ((uchar_t *)&(wbpb->bpb.hidden_sectors))[3] = *grabp++; 250 ((uchar_t *)&(wbpb->bpb.hidden_sectors))[2] = *grabp++; 251 ((uchar_t *)&(wbpb->bpb.hidden_sectors))[1] = *grabp++; 252 ((uchar_t *)&(wbpb->bpb.hidden_sectors))[0] = *grabp++; 253 ((uchar_t *)&(wbpb->bpb.sectors_in_logical_volume))[3] = *grabp++; 254 ((uchar_t *)&(wbpb->bpb.sectors_in_logical_volume))[2] = *grabp++; 255 ((uchar_t *)&(wbpb->bpb.sectors_in_logical_volume))[1] = *grabp++; 256 ((uchar_t *)&(wbpb->bpb.sectors_in_logical_volume))[0] = *grabp++; 257 wbpb->ebpb.phys_drive_num = *grabp++; 258 wbpb->ebpb.reserved = *grabp++; 259 wbpb->ebpb.ext_signature = *grabp++; 260 ((uchar_t *)&(wbpb->ebpb.volume_id))[3] = *grabp++; 261 ((uchar_t *)&(wbpb->ebpb.volume_id))[2] = *grabp++; 262 ((uchar_t *)&(wbpb->ebpb.volume_id))[1] = *grabp++; 263 ((uchar_t *)&(wbpb->ebpb.volume_id))[0] = *grabp++; 264 265 (void) strncpy((char *)wbpb->ebpb.volume_label, (char *)grabp, 11); 266 grabp += 11; 267 (void) strncpy((char *)wbpb->ebpb.type, (char *)grabp, 8); 268 } 269 270 void 271 swap_pack_grab32bpb(bpb_t *wbpb, struct _boot_sector *bsp) 272 { 273 uchar_t *grabp; 274 275 grabp = (uchar_t *)&(bsp->bs_filler[BPB_32_START_INDEX]); 276 277 ((uchar_t *)&(wbpb->bpb32.big_sectors_per_fat))[3] = *grabp++; 278 ((uchar_t *)&(wbpb->bpb32.big_sectors_per_fat))[2] = *grabp++; 279 ((uchar_t *)&(wbpb->bpb32.big_sectors_per_fat))[1] = *grabp++; 280 ((uchar_t *)&(wbpb->bpb32.big_sectors_per_fat))[0] = *grabp++; 281 ((uchar_t *)&(wbpb->bpb32.ext_flags))[1] = *grabp++; 282 ((uchar_t *)&(wbpb->bpb32.ext_flags))[0] = *grabp++; 283 wbpb->bpb32.fs_vers_lo = *grabp++; 284 wbpb->bpb32.fs_vers_hi = *grabp++; 285 ((uchar_t *)&(wbpb->bpb32.root_dir_clust))[3] = *grabp++; 286 ((uchar_t *)&(wbpb->bpb32.root_dir_clust))[2] = *grabp++; 287 ((uchar_t *)&(wbpb->bpb32.root_dir_clust))[1] = *grabp++; 288 ((uchar_t *)&(wbpb->bpb32.root_dir_clust))[0] = *grabp++; 289 ((uchar_t *)&(wbpb->bpb32.fsinfosec))[1] = *grabp++; 290 ((uchar_t *)&(wbpb->bpb32.fsinfosec))[0] = *grabp++; 291 ((uchar_t *)&(wbpb->bpb32.backupboot))[1] = *grabp++; 292 ((uchar_t *)&(wbpb->bpb32.backupboot))[0] = *grabp++; 293 ((uchar_t *)&(wbpb->bpb32.reserved[0]))[1] = *grabp++; 294 ((uchar_t *)&(wbpb->bpb32.reserved[0]))[0] = *grabp++; 295 ((uchar_t *)&(wbpb->bpb32.reserved[1]))[1] = *grabp++; 296 ((uchar_t *)&(wbpb->bpb32.reserved[1]))[0] = *grabp++; 297 ((uchar_t *)&(wbpb->bpb32.reserved[2]))[1] = *grabp++; 298 ((uchar_t *)&(wbpb->bpb32.reserved[2]))[0] = *grabp++; 299 ((uchar_t *)&(wbpb->bpb32.reserved[3]))[1] = *grabp++; 300 ((uchar_t *)&(wbpb->bpb32.reserved[3]))[0] = *grabp++; 301 ((uchar_t *)&(wbpb->bpb32.reserved[4]))[1] = *grabp++; 302 ((uchar_t *)&(wbpb->bpb32.reserved[4]))[0] = *grabp++; 303 ((uchar_t *)&(wbpb->bpb32.reserved[5]))[1] = *grabp++; 304 ((uchar_t *)&(wbpb->bpb32.reserved[5]))[0] = *grabp++; 305 } 306 #endif /* _BIG_ENDIAN */ 307 308 int 309 yes(void) 310 { 311 char *affirmative = gettext("yY"); 312 char *a = affirmative; 313 char input[80]; 314 315 if (AlwaysYes) { 316 (void) printf("y\n"); 317 return (1); 318 } else if (AlwaysNo) { 319 (void) printf("n\n"); 320 return (0); 321 } 322 if (fgets(input, sizeof (input), stdin) == NULL) { 323 AlwaysNo = 1; 324 (void) printf("n\n"); 325 return (0); 326 } 327 while (*a) { 328 if (input[0] == (int)*a) 329 break; 330 a++; 331 } 332 return ((int)*a); 333 } 334 335 char * 336 stat_actual_disk(char *diskname, struct stat *info, char **suffix) 337 { 338 char *actualdisk; 339 340 if (stat(diskname, info)) { 341 /* 342 * Device named on command line doesn't exist. That 343 * probably means there is a partition-specifying 344 * suffix attached to the actual disk name. 345 */ 346 if ((actualdisk = strdup(diskname)) == NULL) { 347 (void) fprintf(stderr, 348 gettext("Out of memory for disk name.\n")); 349 exit(2); 350 } 351 if ((*suffix = strchr(actualdisk, ':')) != NULL) { 352 **suffix = '\0'; 353 (*suffix)++; 354 } 355 356 if (stat(actualdisk, info)) { 357 perror(actualdisk); 358 exit(2); 359 } 360 } else { 361 if ((actualdisk = strdup(diskname)) == NULL) { 362 (void) fprintf(stderr, 363 gettext("Out of memory for disk name.\n")); 364 exit(2); 365 } 366 } 367 368 return (actualdisk); 369 } 370 371 extern void usage(void); 372 373 void 374 bad_arg(char *option) 375 { 376 (void) fprintf(stderr, 377 gettext("Unrecognized option -o %s.\n"), option); 378 usage(); 379 exit(2); 380 } 381 382 void 383 missing_arg(char *option) 384 { 385 (void) fprintf(stderr, 386 gettext("Option %s requires a value.\n"), option); 387 usage(); 388 exit(3); 389 } 390 391 static int 392 parse_drvnum(char *pn) 393 { 394 int drvnum; 395 396 /* 397 * Determine logical drive to seek after. 398 */ 399 if ((strlen(pn) == 1) && ((*pn >= 'c') && (*pn <= 'z'))) { 400 drvnum = *pn - 'c' + 1; 401 } else if ((*pn >= '0') && (*pn <= '9')) { 402 char *d; 403 int v = 0; 404 405 d = pn; 406 while ((*d != '\0') && (*d >= '0') && (*d <= '9')) { 407 v *= 10; 408 v += *d - '0'; 409 d++; 410 } 411 if ((*d != '\0') || (v > 24)) { 412 (void) fprintf(stderr, 413 gettext("%s: bogus logical drive specification.\n"), 414 pn); 415 return (-1); 416 } 417 drvnum = v; 418 } else if (strcmp(pn, "boot") == 0) { 419 drvnum = 99; 420 } else { 421 (void) fprintf(stderr, 422 gettext("%s: bogus logical drive specification.\n"), pn); 423 return (-1); 424 } 425 426 return (drvnum); 427 } 428 429 /* 430 * isDosDrive() 431 * Boolean function. Give it the systid field for an fdisk partition 432 * and it decides if that's a systid that describes a DOS drive. We 433 * use systid values defined in sys/dktp/fdisk.h. 434 */ 435 static int 436 isDosDrive(uchar_t checkMe) 437 { 438 return ((checkMe == DOSOS12) || (checkMe == DOSOS16) || 439 (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) || 440 (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) || 441 (checkMe == DIAGPART)); 442 } 443 444 /* 445 * isDosExtended() 446 * Boolean function. Give it the systid field for an fdisk partition 447 * and it decides if that's a systid that describes an extended DOS 448 * partition. 449 */ 450 static int 451 isDosExtended(uchar_t checkMe) 452 { 453 return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA)); 454 } 455 456 /* 457 * isBootPart() 458 * Boolean function. Give it the systid field for an fdisk partition 459 * and it decides if that's a systid that describes a Solaris boot 460 * partition. 461 */ 462 static int 463 isBootPart(uchar_t checkMe) 464 { 465 return (checkMe == X86BOOT); 466 } 467 468 off64_t 469 findPartitionOffset(int fd, char *ldrive) 470 { 471 struct ipart part[FD_NUMPART]; 472 struct mboot extmboot; 473 struct mboot mb; 474 diskaddr_t xstartsect; 475 off64_t nextseek = 0; 476 off64_t lastseek = 0; 477 off64_t found = 0; 478 off64_t error = -1; 479 int logicalDriveCount = 0; 480 int extendedPart = -1; 481 int primaryPart = -1; 482 int bootPart = -1; 483 uint32_t xnumsect = 0; 484 int drvnum; 485 int driveIndex; 486 int i; 487 /* 488 * Count of drives in the current extended partition's 489 * FDISK table, and indexes of the drives themselves. 490 */ 491 int extndDrives[FD_NUMPART]; 492 int numDrives = 0; 493 /* 494 * Count of drives (beyond primary) in master boot record's 495 * FDISK table, and indexes of the drives themselves. 496 */ 497 int extraDrives[FD_NUMPART]; 498 int numExtraDrives = 0; 499 500 if ((drvnum = parse_drvnum(ldrive)) < 0) 501 return (error); 502 503 if (read(fd, &mb, sizeof (mb)) != sizeof (mb)) { 504 (void) fprintf(stderr, 505 gettext("Couldn't read a Master Boot Record\n")); 506 return (error); 507 } 508 509 if (ltohs(mb.signature) != BOOTSECSIG) { 510 (void) fprintf(stderr, 511 gettext("Bad signature on master boot record (%x)\n"), 512 ltohs(mb.signature)); 513 return (error); 514 } 515 516 /* 517 * Copy partition table into memory 518 */ 519 (void) memcpy(part, mb.parts, sizeof (part)); 520 521 /* 522 * Get a summary of what is in the Master FDISK table. 523 * Normally we expect to find one partition marked as a DOS drive. 524 * This partition is the one Windows calls the primary dos partition. 525 * If the machine has any logical drives then we also expect 526 * to find a partition marked as an extended DOS partition. 527 * 528 * Sometimes we'll find multiple partitions marked as DOS drives. 529 * The Solaris fdisk program allows these partitions 530 * to be created, but Windows fdisk no longer does. We still need 531 * to support these, though, since Windows does. We also need to fix 532 * our fdisk to behave like the Windows version. 533 * 534 * It turns out that some off-the-shelf media have *only* an 535 * Extended partition, so we need to deal with that case as 536 * well. 537 * 538 * Only a single (the first) Extended or Boot Partition will 539 * be recognized. Any others will be ignored. 540 */ 541 for (i = 0; i < FD_NUMPART; i++) { 542 if (isDosDrive(part[i].systid)) { 543 if (primaryPart < 0) { 544 logicalDriveCount++; 545 primaryPart = i; 546 } else { 547 extraDrives[numExtraDrives++] = i; 548 } 549 continue; 550 } 551 if ((extendedPart < 0) && isDosExtended(part[i].systid)) { 552 extendedPart = i; 553 continue; 554 } 555 if ((bootPart < 0) && isBootPart(part[i].systid)) { 556 bootPart = i; 557 continue; 558 } 559 } 560 561 if (drvnum == BOOT_PARTITION_DRIVE) { 562 if (bootPart < 0) { 563 (void) fprintf(stderr, 564 gettext("No boot partition found on drive\n")); 565 return (error); 566 } 567 found = ltohi(part[bootPart].relsect) * BPSEC; 568 return (found); 569 } 570 571 if (drvnum == PRIMARY_DOS_DRIVE && primaryPart >= 0) { 572 found = ltohi(part[primaryPart].relsect) * BPSEC; 573 return (found); 574 } 575 576 /* 577 * We are not looking for the C: drive (or there was no primary 578 * drive found), so we had better have an extended partition or 579 * extra drives in the Master FDISK table. 580 */ 581 if ((extendedPart < 0) && (numExtraDrives == 0)) { 582 (void) fprintf(stderr, 583 gettext("No such logical drive " 584 "(missing extended partition entry)\n")); 585 return (error); 586 } 587 588 if (extendedPart >= 0) { 589 nextseek = xstartsect = ltohi(part[extendedPart].relsect); 590 xnumsect = ltohi(part[extendedPart].numsect); 591 do { 592 /* 593 * If the seek would not cause us to change 594 * position on the drive, then we're out of 595 * extended partitions to examine. 596 */ 597 if (nextseek == lastseek) 598 break; 599 logicalDriveCount += numDrives; 600 /* 601 * Seek the next extended partition, and find 602 * logical drives within it. 603 */ 604 if (lseek64(fd, nextseek * BPSEC, SEEK_SET) < 0 || 605 read(fd, &extmboot, sizeof (extmboot)) != 606 sizeof (extmboot)) { 607 perror(gettext("Unable to read extended " 608 "partition record")); 609 return (error); 610 } 611 (void) memcpy(part, extmboot.parts, sizeof (part)); 612 lastseek = nextseek; 613 if (ltohs(extmboot.signature) != MBB_MAGIC) { 614 (void) fprintf(stderr, 615 gettext("Bad signature on " 616 "extended partition\n")); 617 return (error); 618 } 619 /* 620 * Count up drives, and track where the next 621 * extended partition is in case we need it. We 622 * are expecting only one extended partition. If 623 * there is more than one we'll only go to the 624 * first one we see, but warn about ignoring. 625 */ 626 numDrives = 0; 627 for (i = 0; i < FD_NUMPART; i++) { 628 if (isDosDrive(part[i].systid)) { 629 extndDrives[numDrives++] = i; 630 continue; 631 } else if (isDosExtended(part[i].systid)) { 632 if (nextseek != lastseek) { 633 /* 634 * Already found an extended 635 * partition in this table. 636 */ 637 (void) fprintf(stderr, 638 gettext("WARNING: " 639 "Ignoring unexpected " 640 "additional extended " 641 "partition")); 642 continue; 643 } 644 nextseek = xstartsect + 645 ltohi(part[i].relsect); 646 continue; 647 } 648 } 649 } while (drvnum > logicalDriveCount + numDrives); 650 651 if (drvnum <= logicalDriveCount + numDrives) { 652 /* 653 * The number of logical drives we've found thus 654 * far is enough to get us to the one we were 655 * searching for. 656 */ 657 driveIndex = logicalDriveCount + numDrives - drvnum; 658 found = 659 ltohi(part[extndDrives[driveIndex]].relsect) + 660 lastseek; 661 if (found > (xstartsect + xnumsect)) { 662 (void) fprintf(stderr, 663 gettext("Logical drive start sector (%d) " 664 "is not within the partition!\n"), found); 665 return (error); 666 } else { 667 found *= BPSEC; 668 } 669 return (found); 670 } else { 671 /* 672 * We ran out of extended dos partition 673 * drives. The only hope now is to go 674 * back to extra drives defined in the master 675 * fdisk table. But we overwrote that table 676 * already, so we must load it in again. 677 */ 678 logicalDriveCount += numDrives; 679 (void) memcpy(part, mb.parts, sizeof (part)); 680 } 681 } 682 /* 683 * Still haven't found the drive, is it an extra 684 * drive defined in the main FDISK table? 685 */ 686 if (drvnum <= logicalDriveCount + numExtraDrives) { 687 driveIndex = logicalDriveCount + numExtraDrives - drvnum; 688 found = ltohi(part[extraDrives[driveIndex]].relsect) * BPSEC; 689 return (found); 690 } 691 return (error); 692 } 693