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