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