pcfs_common.c revision 7563:84ec90ffc3f7
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
56void swap_pack_grab32bpb(bpb_t *wbpb, struct _boot_sector *bsp);
57void swap_pack_grabbpb(bpb_t *wbpb, struct _boot_sector *bsp);
58#endif /* _BIG_ENDIAN */
59
60/*
61 *  Global variables related to input questions
62 */
63extern int AlwaysYes;
64extern int AlwaysNo;
65
66int
67is_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 */
91void
92store_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
101void
102read_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 */
115void
116store_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
128void
129read_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
145void
146dump_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 */
194void
195header_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 */
225void
226swap_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
270void
271swap_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
308int
309yes(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
335char *
336stat_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
371extern void usage(void);
372
373void
374bad_arg(char *option)
375{
376	(void) fprintf(stderr,
377	    gettext("Unrecognized option -o %s.\n"), option);
378	usage();
379	exit(2);
380}
381
382void
383missing_arg(char *option)
384{
385	(void) fprintf(stderr,
386	    gettext("Option %s requires a value.\n"), option);
387	usage();
388	exit(3);
389}
390
391static int
392parse_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 */
435static int
436isDosDrive(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 */
450static int
451isDosExtended(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 */
462static int
463isBootPart(uchar_t checkMe)
464{
465	return (checkMe == X86BOOT);
466}
467
468off64_t
469findPartitionOffset(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