xref: /titanic_50/usr/src/cmd/fs.d/pcfs/mkfs/mkfs.c (revision 142c9f13e148d687426ed2d4e8bd93717eeaebbc)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <ctype.h>
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <libintl.h>
37 #include <locale.h>
38 #include <sys/fdio.h>
39 #include <sys/dktp/fdisk.h>
40 #include <sys/dkio.h>
41 #include <sys/sysmacros.h>
42 #include "mkfs_pcfs.h"
43 #include <sys/fs/pc_fs.h>
44 #include <sys/fs/pc_dir.h>
45 #include <sys/fs/pc_label.h>
46 #include <macros.h>
47 
48 /*
49  *	mkfs (for pcfs)
50  *
51  *	Install a boot block, FAT, and (if desired) the first resident
52  *	of the new fs.
53  *
54  *	XXX -- floppy opens need O_NDELAY?
55  */
56 #define	DEFAULT_LABEL "NONAME"
57 
58 static char	*BootBlkFn = NULL;
59 static char	*DiskName = NULL;
60 static char	*FirstFn = NULL;
61 static char	*Label = NULL;
62 static char	Firstfileattr = 0x20;
63 static int	Outputtofile = 0;
64 static int	SunBPBfields = 0;
65 static int	GetFsParams = 0;
66 static int	Fatentsize = 0;
67 static int	Imagesize = 3;
68 static int	Notreally = 0;
69 static int	Verbose = 0;
70 static int	MakeFAT32 = 0;
71 
72 /*
73  * If there is an FDISK entry for the device where we're about to
74  * make the file system, we ought to make a file system that has the
75  * same size FAT as the FDISK table claims.  We track the size FDISK
76  * thinks in this variable.
77  */
78 static int	FdiskFATsize = 0;
79 
80 static int	GetSize = 1;	/* Unless we're given as arg, must look it up */
81 static ulong_t	TotSize;	/* Total size of FS in # of sectors */
82 static int	GetSPC = 1;	/* Unless we're given as arg, must calculate */
83 static ulong_t	SecPerClust;	/* # of sectors per cluster */
84 static int	GetOffset = 1;	/* Unless we're given as arg, must look it up */
85 static ulong_t	RelOffset;	/* Relative start sector (hidden sectors) */
86 static int	GetSPT = 1;	/* Unless we're given as arg, must look it up */
87 static ushort_t	SecPerTrk;	/* # of sectors per track */
88 static int	GetTPC = 1;	/* Unless we're given as arg, must look it up */
89 static ushort_t	TrkPerCyl;	/* # of tracks per cylinder */
90 static int	GetResrvd = 1;	/* Unless we're given as arg, must calculate */
91 static int	Resrvd;		/* Number of reserved sectors */
92 static int	GetBPF = 1;	/* Unless we're given as arg, must calculate */
93 static int	BitsPerFAT;	/* Total size of FS in # of sectors */
94 
95 static ulong_t	TotalClusters;	/* Computed total number of clusters */
96 
97 /*
98  * Unless we are told otherwise, we should use fdisk table for non-diskettes.
99  */
100 static int	DontUseFdisk = 0;
101 
102 /*
103  * Function prototypes
104  */
105 #ifndef i386
106 static void swap_pack_grabsebpb(bpb_t *wbpb, struct _boot_sector *bsp);
107 static void swap_pack_bpb32cpy(struct _boot_sector32 *bsp, bpb_t *wbpb);
108 static void swap_pack_sebpbcpy(struct _boot_sector *bsp, bpb_t *wbpb);
109 static void swap_pack_grabbpb(bpb_t *wbpb, struct _boot_sector *bsp);
110 static void swap_pack_bpbcpy(struct _boot_sector *bsp, bpb_t *wbpb);
111 #endif
112 
113 static uchar_t *build_rootdir(bpb_t *wbpb, char *ffn, int fffd,
114 	ulong_t ffsize, pc_cluster32_t ffstart, ulong_t *rdirsize);
115 static uchar_t *build_fat(bpb_t *wbpb, struct fat_od_fsi *fsinfop,
116 	ulong_t bootblksize, ulong_t *fatsize, char *ffn, int *fffd,
117 	ulong_t *ffsize, pc_cluster32_t *ffstartclust);
118 
119 static char *stat_actual_disk(char *diskname, struct stat *info, char **suffix);
120 
121 static void compare_existing_with_computed(int fd, char *suffix,
122 	bpb_t *wbpb, int *prtsize, int *prtspc, int *prtbpf, int *prtnsect,
123 	int *prtntrk, int *prtfdisk, int *prthidden, int *prtrsrvd,
124 	int *dashos);
125 static void print_reproducing_command(int fd, char *actualdisk, char *suffix,
126 	bpb_t *wbpb);
127 static void compute_file_area_size(bpb_t *wbpb);
128 static void write_fat32_bootstuff(int fd, boot_sector_t *bsp,
129 	struct fat_od_fsi *fsinfop, off64_t seekto);
130 static void sanity_check_options(int argc, int optind);
131 static void compute_cluster_size(bpb_t *wbpb);
132 static void find_fixed_details(int fd, bpb_t *wbpb);
133 static void dirent_fname_fill(struct pcdir *dep, char *fn);
134 static void floppy_bpb_fillin(bpb_t *wbpb,
135 	int diam, int hds, int spt);
136 static void read_existing_bpb(int fd, bpb_t *wbpb);
137 static void warn_funky_fatsize(void);
138 static void warn_funky_floppy(void);
139 static void dirent_time_fill(struct pcdir *dep);
140 static void parse_suboptions(char *optsstr);
141 static void header_for_dump(void);
142 static void write_bootsects(int fd, boot_sector_t *bsp, bpb_t *wbpb,
143 	struct fat_od_fsi *fsinfop, off64_t seekto);
144 static void fill_bpb_sizes(bpb_t *wbpb, struct ipart part[],
145 	int partno, off64_t offset);
146 static void set_fat_string(bpb_t *wbpb, int fatsize);
147 static void partn_lecture(char *dn);
148 static void store_16_bits(uchar_t **bp, uint32_t v);
149 static void store_32_bits(uchar_t **bp, uint32_t v);
150 static void lookup_floppy(struct fd_char *fdchar, bpb_t *wbpb);
151 static void label_volume(char *lbl, bpb_t *wbpb);
152 static void mark_cluster(uchar_t *fatp, pc_cluster32_t clustnum,
153 	uint32_t value);
154 static void missing_arg(char *option);
155 static void dashm_bail(int fd);
156 static void dump_bytes(uchar_t *, int);
157 static void write_rest(bpb_t *wbpb, char *efn,
158 	int dfd, int sfd, int remaining);
159 static void write_fat(int fd, off64_t seekto, char *fn, char *lbl,
160 	char *ffn, bpb_t *wbpb);
161 static void bad_arg(char *option);
162 static void usage(void);
163 
164 static int prepare_image_file(char *fn, bpb_t *wbpb);
165 static int verify_bootblkfile(char *fn, boot_sector_t *bs,
166 	ulong_t *blkfilesize);
167 static int open_and_examine(char *dn, bpb_t *wbpb);
168 static int verify_firstfile(char *fn, ulong_t *filesize);
169 static int lookup_FAT_size(uchar_t partid);
170 static int powerofx_le_y(int x, int y, int value);
171 static int open_and_seek(char *dn, bpb_t *wbpb, off64_t *seekto);
172 static int warn_mismatch(char *desc, char *src, int expect, int assigned);
173 static int copy_bootblk(char *fn, boot_sector_t *bootsect,
174 	ulong_t *bootblksize);
175 static int parse_drvnum(char *pn);
176 static int seek_nofdisk(int fd, bpb_t *wbpb, off64_t *seekto);
177 static int ask_nicely(char *special);
178 static int seek_partn(int fd, char *pn, bpb_t *wbpb, off64_t *seekto);
179 static int yes(void);
180 
181 /*
182  *  usage
183  *
184  *	Display usage message and exit.
185  */
186 static
187 void
188 usage(void)
189 {
190 	(void) fprintf(stderr,
191 	    gettext("pcfs usage: mkfs [-F FSType] [-V] [-m] "
192 	    "[-o specific_options] special\n"));
193 
194 	(void) fprintf(stderr,
195 	    gettext(" -V: print this command line and return\n"
196 	    " -m: dump command line used to create a FAT on this media\n"
197 	    "\t(other options are ignored if this option is chosen).\n"
198 	    " -o: pcfs_specific_options:\n"
199 	    "\t'pcfs_specific_options' is a comma separated list\n"
200 	    "\tincluding one or more of the following options:\n"
201 	    "\t    N,v,r,h,s,b=label,B=filename,i=filename,\n"
202 	    "\t    spc=n,fat=n,nsect=n,ntrack=n,nofdisk,size=n,\n"
203 	    "\t    reserve=n,hidden=n\n\n"));
204 
205 	(void) fprintf(stderr,
206 	    gettext("'Special' should specify a raw diskette "
207 	    "or raw fixed disk device.  \"Fixed\"\n"
208 	    "disks (which include high-capacity removable "
209 	    "media such as Zip disks)\n"
210 	    "may be further qualified with a logical "
211 	    "drive specifier.\n"
212 	    "Examples are: /dev/rdiskette and "
213 	    "/dev/rdsk/c0t0d0p0:c\n"));
214 	exit(1);
215 }
216 
217 static
218 int
219 yes(void)
220 {
221 	char *affirmative = gettext("yY");
222 	char *a = affirmative;
223 	int b;
224 
225 	b = getchar();
226 	while (b == '\n' && b != '\0' && b != EOF)
227 		b = getchar();
228 	while (*a) {
229 		if (b == (int)*a)
230 			break;
231 		a++;
232 	}
233 	return (*a);
234 }
235 
236 /*
237  * powerofx_le_y
238  *	args of x,y, and value to be checked
239  *	returns 1 if x**n == value and n >= 0 and value <= y
240  *	returns 0 otherwise
241  */
242 static
243 int
244 powerofx_le_y(int x, int y, int value)
245 {
246 	int ispower = 0;
247 	int pow = 1;
248 
249 	if (value < 1 || value > y)
250 		return (ispower);
251 
252 	do {
253 		if (pow == value) {
254 			ispower = 1;
255 			break;
256 		}
257 		pow *= x;
258 	} while (pow <= y);
259 
260 	return (ispower);
261 }
262 
263 static
264 int
265 ask_nicely(char *special)
266 {
267 	/*
268 	 * 4228473 - No way to non-interactively make a pcfs filesystem
269 	 *
270 	 *	If we don't have an input TTY, or we aren't really doing
271 	 *	anything, then don't ask questions.  Assume a yes answer
272 	 *	to any questions we would ask.
273 	 */
274 	if (Notreally || !isatty(fileno(stdin)))
275 		return (1);
276 
277 	(void) printf(
278 	    gettext("Construct a new FAT file system on %s: (y/n)? "), special);
279 	(void) fflush(stdout);
280 	return (yes());
281 }
282 
283 /*
284  * store_16_bits
285  *	Save the lower 16 bits of a 32 bit value (v) into the provided
286  *	buffer (pointed at by *bp), and increment the buffer pointer
287  *	as well.  This way the routine can be called multiple times in
288  *	succession to fill buffers.  The value is stored in little-endian
289  *	order.
290  */
291 static
292 void
293 store_16_bits(uchar_t **bp, uint32_t v)
294 {
295 	uchar_t *l = *bp;
296 
297 	*l++ = v & 0xff;
298 	*l = (v >> 8) & 0xff;
299 	*bp += 2;
300 }
301 
302 /*
303  * store_32_bits
304  * 	Save the 32 bit value (v) into the provided buffer (pointed
305  *	at by *bp), and increment the buffer pointer as well.  This way
306  *	the routine can be called multiple times in succession to fill
307  *	buffers.  The value is stored in little-endian order.
308  */
309 static
310 void
311 store_32_bits(uchar_t **bp, uint32_t v)
312 {
313 	uchar_t *l = *bp;
314 	int b;
315 
316 	for (b = 0; b < 4; b++) {
317 		*l++ = v & 0xff;
318 		v = v >> 8;
319 	}
320 	*bp += 4;
321 }
322 
323 /*
324  *  dump_bytes  -- display bytes as hex numbers.
325  *		   b is the pointer to the byte buffer
326  *		   n is the number of bytes in the buffer
327  */
328 /* Note: BPL = bytes to display per line */
329 #define	BPL 16
330 
331 static
332 void
333 dump_bytes(uchar_t *b, int n)
334 {
335 	int cd = n;
336 	int cu = 0;
337 	int o = 0;
338 	int bl;
339 	int ac;
340 
341 	/* Display offset, 16 bytes per line, and printable ascii version */
342 	while (cd > 0) {
343 		ac = 0;
344 		(void) printf("\n%06x: ", o);
345 		for (bl = 0; bl < BPL; bl++) {
346 			if (cu+bl < n) {
347 				(void) printf("%02x ", (b[cu+bl] & 0xff));
348 				ac++;
349 			}
350 			else
351 				(void) printf("   ");
352 		}
353 		for (bl = 0; bl < BPL; bl++) {
354 			if ((cu+bl < n) &&
355 			    ((b[cu+bl] >= ' ') && (b[cu+bl] <= '~')))
356 				(void) printf("%c", b[cu+bl]);
357 			else
358 				(void) printf(".");
359 		}
360 		cu += ac; o += ac; cd -= ac;
361 	}
362 	(void) printf("\n\n");
363 }
364 
365 /*
366  *  header_for_dump  --  display simple header over what will be output.
367  */
368 static
369 void
370 header_for_dump(void)
371 {
372 	int bl;
373 
374 	(void) printf("\n        ");
375 	for (bl = 0; bl < BPL; bl++)
376 		(void) printf("%02x ", bl);
377 	(void) printf("\n       ");
378 	bl = 3*BPL;
379 	while (bl-- > 0)
380 		(void) printf("-");
381 }
382 
383 /*
384  *  parse_drvnum
385  *	Convert a partition name into a drive number.
386  */
387 static
388 int
389 parse_drvnum(char *pn)
390 {
391 	int drvnum;
392 
393 	/*
394 	 * Determine logical drive to seek after.
395 	 */
396 	if (strlen(pn) == 1 && *pn >= 'c' && *pn <= 'z') {
397 		drvnum = *pn - 'c' + 1;
398 	} else if (*pn >= '0' && *pn <= '9') {
399 		char *d;
400 		int v, m, c;
401 
402 		v = 0;
403 		d = pn;
404 		while (*d && *d >= '0' && *d <= '9') {
405 			c = strlen(d);
406 			m = 1;
407 			while (--c)
408 				m *= 10;
409 			v += m * (*d - '0');
410 			d++;
411 		}
412 
413 		if (*d || v > 24) {
414 			(void) fprintf(stderr,
415 			    gettext("%s: bogus logical drive specification.\n"),
416 			    pn);
417 			return (-1);
418 		}
419 		drvnum = v;
420 	} else if (strcmp(pn, "boot") == 0) {
421 		drvnum = 99;
422 	} else {
423 		(void) fprintf(stderr,
424 		    gettext("%s: bogus logical drive specification.\n"), pn);
425 		return (-1);
426 	}
427 
428 	return (drvnum);
429 }
430 
431 /*
432  *  Define some special logical drives we use.
433  */
434 #define	BOOT_PARTITION_DRIVE	99
435 #define	PRIMARY_DOS_DRIVE	1
436 
437 /*
438  * isDosDrive()
439  *	Boolean function.  Give it the systid field for an fdisk partition
440  *	and it decides if that's a systid that describes a DOS drive.  We
441  *	use systid values defined in sys/dktp/fdisk.h.
442  */
443 static int
444 isDosDrive(uchar_t checkMe)
445 {
446 	return ((checkMe == DOSOS12) || (checkMe == DOSOS16) ||
447 	    (checkMe == DOSHUGE) || (checkMe == FDISK_WINDOWS) ||
448 	    (checkMe == FDISK_EXT_WIN) || (checkMe == FDISK_FAT95) ||
449 	    (checkMe == DIAGPART));
450 }
451 
452 /*
453  * isDosExtended()
454  *	Boolean function.  Give it the systid field for an fdisk partition
455  *	and it decides if that's a systid that describes an extended DOS
456  *	partition.
457  */
458 static int
459 isDosExtended(uchar_t checkMe)
460 {
461 	return ((checkMe == EXTDOS) || (checkMe == FDISK_EXTLBA));
462 }
463 
464 /*
465  * isBootPart()
466  *	Boolean function.  Give it the systid field for an fdisk partition
467  *	and it decides if that's a systid that describes a Solaris boot
468  *	partition.
469  */
470 static int
471 isBootPart(uchar_t checkMe)
472 {
473 	return (checkMe == X86BOOT);
474 }
475 
476 static
477 int
478 warn_mismatch(char *desc, char *src, int expect, int assigned)
479 {
480 	if (expect == assigned)
481 		return (assigned);
482 
483 	/*
484 	 * 4228473 - No way to non-interactively make a pcfs filesystem
485 	 *
486 	 *	If we don't have an input TTY, or we aren't really doing
487 	 *	anything, then don't ask questions.  Assume a yes answer
488 	 *	to any questions we would ask.
489 	 */
490 	if (Notreally || !isatty(fileno(stdin))) {
491 		(void) printf(gettext("WARNING: User supplied %s is %d,"
492 		    "\nbut value obtained from the %s is %d.\n"
493 		    "Using user supplied value.\n"),
494 		    desc, assigned, src, expect);
495 		return (assigned);
496 	}
497 
498 	(void) printf(gettext("User supplied %s is %d."
499 	    "\nThe value obtained from the %s is %d.\n"),
500 	    desc, assigned, src, expect);
501 
502 	(void) printf(
503 	    gettext("Continue with value given on command line (y/n)? "));
504 	(void) fflush(stdout);
505 	if (yes())
506 		return (assigned);
507 	else
508 		exit(2);
509 	/*NOTREACHED*/
510 }
511 
512 static
513 void
514 fill_fat32_bpb(bpb_t *wbpb)
515 {
516 	/*
517 	 * ExtFlags means (according to MSDN BPB (FAT32) document)
518 	 *
519 	 * Bit 8 indicates info written to the active FAT is written
520 	 * to all copies of the FAT.  (I think they mean bit 7, with
521 	 * numbering starting at 0)
522 	 *
523 	 * Lowest 4 bits of field are the 0 based FAT number of the
524 	 * Active FAT.  (only meaningful if bit 8 is set)
525 	 *
526 	 * Field contains combination of these values:
527 	 *
528 	 *	VALUE				DESCRIPTION
529 	 * BGBPB_F_ActiveFATMsk		Mask for low four bits
530 	 * (0x000F)
531 	 * BGBPB_F_NoFATMirror		If set FAT mirroring disabled.
532 	 * (0x0080)			If clear, FAT mirroring enabled.
533 	 *
534 	 * We set the value based on what I've seen on all the FAT32 drives
535 	 * I've seen created by Windows.
536 	 *
537 	 */
538 	wbpb->bpb32.ext_flags = 0x0;
539 	/*
540 	 * No real explanation of the fs_vers file in the BPB doc.  The
541 	 * high byte is supposed to be the major version and the low the
542 	 * minor version.  Again I set according to what I've seen on Windows.
543 	 */
544 	wbpb->bpb32.fs_vers_lo = '\0';
545 	wbpb->bpb32.fs_vers_hi = '\0';
546 	/*
547 	 * The convention appears to be to place the fs info sector
548 	 * immediately after the boot sector, and that the backup boot
549 	 * sector should be at sector 6. (based on what I see with
550 	 * Windows)
551 	 */
552 	wbpb->bpb32.fsinfosec = 1;
553 	wbpb->bpb32.backupboot = 6;
554 }
555 
556 static
557 void
558 fill_bpb_sizes(bpb_t *wbpb, struct ipart part[], int partno, off64_t offset)
559 {
560 	ulong_t usesize;
561 
562 	if (GetFsParams || GetSize) {
563 		usesize = ltohi(part[partno].numsect);
564 		if (Verbose) {
565 			(void) printf(
566 			    gettext("Partition size (from FDISK table) "
567 			    "= %d sectors.\n"), usesize);
568 		}
569 	} else {
570 		usesize = warn_mismatch(
571 		    gettext("length of partition (in sectors)"),
572 		    gettext("FDISK table"),
573 		    ltohi(part[partno].numsect), TotSize);
574 	}
575 
576 	if (GetFsParams) {
577 		TotSize = usesize;
578 	} else {
579 		if (usesize > 0xffff)
580 			wbpb->bpb.sectors_in_volume = 0;
581 		else
582 			wbpb->bpb.sectors_in_volume = usesize;
583 		wbpb->bpb.sectors_in_logical_volume = usesize;
584 	}
585 
586 	wbpb->bpb.hidden_sectors = offset;
587 
588 	if (GetFsParams) {
589 		RelOffset = offset;
590 	} else {
591 		wbpb->sunbpb.bs_offset_high = offset >> 16;
592 		wbpb->sunbpb.bs_offset_low = offset & 0xFFFF;
593 	}
594 }
595 
596 /*
597  *  lookup_FAT_size
598  *
599  *	Given the FDISK partition file system identifier, return the
600  *	expected FAT size for the partition.
601  */
602 static
603 int
604 lookup_FAT_size(uchar_t partid)
605 {
606 	int rval;
607 
608 	switch (partid) {
609 	case DOSOS12:
610 		rval = 12;
611 		break;
612 	case DOSOS16:
613 	case DOSHUGE:
614 	case FDISK_FAT95:
615 	case X86BOOT:
616 		rval = 16;
617 		break;
618 	case FDISK_WINDOWS:
619 	case FDISK_EXT_WIN:
620 		rval = 32;
621 		break;
622 	case EXTDOS:
623 	case FDISK_EXTLBA:
624 	default:
625 		rval = -1;
626 		break;
627 	}
628 
629 	return (rval);
630 }
631 
632 /*
633  *  seek_partn
634  *
635  *	Seek to the beginning of the partition where we need to install
636  *	the new FAT.  Zero return for any error, but print error
637  *	messages here.
638  */
639 static
640 int
641 seek_partn(int fd, char *pn, bpb_t *wbpb, off64_t *seekto)
642 {
643 	struct ipart part[FD_NUMPART];
644 	struct mboot extmboot;
645 	struct mboot mb;
646 	daddr_t xstartsect;
647 	off64_t nextseek = 0;
648 	off64_t lastseek = 0;
649 	int logicalDriveCount = 0;
650 	int extendedPart = -1;
651 	int primaryPart = -1;
652 	int bootPart = -1;
653 	int xnumsect = -1;
654 	int drvnum;
655 	int driveIndex;
656 	int i;
657 	/*
658 	 * Count of drives in the current extended partition's
659 	 * FDISK table, and indexes of the drives themselves.
660 	 */
661 	int extndDrives[FD_NUMPART];
662 	int numDrives = 0;
663 	/*
664 	 * Count of drives (beyond primary) in master boot record's
665 	 * FDISK table, and indexes of the drives themselves.
666 	 */
667 	int extraDrives[FD_NUMPART];
668 	int numExtraDrives = 0;
669 
670 	if ((drvnum = parse_drvnum(pn)) < 0)
671 		return (PART_NOT_FOUND);
672 
673 	if (read(fd, &mb, sizeof (mb)) != sizeof (mb)) {
674 		(void) fprintf(stderr,
675 		    gettext("Couldn't read a Master Boot Record?!\n"));
676 		return (PART_NOT_FOUND);
677 	}
678 
679 	if (ltohs(mb.signature) != BOOTSECSIG) {
680 		(void) fprintf(stderr,
681 		    gettext("Bad Sig on master boot record!\n"));
682 		return (PART_NOT_FOUND);
683 	}
684 
685 	*seekto = 0;
686 
687 	/*
688 	 * Copy partition table into memory
689 	 */
690 	(void) memcpy(part, mb.parts, sizeof (part));
691 
692 	/*
693 	 * Get a summary of what is in the Master FDISK table.
694 	 * Normally we expect to find one partition marked as a DOS drive.
695 	 * This partition is the one Windows calls the primary dos partition.
696 	 * If the machine has any logical drives then we also expect
697 	 * to find a partition marked as an extended DOS partition.
698 	 *
699 	 * Sometimes we'll find multiple partitions marked as DOS drives.
700 	 * The Solaris fdisk program allows these partitions
701 	 * to be created, but Windows fdisk no longer does.  We still need
702 	 * to support these, though, since Windows does.  We also need to fix
703 	 * our fdisk to behave like the Windows version.
704 	 *
705 	 * It turns out that some off-the-shelf media have *only* an
706 	 * Extended partition, so we need to deal with that case as
707 	 * well.
708 	 *
709 	 * Only a single (the first) Extended or Boot Partition will
710 	 * be recognized.  Any others will be ignored.
711 	 */
712 	for (i = 0; i < FD_NUMPART; i++) {
713 		if (isDosDrive(part[i].systid)) {
714 			if (primaryPart < 0) {
715 				logicalDriveCount++;
716 				primaryPart = i;
717 			} else {
718 				extraDrives[numExtraDrives++] = i;
719 			}
720 			continue;
721 		}
722 		if ((extendedPart < 0) && isDosExtended(part[i].systid)) {
723 			extendedPart = i;
724 			continue;
725 		}
726 		if ((bootPart < 0) && isBootPart(part[i].systid)) {
727 			bootPart = i;
728 			continue;
729 		}
730 	}
731 
732 	if (drvnum == BOOT_PARTITION_DRIVE) {
733 		if (bootPart < 0) {
734 			(void) fprintf(stderr,
735 			    gettext("No boot partition found on drive\n"));
736 			return (PART_NOT_FOUND);
737 		}
738 		if ((*seekto = ltohi(part[bootPart].relsect)) == 0) {
739 			(void) fprintf(stderr, gettext("Bogus FDISK entry? "
740 			    "A boot partition starting\nat sector 0 would "
741 			    "collide with the FDISK table!\n"));
742 			return (PART_NOT_FOUND);
743 		}
744 
745 		fill_bpb_sizes(wbpb, part, bootPart, *seekto);
746 		*seekto *= BPSEC;
747 		FdiskFATsize = lookup_FAT_size(part[bootPart].systid);
748 		if (Verbose)
749 			(void) printf(gettext("Boot partition's offset: "
750 			    "Sector %x.\n"), *seekto/BPSEC);
751 		if (lseek64(fd, *seekto, SEEK_SET) < 0) {
752 			(void) fprintf(stderr, gettext("Partition %s: "), pn);
753 			perror("");
754 			return (PART_NOT_FOUND);
755 		}
756 		return (PART_FOUND);
757 	}
758 
759 	if (drvnum == PRIMARY_DOS_DRIVE && primaryPart >= 0) {
760 		if ((*seekto = ltohi(part[primaryPart].relsect)) == 0) {
761 			(void) fprintf(stderr, gettext("Bogus FDISK entry? "
762 			    "A partition starting\nat sector 0 would "
763 			    "collide with the FDISK table!\n"));
764 			return (PART_NOT_FOUND);
765 		}
766 
767 		fill_bpb_sizes(wbpb, part, primaryPart, *seekto);
768 		*seekto *= BPSEC;
769 		FdiskFATsize = lookup_FAT_size(part[primaryPart].systid);
770 		if (Verbose)
771 			(void) printf(gettext("Partition's offset: "
772 			    "Sector %x.\n"), *seekto/BPSEC);
773 		if (lseek64(fd, *seekto, SEEK_SET) < 0) {
774 			(void) fprintf(stderr, gettext("Partition %s: "), pn);
775 			perror("");
776 			return (PART_NOT_FOUND);
777 		}
778 		return (PART_FOUND);
779 	}
780 
781 	/*
782 	 * We are not looking for the C: drive (or there was no primary
783 	 * drive found), so we had better have an extended partition or
784 	 * extra drives in the Master FDISK table.
785 	 */
786 	if ((extendedPart < 0) && (numExtraDrives == 0)) {
787 		(void) fprintf(stderr,
788 		    gettext("No such logical drive "
789 		    "(missing extended partition entry)\n"));
790 		return (PART_NOT_FOUND);
791 	}
792 
793 	if (extendedPart >= 0) {
794 		nextseek = xstartsect = ltohi(part[extendedPart].relsect);
795 		xnumsect = ltohi(part[extendedPart].numsect);
796 		do {
797 			/*
798 			 *  If the seek would not cause us to change
799 			 *  position on the drive, then we're out of
800 			 *  extended partitions to examine.
801 			 */
802 			if (nextseek == lastseek)
803 				break;
804 			logicalDriveCount += numDrives;
805 			/*
806 			 *  Seek the next extended partition, and find
807 			 *  logical drives within it.
808 			 */
809 			if (lseek64(fd, nextseek * BPSEC, SEEK_SET) < 0 ||
810 			    read(fd, &extmboot, sizeof (extmboot)) !=
811 			    sizeof (extmboot)) {
812 				perror(gettext("Unable to read extended "
813 				    "partition record"));
814 				return (PART_NOT_FOUND);
815 			}
816 			(void) memcpy(part, extmboot.parts, sizeof (part));
817 			lastseek = nextseek;
818 			if (ltohs(extmboot.signature) != MBB_MAGIC) {
819 				(void) fprintf(stderr,
820 				    gettext("Bad signature on "
821 				    "extended partition\n"));
822 				return (PART_NOT_FOUND);
823 			}
824 			/*
825 			 *  Count up drives, and track where the next
826 			 *  extended partition is in case we need it.  We
827 			 *  are expecting only one extended partition.  If
828 			 *  there is more than one we'll only go to the
829 			 *  first one we see, but warn about ignoring.
830 			 */
831 			numDrives = 0;
832 			for (i = 0; i < FD_NUMPART; i++) {
833 				if (isDosDrive(part[i].systid)) {
834 					extndDrives[numDrives++] = i;
835 					continue;
836 				} else if (isDosExtended(part[i].systid)) {
837 					if (nextseek != lastseek) {
838 						/*
839 						 * Already found an extended
840 						 * partition in this table.
841 						 */
842 						(void) fprintf(stderr,
843 						    gettext("WARNING: "
844 						    "Ignoring unexpected "
845 						    "additional extended "
846 						    "partition"));
847 						continue;
848 					}
849 					nextseek = xstartsect +
850 					    ltohi(part[i].relsect);
851 					continue;
852 				}
853 			}
854 		} while (drvnum > logicalDriveCount + numDrives);
855 
856 		if (drvnum <= logicalDriveCount + numDrives) {
857 			/*
858 			 * The number of logical drives we've found thus
859 			 * far is enough to get us to the one we were
860 			 * searching for.
861 			 */
862 			driveIndex = logicalDriveCount + numDrives - drvnum;
863 			*seekto =
864 			    ltohi(part[extndDrives[driveIndex]].relsect) +
865 			    lastseek;
866 			if (*seekto == lastseek) {
867 				(void) fprintf(stderr,
868 				    gettext("Bogus FDISK entry?  A logical "
869 				    "drive starting at\nsector 0x%llx would "
870 				    "collide with the\nFDISK information in "
871 				    "that sector.\n"), *seekto);
872 				return (PART_NOT_FOUND);
873 			} else if (*seekto <= xstartsect ||
874 			    *seekto >= (xstartsect + xnumsect)) {
875 				(void) fprintf(stderr,
876 				    gettext("Bogus FDISK entry?  "
877 				    "Logical drive start sector (0x%llx)\n"
878 				    "not within extended partition! "
879 				    "(Expected in range 0x%x - 0x%x)\n"),
880 				    *seekto, xstartsect + 1,
881 				    xstartsect + xnumsect - 1);
882 				return (PART_NOT_FOUND);
883 			}
884 			fill_bpb_sizes(wbpb, part, extndDrives[driveIndex],
885 			    *seekto);
886 			*seekto *= BPSEC;
887 			FdiskFATsize = lookup_FAT_size(
888 			    part[extndDrives[driveIndex]].systid);
889 			if (Verbose)
890 				(void) printf(gettext("Partition's offset: "
891 				    "Sector 0x%x.\n"), *seekto/BPSEC);
892 			if (lseek64(fd, *seekto, SEEK_SET) < 0) {
893 				(void) fprintf(stderr,
894 				    gettext("Partition %s: "), pn);
895 				perror("");
896 				return (PART_NOT_FOUND);
897 			}
898 			return (PART_FOUND);
899 		} else {
900 			/*
901 			 * We ran out of extended dos partition
902 			 * drives.  The only hope now is to go
903 			 * back to extra drives defined in the master
904 			 * fdisk table.  But we overwrote that table
905 			 * already, so we must load it in again.
906 			 */
907 			logicalDriveCount += numDrives;
908 			(void) memcpy(part, mb.parts, sizeof (part));
909 		}
910 	}
911 	/*
912 	 *  Still haven't found the drive, is it an extra
913 	 *  drive defined in the main FDISK table?
914 	 */
915 	if (drvnum <= logicalDriveCount + numExtraDrives) {
916 		driveIndex = logicalDriveCount + numExtraDrives - drvnum;
917 		*seekto = ltohi(part[extraDrives[driveIndex]].relsect);
918 		if (*seekto == 0) {
919 			(void) fprintf(stderr, gettext("Bogus FDISK entry? "
920 			    "A partition starting\nat sector 0 would "
921 			    "collide with the FDISK table!\n"));
922 			return (PART_NOT_FOUND);
923 		}
924 
925 		fill_bpb_sizes(wbpb, part, extraDrives[driveIndex], *seekto);
926 		*seekto *= BPSEC;
927 		FdiskFATsize =
928 		    lookup_FAT_size(part[extraDrives[driveIndex]].systid);
929 		if (Verbose)
930 			(void) printf(gettext("Partition's offset: "
931 			    "Sector %x.\n"), *seekto/BPSEC);
932 		if (lseek64(fd, *seekto, SEEK_SET) < 0) {
933 			(void) fprintf(stderr,
934 			    gettext("Partition %s: "), pn);
935 			perror("");
936 			return (PART_NOT_FOUND);
937 		}
938 		return (PART_FOUND);
939 	}
940 	(void) fprintf(stderr, gettext("No such logical drive\n"));
941 	return (PART_NOT_FOUND);
942 }
943 
944 /*
945  *  seek_nofdisk
946  *
947  *	User is asking us to trust them that they know best.
948  *	We basically won't do much seeking here, the only seeking we'll do
949  *	is if the 'hidden' parameter was given.
950  */
951 static
952 int
953 seek_nofdisk(int fd, bpb_t *wbpb, off64_t *seekto)
954 {
955 	if (TotSize > 0xffff)
956 		wbpb->bpb.sectors_in_volume = 0;
957 	else
958 		wbpb->bpb.sectors_in_volume = (short)TotSize;
959 	wbpb->bpb.sectors_in_logical_volume = TotSize;
960 
961 	*seekto = RelOffset * BPSEC;
962 	wbpb->bpb.hidden_sectors = RelOffset;
963 	wbpb->sunbpb.bs_offset_high = RelOffset >> 16;
964 	wbpb->sunbpb.bs_offset_low = RelOffset & 0xFFFF;
965 
966 	if (Verbose)
967 		(void) printf(gettext("Requested offset: Sector %x.\n"),
968 		    *seekto/BPSEC);
969 
970 	if (lseek64(fd, *seekto, SEEK_SET) < 0) {
971 		(void) fprintf(stderr,
972 		    gettext("User specified start sector %d"), RelOffset);
973 		perror("");
974 		return (PART_NOT_FOUND);
975 	}
976 	return (PART_FOUND);
977 }
978 
979 /*
980  * set_fat_string
981  *
982  *	Fill in the type string of the FAT
983  */
984 static
985 void
986 set_fat_string(bpb_t *wbpb, int fatsize)
987 {
988 	if (fatsize == 12) {
989 		(void) strncpy((char *)wbpb->ebpb.type, FAT12_TYPE_STRING,
990 		    strlen(FAT12_TYPE_STRING));
991 	} else if (fatsize == 16) {
992 		(void) strncpy((char *)wbpb->ebpb.type, FAT16_TYPE_STRING,
993 		    strlen(FAT16_TYPE_STRING));
994 	} else {
995 		(void) strncpy((char *)wbpb->ebpb.type, FAT32_TYPE_STRING,
996 		    strlen(FAT32_TYPE_STRING));
997 	}
998 }
999 
1000 /*
1001  *  prepare_image_file
1002  *
1003  *	Open the file that will hold the image (as opposed to the image
1004  *	being written to the boot sector of an actual disk).
1005  */
1006 static
1007 int
1008 prepare_image_file(char *fn, bpb_t *wbpb)
1009 {
1010 	int fd;
1011 	char zerobyte = '\0';
1012 
1013 	if ((fd = open(fn, O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
1014 		perror(fn);
1015 		exit(2);
1016 	}
1017 
1018 	if (Imagesize == 5) {
1019 		/* Disk image of a 1.2M floppy */
1020 		wbpb->bpb.sectors_in_volume = 2 * 80 * 15;
1021 		wbpb->bpb.sectors_in_logical_volume = 2 * 80 * 15;
1022 		wbpb->bpb.sectors_per_track = 15;
1023 		wbpb->bpb.heads = 2;
1024 		wbpb->bpb.media = 0xF9;
1025 		wbpb->bpb.num_root_entries = 224;
1026 		wbpb->bpb.sectors_per_cluster = 1;
1027 		wbpb->bpb.sectors_per_fat = 7;
1028 	} else {
1029 		/* Disk image of a 1.44M floppy */
1030 		wbpb->bpb.sectors_in_volume = 2 * 80 * 18;
1031 		wbpb->bpb.sectors_in_logical_volume = 2 * 80 * 18;
1032 		wbpb->bpb.sectors_per_track = 18;
1033 		wbpb->bpb.heads = 2;
1034 		wbpb->bpb.media = 0xF0;
1035 		wbpb->bpb.num_root_entries = 224;
1036 		wbpb->bpb.sectors_per_cluster = 1;
1037 		wbpb->bpb.sectors_per_fat = 9;
1038 	}
1039 
1040 	/*
1041 	 * Make a holey file, with length the exact
1042 	 * size of the floppy image.
1043 	 */
1044 	if (lseek(fd, (wbpb->bpb.sectors_in_volume * BPSEC)-1, SEEK_SET) < 0) {
1045 		(void) close(fd);
1046 		perror(fn);
1047 		exit(2);
1048 	}
1049 
1050 	if (write(fd, &zerobyte, 1) != 1) {
1051 		(void) close(fd);
1052 		perror(fn);
1053 		exit(2);
1054 	}
1055 
1056 	if (lseek(fd, 0, SEEK_SET) < 0) {
1057 		(void) close(fd);
1058 		perror(fn);
1059 		exit(2);
1060 	}
1061 
1062 	Fatentsize = 12;  /* Size of fat entry in bits */
1063 	set_fat_string(wbpb, Fatentsize);
1064 
1065 	wbpb->ebpb.phys_drive_num = 0;
1066 
1067 	wbpb->sunbpb.bs_offset_high = 0;
1068 	wbpb->sunbpb.bs_offset_low = 0;
1069 
1070 	return (fd);
1071 }
1072 
1073 /*
1074  *  partn_lecture
1075  *
1076  *	Give a brief sermon on dev_name user should pass to
1077  *	the program from the command line.
1078  *
1079  */
1080 static
1081 void
1082 partn_lecture(char *dn)
1083 {
1084 	(void) fprintf(stderr,
1085 	    gettext("\nDevice %s was assumed to be a diskette.\n"
1086 	    "A diskette specific operation failed on this device.\n"
1087 	    "If the device is a hard disk, provide the name of "
1088 	    "the full physical disk,\n"
1089 	    "and qualify that name with a logical drive specifier.\n\n"
1090 	    "Hint: the device is usually something similar to\n\n"
1091 	    "/dev/rdsk/c0d0p0 or /dev/rdsk/c0t0d0p0 (x86)\n"
1092 	    "/dev/rdsk/c0t5d0s2 (sparc)\n\n"
1093 	    "The drive specifier is appended to the device name."
1094 	    " For example:\n\n"
1095 	    "/dev/rdsk/c0t5d0s2:c or /dev/rdsk/c0d0p0:boot\n\n"), dn);
1096 }
1097 
1098 static
1099 void
1100 warn_funky_floppy(void)
1101 {
1102 	(void) fprintf(stderr,
1103 	    gettext("Use the 'nofdisk' option to create file systems\n"
1104 	    "on non-standard floppies.\n\n"));
1105 	exit(4);
1106 }
1107 
1108 static
1109 void
1110 warn_funky_fatsize(void)
1111 {
1112 	(void) fprintf(stderr,
1113 	    gettext("Non-standard FAT size requested for floppy.\n"
1114 	    "The 'nofdisk' option must be used to\n"
1115 	    "override the 12 bit floppy default.\n\n"));
1116 	exit(4);
1117 }
1118 
1119 static
1120 void
1121 floppy_bpb_fillin(bpb_t *wbpb, int diam, int hds, int spt)
1122 {
1123 	switch (diam) {
1124 	case 3:
1125 		switch (hds) {
1126 		case 2:
1127 			switch (spt) {
1128 			case 9:
1129 				wbpb->bpb.media = 0xF9;
1130 				wbpb->bpb.num_root_entries = 112;
1131 				wbpb->bpb.sectors_per_cluster = 2;
1132 				wbpb->bpb.sectors_per_fat = 3;
1133 				break;
1134 			case 18:
1135 				wbpb->bpb.media = 0xF0;
1136 				wbpb->bpb.num_root_entries = 224;
1137 				wbpb->bpb.sectors_per_cluster = 1;
1138 				wbpb->bpb.sectors_per_fat = 9;
1139 				break;
1140 			case 36:
1141 				wbpb->bpb.media = 0xF0;
1142 				wbpb->bpb.num_root_entries = 240;
1143 				wbpb->bpb.sectors_per_cluster = 2;
1144 				wbpb->bpb.sectors_per_fat = 9;
1145 				break;
1146 			default:
1147 				(void) fprintf(stderr,
1148 				    gettext("Unknown diskette parameters!  "
1149 				    "3.5'' diskette with %d heads "
1150 				    "and %d sectors/track.\n"), hds, spt);
1151 				warn_funky_floppy();
1152 			}
1153 			break;
1154 		case 1:
1155 		default:
1156 			(void) fprintf(stderr,
1157 			    gettext("Unknown diskette parameters!  "
1158 			    "3.5'' diskette with %d heads "), hds);
1159 			warn_funky_floppy();
1160 		}
1161 		break;
1162 	case 5:
1163 		switch (hds) {
1164 		case 2:
1165 			switch (spt) {
1166 			case 15:
1167 				wbpb->bpb.media = 0xF9;
1168 				wbpb->bpb.num_root_entries = 224;
1169 				wbpb->bpb.sectors_per_cluster = 1;
1170 				wbpb->bpb.sectors_per_fat = 7;
1171 				break;
1172 			case 9:
1173 				wbpb->bpb.media = 0xFD;
1174 				wbpb->bpb.num_root_entries = 112;
1175 				wbpb->bpb.sectors_per_cluster = 2;
1176 				wbpb->bpb.sectors_per_fat = 2;
1177 				break;
1178 			case 8:
1179 				wbpb->bpb.media = 0xFF;
1180 				wbpb->bpb.num_root_entries = 112;
1181 				wbpb->bpb.sectors_per_cluster = 1;
1182 				wbpb->bpb.sectors_per_fat = 2;
1183 				break;
1184 			default:
1185 				(void) fprintf(stderr,
1186 				    gettext("Unknown diskette parameters!  "
1187 				    "5.25'' diskette with %d heads "
1188 				    "and %d sectors/track.\n"), hds, spt);
1189 				warn_funky_floppy();
1190 			}
1191 			break;
1192 		case 1:
1193 			switch (spt) {
1194 			case 9:
1195 				wbpb->bpb.media = 0xFC;
1196 				wbpb->bpb.num_root_entries = 64;
1197 				wbpb->bpb.sectors_per_cluster = 1;
1198 				wbpb->bpb.sectors_per_fat = 2;
1199 				break;
1200 			case 8:
1201 				wbpb->bpb.media = 0xFE;
1202 				wbpb->bpb.num_root_entries = 64;
1203 				wbpb->bpb.sectors_per_cluster = 1;
1204 				wbpb->bpb.sectors_per_fat = 1;
1205 				break;
1206 			default:
1207 				(void) fprintf(stderr,
1208 				    gettext("Unknown diskette parameters! "
1209 				    "5.25'' diskette with %d heads "
1210 				    "and %d sectors/track.\n"), hds, spt);
1211 				warn_funky_floppy();
1212 			}
1213 			break;
1214 		default:
1215 			(void) fprintf(stderr,
1216 			    gettext("Unknown diskette parameters! "
1217 			    "5.25'' diskette with %d heads."), hds);
1218 			warn_funky_floppy();
1219 		}
1220 		break;
1221 	default:
1222 		(void) fprintf(stderr,
1223 		    gettext("\nUnknown diskette type.  Only know about "
1224 		    "5.25'' and 3.5'' diskettes.\n"));
1225 		warn_funky_floppy();
1226 	}
1227 }
1228 
1229 /*
1230  *  lookup_floppy
1231  *
1232  *	Look up a media descriptor byte and other crucial BPB values
1233  *	based on floppy characteristics.
1234  */
1235 static
1236 void
1237 lookup_floppy(struct fd_char *fdchar, bpb_t *wbpb)
1238 {
1239 	ulong_t tsize;
1240 	ulong_t cyls, spt, hds, diam;
1241 
1242 	cyls = fdchar->fdc_ncyl;
1243 	diam = fdchar->fdc_medium;
1244 	spt = fdchar->fdc_secptrack;
1245 	hds = fdchar->fdc_nhead;
1246 
1247 	tsize = cyls * hds * spt;
1248 
1249 	if (GetFsParams)
1250 		TotSize = tsize;
1251 
1252 	if (GetSize) {
1253 		wbpb->bpb.sectors_in_logical_volume = tsize;
1254 	} else {
1255 		wbpb->bpb.sectors_in_logical_volume =
1256 		    warn_mismatch(
1257 		    gettext("length of partition (in sectors)"),
1258 		    gettext("FDIOGCHAR call"), tsize, TotSize);
1259 	}
1260 	wbpb->bpb.sectors_in_volume =
1261 	    (short)wbpb->bpb.sectors_in_logical_volume;
1262 
1263 	if (GetSPT) {
1264 		wbpb->bpb.sectors_per_track = spt;
1265 	} else {
1266 		wbpb->bpb.sectors_per_track =
1267 		    warn_mismatch(
1268 		    gettext("sectors per track"),
1269 		    gettext("FDIOGCHAR call"), spt, SecPerTrk);
1270 		spt = wbpb->bpb.sectors_per_track;
1271 	}
1272 
1273 	if (GetTPC) {
1274 		wbpb->bpb.heads = hds;
1275 	} else {
1276 		wbpb->bpb.heads =
1277 		    warn_mismatch(
1278 		    gettext("number of heads"),
1279 		    gettext("FDIOGCHAR call"), hds, TrkPerCyl);
1280 		hds = wbpb->bpb.heads;
1281 	}
1282 
1283 	Fatentsize = 12;  /* Size of fat entry in bits */
1284 	if (!GetBPF && BitsPerFAT != Fatentsize) {
1285 		warn_funky_fatsize();
1286 	}
1287 	set_fat_string(wbpb, Fatentsize);
1288 
1289 	wbpb->ebpb.phys_drive_num = 0;
1290 
1291 	wbpb->bpb.hidden_sectors = 0;
1292 	wbpb->sunbpb.bs_offset_high = 0;
1293 	wbpb->sunbpb.bs_offset_low = 0;
1294 
1295 	floppy_bpb_fillin(wbpb, diam, hds, spt);
1296 }
1297 
1298 /*
1299  *  compute_cluster_size
1300  *
1301  *	Compute an acceptable sectors/cluster value.
1302  *
1303  * 	Based on values from the Hardware White Paper
1304  *	from Microsoft.
1305  *	"Microsoft Extensible Firmware Initiative
1306  *	 FAT32 File System Specification
1307  *	 FAT: General Overview of On-Disk Format"
1308  *
1309  *	Version 1.03, December 6, 2000
1310  *
1311  */
1312 static
1313 void
1314 compute_cluster_size(bpb_t *wbpb)
1315 {
1316 	ulong_t volsize;
1317 	ulong_t spc;
1318 	ulong_t rds, tmpval1, tmpval2;
1319 	ulong_t fatsz;
1320 	int newfat = 16;
1321 
1322 #define	FAT12_MAX_CLUSTERS	0x0FF4
1323 #define	FAT16_MAX_CLUSTERS	0xFFF4
1324 #define	FAT32_MAX_CLUSTERS	0x0FFFFFF0
1325 #define	FAT32_SUGGESTED_NCLUST	0x400000
1326 
1327 	/* compute volume size in sectors. */
1328 	volsize = wbpb->bpb.sectors_in_volume ? wbpb->bpb.sectors_in_volume :
1329 	    wbpb->bpb.sectors_in_logical_volume;
1330 	volsize -= wbpb->bpb.resv_sectors;
1331 
1332 	if (GetSPC) {
1333 		/*
1334 		 * User indicated what sort of FAT to create,
1335 		 * make sure it is valid with the given size
1336 		 * and compute an SPC value.
1337 		 */
1338 		if (!MakeFAT32) { /* FAT16 */
1339 			/* volsize is in sectors */
1340 			if (volsize < FAT12_MAX_CLUSTERS) {
1341 				(void) fprintf(stderr,
1342 				    gettext("Requested size is too "
1343 				    "small for FAT16.\n"));
1344 				exit(4);
1345 			}
1346 			/* SPC must be a power of 2 */
1347 			for (spc = 1; spc <= 64; spc = spc * 2) {
1348 				if (volsize < spc * FAT16_MAX_CLUSTERS)
1349 					break;
1350 			}
1351 			if (volsize > (spc * FAT16_MAX_CLUSTERS)) {
1352 				(void) fprintf(stderr,
1353 				    gettext("Requested size is too "
1354 				    "large for FAT16.\n"));
1355 				exit(4);
1356 			}
1357 		} else { /* FAT32 */
1358 			/* volsize is in sectors */
1359 			if (volsize < FAT16_MAX_CLUSTERS) {
1360 				(void) fprintf(stderr,
1361 				    gettext("Requested size is too "
1362 				    "small for FAT32.\n"));
1363 				exit(4);
1364 			}
1365 			/* SPC must be a power of 2 */
1366 			for (spc = 1; spc <= 64; spc = spc * 2) {
1367 				if (volsize < (spc * FAT32_SUGGESTED_NCLUST))
1368 					break;
1369 			}
1370 			if (volsize > (spc * FAT32_MAX_CLUSTERS)) {
1371 				(void) fprintf(stderr,
1372 				    gettext("Requested size is too "
1373 				    "large for FAT32.\n"));
1374 				exit(4);
1375 			}
1376 		}
1377 	} else {
1378 		/*
1379 		 * User gave the SPC as an explicit option,
1380 		 * make sure it will work with the requested
1381 		 * volume size.
1382 		 */
1383 		int nclust;
1384 
1385 		spc = SecPerClust;
1386 		nclust = volsize / spc;
1387 
1388 		if (nclust <= FAT16_MAX_CLUSTERS && MakeFAT32) {
1389 			(void) fprintf(stderr, gettext("Requested size is too "
1390 			    "small for FAT32.\n"));
1391 			exit(4);
1392 		}
1393 		if (!MakeFAT32) {
1394 			/* Determine if FAT12 or FAT16 */
1395 			if (nclust < FAT12_MAX_CLUSTERS)
1396 				newfat = 12;
1397 			else if (nclust < FAT16_MAX_CLUSTERS)
1398 				newfat = 16;
1399 			else {
1400 				(void) fprintf(stderr,
1401 				    gettext("Requested size is too "
1402 				    "small for FAT32.\n"));
1403 				exit(4);
1404 			}
1405 		}
1406 	}
1407 
1408 	/*
1409 	 * RootDirSectors = ((BPB_RootEntCnt * 32) +
1410 	 *	(BPB_BytsPerSec  1)) / BPB_BytsPerSec;
1411 	 */
1412 	rds = ((wbpb->bpb.num_root_entries * 32) +
1413 	    (wbpb->bpb.bytes_sector - 1)) / wbpb->bpb.bytes_sector;
1414 
1415 	if (GetBPF) {
1416 		if (MakeFAT32)
1417 			Fatentsize = 32;
1418 		else
1419 			Fatentsize = newfat;
1420 	} else {
1421 		Fatentsize = BitsPerFAT;
1422 
1423 		if (Fatentsize == 12 &&
1424 		    (volsize - rds) >= DOS_F12MAXC * spc) {
1425 			/*
1426 			 * If we don't have an input TTY, or we aren't
1427 			 * really doing anything, then don't ask
1428 			 * questions.  Assume a yes answer to any
1429 			 * questions we would ask.
1430 			 */
1431 			if (Notreally || !isatty(fileno(stdin))) {
1432 				(void) printf(
1433 				gettext("Volume too large for 12 bit FAT,"
1434 				    " increasing to 16 bit FAT size.\n"));
1435 				(void) fflush(stdout);
1436 				Fatentsize = 16;
1437 			} else {
1438 				(void) printf(
1439 				gettext("Volume too large for a 12 bit FAT.\n"
1440 				    "Increase to 16 bit FAT "
1441 				    "and continue (y/n)? "));
1442 				(void) fflush(stdout);
1443 				if (yes())
1444 					Fatentsize = 16;
1445 				else
1446 					exit(5);
1447 			}
1448 		}
1449 	}
1450 	wbpb->bpb.sectors_per_cluster = spc;
1451 
1452 	if (!GetFsParams && FdiskFATsize < 0) {
1453 		(void) printf(
1454 		    gettext("Cannot verify chosen/computed FAT "
1455 		    "entry size (%d bits) with FDISK table.\n"
1456 		    "FDISK table has an unknown file system "
1457 		    "type for this device.  Giving up...\n"),
1458 		    Fatentsize, Fatentsize);
1459 		exit(6);
1460 	} else if (!GetFsParams && FdiskFATsize && FdiskFATsize != Fatentsize) {
1461 		(void) printf(
1462 		    gettext("Chosen/computed FAT entry size (%d bits) "
1463 		    "does not match FDISK table (%d bits).\n"),
1464 		    Fatentsize, FdiskFATsize);
1465 		(void) printf(
1466 		    gettext("Use -o fat=%d to build a FAT "
1467 		    "that matches the FDISK entry.\n"), FdiskFATsize);
1468 		exit(6);
1469 	}
1470 	set_fat_string(wbpb, Fatentsize);
1471 	/*
1472 	 * Compure the FAT sizes according to algorithm from Microsoft:
1473 	 *
1474 	 * RootDirSectors = ((BPB_RootEntCnt * 32) +
1475 	 *	(BPB_BytsPerSec  1)) / BPB_BytsPerSec;
1476 	 * TmpVal1 = DskSize - (BPB_ResvdSecCnt + RootDirSectors);
1477 	 * TmpVal2 = (256 * BPB_SecPerClus) + BPB_NumFATs;
1478 	 * If (FATType == FAT32)
1479 	 * 	TmpVal2 = TmpVal2 / 2;
1480 	 * FATSz = (TMPVal1 + (TmpVal2  1)) / TmpVal2;
1481 	 * If (FATType == FAT32) {
1482 	 * 	BPB_FATSz16 = 0;
1483 	 *	BPB_FATSz32 = FATSz;
1484 	 * } else {
1485 	 *	BPB_FATSz16 = LOWORD(FATSz);
1486 	 * 	// there is no BPB_FATSz32 in a FAT16 BPB
1487 	 * }
1488 	 */
1489 	tmpval1 = volsize - (wbpb->bpb.resv_sectors + rds);
1490 
1491 	tmpval2 = (256 * wbpb->bpb.sectors_per_cluster) + wbpb->bpb.num_fats;
1492 
1493 	if (Fatentsize == 32)
1494 		tmpval2 = tmpval2 / 2;
1495 
1496 	fatsz = (tmpval1 + (tmpval2 - 1)) / tmpval2;
1497 
1498 	/* Compute a sector/fat figure */
1499 	switch (Fatentsize) {
1500 	case 32:
1501 		wbpb->bpb.sectors_per_fat = 0;
1502 		wbpb->bpb32.big_sectors_per_fat = fatsz;
1503 		if (Verbose)
1504 			(void) printf("compute_cluster_size: Sectors per "
1505 			    "FAT32 = %d\n", wbpb->bpb32.big_sectors_per_fat);
1506 		break;
1507 	case 12:
1508 	default:	/* 16 bit FAT */
1509 		wbpb->bpb.sectors_per_fat = (ushort_t)(fatsz & 0x0000FFFF);
1510 		if (Verbose)
1511 			(void) printf("compute_cluster_size: Sectors per "
1512 			    "FAT16 = %d\n", wbpb->bpb.sectors_per_fat);
1513 		break;
1514 	}
1515 }
1516 
1517 static
1518 void
1519 find_fixed_details(int fd, bpb_t *wbpb)
1520 {
1521 	struct dk_geom dginfo;
1522 
1523 	/*
1524 	 *  Look up the last remaining bits of info we need
1525 	 *  that is specific to the hard drive using a disk ioctl.
1526 	 */
1527 	if (GetSPT || GetTPC) {
1528 		if (ioctl(fd, DKIOCG_VIRTGEOM, &dginfo) == -1 &&
1529 		    ioctl(fd, DKIOCG_PHYGEOM, &dginfo) == -1 &&
1530 		    ioctl(fd, DKIOCGGEOM, &dginfo) == -1) {
1531 			(void) close(fd);
1532 			perror(
1533 			    gettext("Drive geometry lookup (need "
1534 			    "tracks/cylinder and/or sectors/track"));
1535 			exit(2);
1536 		}
1537 	}
1538 
1539 	wbpb->bpb.heads = (GetTPC ? dginfo.dkg_nhead : TrkPerCyl);
1540 	wbpb->bpb.sectors_per_track = (GetSPT ? dginfo.dkg_nsect : SecPerTrk);
1541 
1542 	if (Verbose) {
1543 		if (GetTPC) {
1544 			(void) printf(
1545 			    gettext("DKIOCG determined number of heads = %d\n"),
1546 			    dginfo.dkg_nhead);
1547 		}
1548 		if (GetSPT) {
1549 			(void) printf(
1550 			    gettext("DKIOCG determined sectors per track"
1551 			    " = %d\n"), dginfo.dkg_nsect);
1552 		}
1553 	}
1554 
1555 	/*
1556 	 * XXX - MAY need an additional flag (or flags) to set media
1557 	 * and physical drive number fields.  That in the case of weird
1558 	 * floppies that have to go through 'nofdisk' route for formatting.
1559 	 */
1560 	wbpb->bpb.media = 0xF8;
1561 	if (MakeFAT32)
1562 		wbpb->bpb.num_root_entries = 0;
1563 	else
1564 		wbpb->bpb.num_root_entries = 512;
1565 	wbpb->ebpb.phys_drive_num = 0x80;
1566 	compute_cluster_size(wbpb);
1567 }
1568 
1569 static
1570 char *
1571 stat_actual_disk(char *diskname, struct stat *info, char **suffix)
1572 {
1573 	char *actualdisk;
1574 
1575 	if (stat(diskname, info)) {
1576 		/*
1577 		 *  Device named on command line doesn't exist.  That
1578 		 *  probably means there is a partition-specifying
1579 		 *  suffix attached to the actual disk name.
1580 		 */
1581 		actualdisk = strtok(strdup(diskname), ":");
1582 		if (*suffix = strchr(diskname, ':'))
1583 			(*suffix)++;
1584 
1585 		if (stat(actualdisk, info)) {
1586 			perror(actualdisk);
1587 			exit(2);
1588 		}
1589 	} else {
1590 		actualdisk = strdup(diskname);
1591 	}
1592 
1593 	return (actualdisk);
1594 }
1595 
1596 static
1597 void
1598 compute_file_area_size(bpb_t *wbpb)
1599 {
1600 	int FATSz;
1601 	int TotSec;
1602 	int DataSec;
1603 	int RootDirSectors =
1604 	    ((wbpb->bpb.num_root_entries * 32) + (wbpb->bpb.bytes_sector - 1)) /
1605 	    wbpb->bpb.bytes_sector;
1606 
1607 	if (wbpb->bpb.sectors_per_fat) {
1608 		/*
1609 		 * Good old FAT12 or FAT16
1610 		 */
1611 		FATSz = wbpb->bpb.sectors_per_fat;
1612 		TotSec = wbpb->bpb.sectors_in_volume;
1613 	} else {
1614 		/*
1615 		 *  FAT32
1616 		 */
1617 		FATSz = wbpb->bpb32.big_sectors_per_fat;
1618 		TotSec = wbpb->bpb.sectors_in_logical_volume;
1619 	}
1620 
1621 	DataSec = TotSec -
1622 	    (wbpb->bpb.resv_sectors + (wbpb->bpb.num_fats * FATSz) +
1623 	    RootDirSectors);
1624 
1625 
1626 	/*
1627 	 * Now change sectors to clusters
1628 	 */
1629 	TotalClusters = DataSec / wbpb->bpb.sectors_per_cluster;
1630 
1631 	if (Verbose)
1632 		(void) printf(gettext("Disk has a file area of %d "
1633 		    "allocation units,\neach with %d sectors = %d "
1634 		    "bytes.\n"), TotalClusters, wbpb->bpb.sectors_per_cluster,
1635 		    TotalClusters * wbpb->bpb.sectors_per_cluster * BPSEC);
1636 }
1637 
1638 #ifndef i386
1639 /*
1640  *  swap_pack_{bpb,bpb32,sebpb}cpy
1641  *
1642  *	If not on an x86 we assume the structures making up the bpb
1643  *	were not packed and that longs and shorts need to be byte swapped
1644  *	(we've kept everything in host order up until now).  A new architecture
1645  *	might not need to swap or might not need to pack, in which case
1646  *	new routines will have to be written.  Of course if an architecture
1647  *	supports both packing and little-endian host order, it can follow the
1648  *	same path as the x86 code.
1649  */
1650 static
1651 void
1652 swap_pack_bpbcpy(struct _boot_sector *bsp, bpb_t *wbpb)
1653 {
1654 	uchar_t *fillp;
1655 
1656 	fillp = (uchar_t *)&(bsp->bs_filler[ORIG_BPB_START_INDEX]);
1657 
1658 	store_16_bits(&fillp, wbpb->bpb.bytes_sector);
1659 	*fillp++ = wbpb->bpb.sectors_per_cluster;
1660 	store_16_bits(&fillp, wbpb->bpb.resv_sectors);
1661 	*fillp++ = wbpb->bpb.num_fats;
1662 	store_16_bits(&fillp, wbpb->bpb.num_root_entries);
1663 	store_16_bits(&fillp, wbpb->bpb.sectors_in_volume);
1664 	*fillp++ = wbpb->bpb.media;
1665 	store_16_bits(&fillp, wbpb->bpb.sectors_per_fat);
1666 	store_16_bits(&fillp, wbpb->bpb.sectors_per_track);
1667 	store_16_bits(&fillp, wbpb->bpb.heads);
1668 	store_32_bits(&fillp, wbpb->bpb.hidden_sectors);
1669 	store_32_bits(&fillp, wbpb->bpb.sectors_in_logical_volume);
1670 
1671 	*fillp++ = wbpb->ebpb.phys_drive_num;
1672 	*fillp++ = wbpb->ebpb.reserved;
1673 	*fillp++ = wbpb->ebpb.ext_signature;
1674 	store_32_bits(&fillp, wbpb->ebpb.volume_id);
1675 	(void) strncpy((char *)fillp, (char *)wbpb->ebpb.volume_label, 11);
1676 	fillp += 11;
1677 	(void) strncpy((char *)fillp, (char *)wbpb->ebpb.type, 8);
1678 }
1679 
1680 static
1681 void
1682 swap_pack_bpb32cpy(struct _boot_sector32 *bsp, bpb_t *wbpb)
1683 {
1684 	uchar_t *fillp;
1685 	int r;
1686 
1687 	fillp = (uchar_t *)&(bsp->bs_filler[ORIG_BPB_START_INDEX]);
1688 
1689 	store_16_bits(&fillp, wbpb->bpb.bytes_sector);
1690 	*fillp++ = wbpb->bpb.sectors_per_cluster;
1691 	store_16_bits(&fillp, wbpb->bpb.resv_sectors);
1692 	*fillp++ = wbpb->bpb.num_fats;
1693 	store_16_bits(&fillp, wbpb->bpb.num_root_entries);
1694 	store_16_bits(&fillp, wbpb->bpb.sectors_in_volume);
1695 	*fillp++ = wbpb->bpb.media;
1696 	store_16_bits(&fillp, wbpb->bpb.sectors_per_fat);
1697 	store_16_bits(&fillp, wbpb->bpb.sectors_per_track);
1698 	store_16_bits(&fillp, wbpb->bpb.heads);
1699 	store_32_bits(&fillp, wbpb->bpb.hidden_sectors);
1700 	store_32_bits(&fillp, wbpb->bpb.sectors_in_logical_volume);
1701 
1702 	store_32_bits(&fillp, wbpb->bpb32.big_sectors_per_fat);
1703 	store_16_bits(&fillp, wbpb->bpb32.ext_flags);
1704 	*fillp++ = wbpb->bpb32.fs_vers_lo;
1705 	*fillp++ = wbpb->bpb32.fs_vers_hi;
1706 	store_32_bits(&fillp, wbpb->bpb32.root_dir_clust);
1707 	store_16_bits(&fillp, wbpb->bpb32.fsinfosec);
1708 	store_16_bits(&fillp, wbpb->bpb32.backupboot);
1709 	for (r = 0; r < 6; r++)
1710 		store_16_bits(&fillp, wbpb->bpb32.reserved[r]);
1711 
1712 	*fillp++ = wbpb->ebpb.phys_drive_num;
1713 	*fillp++ = wbpb->ebpb.reserved;
1714 	*fillp++ = wbpb->ebpb.ext_signature;
1715 	store_32_bits(&fillp, wbpb->ebpb.volume_id);
1716 	(void) strncpy((char *)fillp, (char *)wbpb->ebpb.volume_label, 11);
1717 	fillp += 11;
1718 	(void) strncpy((char *)fillp, (char *)wbpb->ebpb.type, 8);
1719 }
1720 
1721 static
1722 void
1723 swap_pack_sebpbcpy(struct _boot_sector *bsp, bpb_t *wbpb)
1724 {
1725 	uchar_t *fillp;
1726 
1727 	fillp = bsp->bs_sun_bpb;
1728 	store_16_bits(&fillp, wbpb->sunbpb.bs_offset_high);
1729 	store_16_bits(&fillp, wbpb->sunbpb.bs_offset_low);
1730 }
1731 
1732 static
1733 void
1734 swap_pack_grabbpb(bpb_t *wbpb, struct _boot_sector *bsp)
1735 {
1736 	uchar_t *grabp;
1737 
1738 	grabp = (uchar_t *)&(bsp->bs_filler[ORIG_BPB_START_INDEX]);
1739 
1740 	((uchar_t *)&(wbpb->bpb.bytes_sector))[1] = *grabp++;
1741 	((uchar_t *)&(wbpb->bpb.bytes_sector))[0] = *grabp++;
1742 	wbpb->bpb.sectors_per_cluster = *grabp++;
1743 	((uchar_t *)&(wbpb->bpb.resv_sectors))[1] = *grabp++;
1744 	((uchar_t *)&(wbpb->bpb.resv_sectors))[0] = *grabp++;
1745 	wbpb->bpb.num_fats = *grabp++;
1746 	((uchar_t *)&(wbpb->bpb.num_root_entries))[1] = *grabp++;
1747 	((uchar_t *)&(wbpb->bpb.num_root_entries))[0] = *grabp++;
1748 	((uchar_t *)&(wbpb->bpb.sectors_in_volume))[1] = *grabp++;
1749 	((uchar_t *)&(wbpb->bpb.sectors_in_volume))[0] = *grabp++;
1750 	wbpb->bpb.media = *grabp++;
1751 	((uchar_t *)&(wbpb->bpb.sectors_per_fat))[1] = *grabp++;
1752 	((uchar_t *)&(wbpb->bpb.sectors_per_fat))[0] = *grabp++;
1753 	((uchar_t *)&(wbpb->bpb.sectors_per_track))[1] = *grabp++;
1754 	((uchar_t *)&(wbpb->bpb.sectors_per_track))[0] = *grabp++;
1755 	((uchar_t *)&(wbpb->bpb.heads))[1] = *grabp++;
1756 	((uchar_t *)&(wbpb->bpb.heads))[0] = *grabp++;
1757 	((uchar_t *)&(wbpb->bpb.hidden_sectors))[3] = *grabp++;
1758 	((uchar_t *)&(wbpb->bpb.hidden_sectors))[2] = *grabp++;
1759 	((uchar_t *)&(wbpb->bpb.hidden_sectors))[1] = *grabp++;
1760 	((uchar_t *)&(wbpb->bpb.hidden_sectors))[0] = *grabp++;
1761 	((uchar_t *)&(wbpb->bpb.sectors_in_logical_volume))[3] = *grabp++;
1762 	((uchar_t *)&(wbpb->bpb.sectors_in_logical_volume))[2] = *grabp++;
1763 	((uchar_t *)&(wbpb->bpb.sectors_in_logical_volume))[1] = *grabp++;
1764 	((uchar_t *)&(wbpb->bpb.sectors_in_logical_volume))[0] = *grabp++;
1765 	wbpb->ebpb.phys_drive_num = *grabp++;
1766 	wbpb->ebpb.reserved = *grabp++;
1767 	wbpb->ebpb.ext_signature = *grabp++;
1768 	((uchar_t *)&(wbpb->ebpb.volume_id))[3] = *grabp++;
1769 	((uchar_t *)&(wbpb->ebpb.volume_id))[2] = *grabp++;
1770 	((uchar_t *)&(wbpb->ebpb.volume_id))[1] = *grabp++;
1771 	((uchar_t *)&(wbpb->ebpb.volume_id))[0] = *grabp++;
1772 
1773 	(void) strncpy((char *)wbpb->ebpb.volume_label, (char *)grabp, 11);
1774 	grabp += 11;
1775 	(void) strncpy((char *)wbpb->ebpb.type, (char *)grabp, 8);
1776 }
1777 
1778 static
1779 void
1780 swap_pack_grabsebpb(bpb_t *wbpb, struct _boot_sector *bsp)
1781 {
1782 	uchar_t *grabp;
1783 
1784 	grabp = bsp->bs_sun_bpb;
1785 	((uchar_t *)&(wbpb->sunbpb.bs_offset_high))[1] = *grabp++;
1786 	((uchar_t *)&(wbpb->sunbpb.bs_offset_high))[0] = *grabp++;
1787 	((uchar_t *)&(wbpb->sunbpb.bs_offset_low))[1] = *grabp++;
1788 	((uchar_t *)&(wbpb->sunbpb.bs_offset_low))[0] = *grabp++;
1789 }
1790 
1791 static
1792 void
1793 swap_pack_grab32bpb(bpb_t *wbpb, struct _boot_sector *bsp)
1794 {
1795 	uchar_t *grabp;
1796 
1797 	grabp = (uchar_t *)&(bsp->bs_filler[BPB_32_START_INDEX]);
1798 
1799 	((uchar_t *)&(wbpb->bpb32.big_sectors_per_fat))[3] = *grabp++;
1800 	((uchar_t *)&(wbpb->bpb32.big_sectors_per_fat))[2] = *grabp++;
1801 	((uchar_t *)&(wbpb->bpb32.big_sectors_per_fat))[1] = *grabp++;
1802 	((uchar_t *)&(wbpb->bpb32.big_sectors_per_fat))[0] = *grabp++;
1803 	((uchar_t *)&(wbpb->bpb32.ext_flags))[1] = *grabp++;
1804 	((uchar_t *)&(wbpb->bpb32.ext_flags))[0] = *grabp++;
1805 	wbpb->bpb32.fs_vers_lo = *grabp++;
1806 	wbpb->bpb32.fs_vers_hi = *grabp++;
1807 	((uchar_t *)&(wbpb->bpb32.root_dir_clust))[3] = *grabp++;
1808 	((uchar_t *)&(wbpb->bpb32.root_dir_clust))[2] = *grabp++;
1809 	((uchar_t *)&(wbpb->bpb32.root_dir_clust))[1] = *grabp++;
1810 	((uchar_t *)&(wbpb->bpb32.root_dir_clust))[0] = *grabp++;
1811 	((uchar_t *)&(wbpb->bpb32.fsinfosec))[1] = *grabp++;
1812 	((uchar_t *)&(wbpb->bpb32.fsinfosec))[0] = *grabp++;
1813 	((uchar_t *)&(wbpb->bpb32.backupboot))[1] = *grabp++;
1814 	((uchar_t *)&(wbpb->bpb32.backupboot))[0] = *grabp++;
1815 	((uchar_t *)&(wbpb->bpb32.reserved[0]))[1] = *grabp++;
1816 	((uchar_t *)&(wbpb->bpb32.reserved[0]))[0] = *grabp++;
1817 	((uchar_t *)&(wbpb->bpb32.reserved[1]))[1] = *grabp++;
1818 	((uchar_t *)&(wbpb->bpb32.reserved[1]))[0] = *grabp++;
1819 	((uchar_t *)&(wbpb->bpb32.reserved[2]))[1] = *grabp++;
1820 	((uchar_t *)&(wbpb->bpb32.reserved[2]))[0] = *grabp++;
1821 	((uchar_t *)&(wbpb->bpb32.reserved[3]))[1] = *grabp++;
1822 	((uchar_t *)&(wbpb->bpb32.reserved[3]))[0] = *grabp++;
1823 	((uchar_t *)&(wbpb->bpb32.reserved[4]))[1] = *grabp++;
1824 	((uchar_t *)&(wbpb->bpb32.reserved[4]))[0] = *grabp++;
1825 	((uchar_t *)&(wbpb->bpb32.reserved[5]))[1] = *grabp++;
1826 	((uchar_t *)&(wbpb->bpb32.reserved[5]))[0] = *grabp++;
1827 }
1828 #endif	/* ! i386 */
1829 
1830 static
1831 void
1832 dashm_bail(int fd)
1833 {
1834 	(void) fprintf(stderr,
1835 	    gettext("This media does not appear to be "
1836 	    "formatted with a FAT file system.\n"));
1837 	(void) close(fd);
1838 	exit(6);
1839 }
1840 
1841 /*
1842  *  read_existing_bpb
1843  *
1844  *	Grab the first sector, which we think is a bios parameter block.
1845  *	If it looks bad, bail.  Otherwise fill in the parameter struct
1846  *	fields that matter.
1847  */
1848 static
1849 void
1850 read_existing_bpb(int fd, bpb_t *wbpb)
1851 {
1852 	boot_sector_t ubpb;
1853 
1854 	if (read(fd, ubpb.buf, BPSEC) < BPSEC) {
1855 		perror(gettext("Read BIOS parameter block "
1856 		    "from previously formatted media"));
1857 		(void) close(fd);
1858 		exit(6);
1859 	}
1860 
1861 	if (ltohs(ubpb.mb.signature) != BOOTSECSIG) {
1862 		dashm_bail(fd);
1863 	}
1864 
1865 #ifdef i386
1866 	(void) memcpy(&(wbpb->bpb), &(ubpb.bs.bs_front.bs_bpb),
1867 	    sizeof (wbpb->bpb));
1868 	(void) memcpy(&(wbpb->ebpb), &(ubpb.bs.bs_ebpb), sizeof (wbpb->ebpb));
1869 #else
1870 	swap_pack_grabbpb(wbpb, &(ubpb.bs));
1871 #endif
1872 	if (SunBPBfields) {
1873 #ifdef i386
1874 		(void) memcpy(&(wbpb->sunbpb), &(ubpb.bs.bs_sebpb),
1875 		    sizeof (wbpb->sunbpb));
1876 #else
1877 		swap_pack_grabsebpb(wbpb, &(ubpb.bs));
1878 #endif
1879 	}
1880 	if (wbpb->bpb.bytes_sector != BPSEC) {
1881 		(void) fprintf(stderr,
1882 		    gettext("Bogus bytes per sector value.\n"));
1883 		if (!powerofx_le_y(2, BPSEC * 8, wbpb->bpb.bytes_sector)) {
1884 			(void) fprintf(stderr,
1885 			    gettext("The device name may be missing a "
1886 			    "logical drive specifier.\n"));
1887 			(void) close(fd);
1888 			exit(6);
1889 		} else {
1890 			(void) fprintf(stderr,
1891 			    gettext("Do not know how to build FATs with a\n"
1892 			    "non-standard sector size. Standard "
1893 			    "size is %d bytes,\nyour sector size "
1894 			    "is %d bytes.\n"), BPSEC,
1895 			    wbpb->bpb.bytes_sector);
1896 			(void) close(fd);
1897 			exit(6);
1898 		}
1899 	}
1900 	if (!(powerofx_le_y(2, 128, wbpb->bpb.sectors_per_cluster))) {
1901 		(void) fprintf(stderr,
1902 		    gettext("Bogus sectors per cluster value.\n"));
1903 		(void) fprintf(stderr,
1904 		    gettext("The device name may be missing a "
1905 		    "logical drive specifier.\n"));
1906 		(void) close(fd);
1907 		exit(6);
1908 	}
1909 
1910 	if (wbpb->bpb.sectors_per_fat == 0) {
1911 #ifdef i386
1912 		(void) memcpy(&(wbpb->bpb32), &(ubpb.bs32.bs_bpb32),
1913 		    sizeof (wbpb->bpb32));
1914 #else
1915 		swap_pack_grab32bpb(wbpb, &(ubpb.bs));
1916 #endif
1917 		compute_file_area_size(wbpb);
1918 		if ((wbpb->bpb32.big_sectors_per_fat * BPSEC / 4) >=
1919 		    TotalClusters) {
1920 			MakeFAT32 = 1;
1921 		} else {
1922 			dashm_bail(fd);
1923 		}
1924 	} else {
1925 		compute_file_area_size(wbpb);
1926 	}
1927 }
1928 
1929 /*
1930  *  compare_existing_with_computed
1931  *
1932  *	We use this function when we the user specifies the -m option.
1933  *	We compute and look up things like we would if they had asked
1934  *	us to make the fs, and compare that to what's already layed down
1935  *	in the existing fs.  If there's a difference we can tell them what
1936  *	options to specify in order to reproduce their existing layout.
1937  *	Note that they still may not get an exact duplicate, because we
1938  *	don't, for example, preserve their existing boot code.  We think
1939  *	we've got all the fields that matter covered, though.
1940  *
1941  *	XXX - We're basically ignoring sbpb at this point.  I'm unsure
1942  *	if we'll ever care about those fields, in terms of the -m option.
1943  */
1944 static
1945 void
1946 compare_existing_with_computed(int fd, char *suffix,
1947     bpb_t *wbpb, int *prtsize, int *prtspc, int *prtbpf, int *prtnsect,
1948     int *prtntrk, int *prtfdisk, int *prthidden, int *prtrsrvd, int *dashos)
1949 {
1950 	struct dk_geom	dginfo;
1951 	struct fd_char	fdchar;
1952 	bpb_t		compare;
1953 	int		fd_ioctl_worked = 0;
1954 	int		fatents;
1955 
1956 	/*
1957 	 *  For all non-floppy cases we expect to find a 16-bit FAT
1958 	 */
1959 	int expectfatsize = 16;
1960 
1961 	compare = *wbpb;
1962 
1963 	if (!suffix) {
1964 		if (ioctl(fd, FDIOGCHAR, &fdchar) != -1) {
1965 			expectfatsize = 12;
1966 			fd_ioctl_worked++;
1967 		}
1968 	}
1969 
1970 	if (fd_ioctl_worked) {
1971 #ifdef sparc
1972 		fdchar.fdc_medium = 3;
1973 #endif
1974 		GetSize = GetSPT = GetSPC = GetTPC = GetBPF = 1;
1975 		lookup_floppy(&fdchar, &compare);
1976 		if (compare.bpb.heads != wbpb->bpb.heads) {
1977 			(*prtntrk)++;
1978 			(*dashos)++;
1979 		}
1980 		if (compare.bpb.sectors_per_track !=
1981 		    wbpb->bpb.sectors_per_track) {
1982 			(*prtnsect)++;
1983 			(*dashos)++;
1984 		}
1985 	} else {
1986 		int dk_ioctl_worked = 1;
1987 
1988 		if (!suffix) {
1989 			(*prtfdisk)++;
1990 			(*prtsize)++;
1991 			*dashos += 2;
1992 		}
1993 		if (ioctl(fd, DKIOCG_VIRTGEOM, &dginfo) == -1 &&
1994 		    ioctl(fd, DKIOCG_PHYGEOM, &dginfo) == -1 &&
1995 		    ioctl(fd, DKIOCGGEOM, &dginfo) == -1) {
1996 			*prtnsect = *prtntrk = 1;
1997 			*dashos += 2;
1998 			dk_ioctl_worked = 0;
1999 		}
2000 		if (dk_ioctl_worked) {
2001 			if (dginfo.dkg_nhead != wbpb->bpb.heads) {
2002 				(*prtntrk)++;
2003 				(*dashos)++;
2004 			}
2005 			if (dginfo.dkg_nsect !=
2006 			    wbpb->bpb.sectors_per_track) {
2007 				(*prtnsect)++;
2008 				(*dashos)++;
2009 			}
2010 		}
2011 		GetBPF = GetSPC = 1;
2012 		compute_cluster_size(&compare);
2013 	}
2014 
2015 	if (!*prtfdisk && TotSize != wbpb->bpb.sectors_in_volume &&
2016 	    TotSize != wbpb->bpb.sectors_in_logical_volume) {
2017 		(*dashos)++;
2018 		(*prtsize)++;
2019 	}
2020 
2021 	if (compare.bpb.sectors_per_cluster != wbpb->bpb.sectors_per_cluster) {
2022 		(*dashos)++;
2023 		(*prtspc)++;
2024 	}
2025 
2026 	if (compare.bpb.hidden_sectors != wbpb->bpb.hidden_sectors) {
2027 		(*dashos)++;
2028 		(*prthidden)++;
2029 	}
2030 
2031 	if (compare.bpb.resv_sectors != wbpb->bpb.resv_sectors) {
2032 		(*dashos)++;
2033 		(*prtrsrvd)++;
2034 	}
2035 
2036 	/*
2037 	 * Compute approximate Fatentsize.  It's approximate because the
2038 	 * size of the FAT may not be exactly a multiple of the number of
2039 	 * clusters.  It should be close, though.
2040 	 */
2041 	if (MakeFAT32) {
2042 		Fatentsize = 32;
2043 		(*dashos)++;
2044 		(*prtbpf)++;
2045 	} else {
2046 		fatents = wbpb->bpb.sectors_per_fat * BPSEC * 2 / 3;
2047 		if (fatents >= TotalClusters && wbpb->ebpb.type[4] == '2')
2048 			Fatentsize = 12;
2049 		else
2050 			Fatentsize = 16;
2051 		if (Fatentsize != expectfatsize) {
2052 			(*dashos)++;
2053 			(*prtbpf)++;
2054 		}
2055 	}
2056 }
2057 
2058 static
2059 void
2060 print_reproducing_command(int fd, char *actualdisk, char *suffix, bpb_t *wbpb)
2061 {
2062 	int needcomma = 0;
2063 	int prthidden = 0;
2064 	int prtrsrvd = 0;
2065 	int prtfdisk = 0;
2066 	int prtnsect = 0;
2067 	int prtntrk = 0;
2068 	int prtsize = 0;
2069 	int prtbpf = 0;
2070 	int prtspc = 0;
2071 	int dashos = 0;
2072 	int ll, i;
2073 
2074 	compare_existing_with_computed(fd, suffix, wbpb,
2075 	    &prtsize, &prtspc, &prtbpf, &prtnsect, &prtntrk,
2076 	    &prtfdisk, &prthidden, &prtrsrvd, &dashos);
2077 
2078 	/*
2079 	 *  Print out the command line they can use to reproduce the
2080 	 *  file system.
2081 	 */
2082 	(void) printf("mkfs -F pcfs");
2083 
2084 	ll = min(11, (int)strlen((char *)wbpb->ebpb.volume_label));
2085 	/*
2086 	 * First, eliminate trailing spaces. Now compare the name against
2087 	 * our default label.  If there's a match we don't need to print
2088 	 * any label info.
2089 	 */
2090 	i = ll;
2091 	while (wbpb->ebpb.volume_label[--i] == ' ')
2092 		;
2093 	ll = i;
2094 
2095 	if (ll == strlen(DEFAULT_LABEL) - 1) {
2096 		char cmpbuf[11];
2097 
2098 		(void) strcpy(cmpbuf, DEFAULT_LABEL);
2099 		for (i = ll; i >= 0; i--) {
2100 			if (cmpbuf[i] !=
2101 			    toupper((int)(wbpb->ebpb.volume_label[i]))) {
2102 				break;
2103 			}
2104 		}
2105 		if (i < 0)
2106 			ll = i;
2107 	}
2108 
2109 	if (ll >= 0) {
2110 		(void) printf(" -o ");
2111 		(void) printf("b=\"");
2112 		for (i = 0; i <= ll; i++) {
2113 			(void) printf("%c", wbpb->ebpb.volume_label[i]);
2114 		}
2115 		(void) printf("\"");
2116 		needcomma++;
2117 	} else if (dashos) {
2118 		(void) printf(" -o ");
2119 	}
2120 
2121 #define	NEXT_DASH_O	dashos--; needcomma++; continue
2122 
2123 	while (dashos) {
2124 		if (needcomma) {
2125 			(void) printf(",");
2126 			needcomma = 0;
2127 		}
2128 		if (prtfdisk) {
2129 			(void) printf("nofdisk");
2130 			prtfdisk--;
2131 			NEXT_DASH_O;
2132 		}
2133 		if (prtsize) {
2134 			(void) printf("size=%u", wbpb->bpb.sectors_in_volume ?
2135 			    wbpb->bpb.sectors_in_volume :
2136 			    wbpb->bpb.sectors_in_logical_volume);
2137 			prtsize--;
2138 			NEXT_DASH_O;
2139 		}
2140 		if (prtnsect) {
2141 			(void) printf("nsect=%d", wbpb->bpb.sectors_per_track);
2142 			prtnsect--;
2143 			NEXT_DASH_O;
2144 		}
2145 		if (prtspc) {
2146 			(void) printf("spc=%d", wbpb->bpb.sectors_per_cluster);
2147 			prtspc--;
2148 			NEXT_DASH_O;
2149 		}
2150 		if (prtntrk) {
2151 			(void) printf("ntrack=%d", wbpb->bpb.heads);
2152 			prtntrk--;
2153 			NEXT_DASH_O;
2154 		}
2155 		if (prtbpf) {
2156 			(void) printf("fat=%d", Fatentsize);
2157 			prtbpf--;
2158 			NEXT_DASH_O;
2159 		}
2160 		if (prthidden) {
2161 			(void) printf("hidden=%u", wbpb->bpb.hidden_sectors);
2162 			prthidden--;
2163 			NEXT_DASH_O;
2164 		}
2165 		if (prtrsrvd) {
2166 			(void) printf("reserve=%d", wbpb->bpb.resv_sectors);
2167 			prtrsrvd--;
2168 			NEXT_DASH_O;
2169 		}
2170 	}
2171 
2172 	(void) printf(" %s%c%c\n", actualdisk,
2173 	    suffix ? ':' : '\0', suffix ? *suffix : '\0');
2174 }
2175 
2176 /*
2177  *  open_and_examine
2178  *
2179  *	Open the requested 'dev_name'.  Seek to point where
2180  *	we'd expect to find boot sectors, etc., based on any ':partition'
2181  *	attachments to the dev_name.
2182  *
2183  *	Examine the fields of any existing boot sector and display best
2184  *	approximation of how this fs could be reproduced with this command.
2185  */
2186 static
2187 int
2188 open_and_examine(char *dn, bpb_t *wbpb)
2189 {
2190 	struct stat di;
2191 	off64_t ignored;
2192 	char *actualdisk = NULL;
2193 	char *suffix = NULL;
2194 	int fd;
2195 
2196 	if (Verbose)
2197 		(void) printf(gettext("Opening destination device/file.\n"));
2198 
2199 	actualdisk = stat_actual_disk(dn, &di, &suffix);
2200 
2201 	/*
2202 	 *  Destination exists, now find more about it.
2203 	 */
2204 	if (!(S_ISCHR(di.st_mode))) {
2205 		(void) fprintf(stderr,
2206 		    gettext("\n%s: device name must be a "
2207 		    "character special device.\n"), actualdisk);
2208 		exit(2);
2209 	} else if ((fd = open(actualdisk, O_RDWR)) < 0) {
2210 		perror(actualdisk);
2211 		exit(2);
2212 	}
2213 
2214 	/*
2215 	 * Find appropriate partition if we were requested to do so.
2216 	 */
2217 	if (suffix && !(seek_partn(fd, suffix, wbpb, &ignored))) {
2218 		(void) close(fd);
2219 		exit(2);
2220 	}
2221 
2222 	read_existing_bpb(fd, wbpb);
2223 	print_reproducing_command(fd, actualdisk, suffix, wbpb);
2224 
2225 	return (fd);
2226 }
2227 
2228 /*
2229  *  open_and_seek
2230  *
2231  *	Open the requested 'dev_name'.  Seek to point where
2232  *	we'll write boot sectors, etc., based on any ':partition'
2233  *	attachments to the dev_name.
2234  *
2235  *	By the time we are finished here, the entire BPB will be
2236  *	filled in, excepting the volume label.
2237  */
2238 static
2239 int
2240 open_and_seek(char *dn, bpb_t *wbpb, off64_t *seekto)
2241 {
2242 	struct fd_char fdchar;
2243 	struct dk_geom dg;
2244 	struct stat di;
2245 	char *actualdisk = NULL;
2246 	char *suffix = NULL;
2247 	int fd;
2248 
2249 	if (Verbose)
2250 		(void) printf(gettext("Opening destination device/file.\n"));
2251 
2252 	/*
2253 	 * We hold these truths to be self evident, all BPBs we create
2254 	 * will have these values in these fields.
2255 	 */
2256 	wbpb->bpb.num_fats = 2;
2257 	wbpb->bpb.bytes_sector = BPSEC;
2258 
2259 	/*
2260 	 * Assign or use supplied numbers for hidden and
2261 	 * reserved sectors in the file system.
2262 	 */
2263 	if (GetResrvd)
2264 		if (MakeFAT32)
2265 			wbpb->bpb.resv_sectors = 32;
2266 		else
2267 			wbpb->bpb.resv_sectors = 1;
2268 	else
2269 		wbpb->bpb.resv_sectors = Resrvd;
2270 
2271 	wbpb->ebpb.ext_signature = 0x29; /* Magic number for modern format */
2272 	wbpb->ebpb.volume_id = 0;
2273 
2274 	if (MakeFAT32)
2275 		fill_fat32_bpb(wbpb);
2276 
2277 	/*
2278 	 * If all output goes to a simple file, call a routine to setup
2279 	 * that scenario. Otherwise, try to find the device.
2280 	 */
2281 	if (Outputtofile)
2282 		return (fd = prepare_image_file(dn, wbpb));
2283 
2284 	actualdisk = stat_actual_disk(dn, &di, &suffix);
2285 
2286 	/*
2287 	 * Sanity check.  If we've been provided a partition-specifying
2288 	 * suffix, we shouldn't also have been told to ignore the
2289 	 * fdisk table.
2290 	 */
2291 	if (DontUseFdisk && suffix) {
2292 		(void) fprintf(stderr,
2293 		    gettext("Using 'nofdisk' option precludes "
2294 		    "appending logical drive\nspecifier "
2295 		    "to the device name.\n"));
2296 		exit(2);
2297 	}
2298 
2299 	/*
2300 	 *  Destination exists, now find more about it.
2301 	 */
2302 	if (!(S_ISCHR(di.st_mode))) {
2303 		(void) fprintf(stderr,
2304 		    gettext("\n%s: device name must indicate a "
2305 		    "character special device.\n"), actualdisk);
2306 		exit(2);
2307 	} else if ((fd = open(actualdisk, O_RDWR)) < 0) {
2308 		perror(actualdisk);
2309 		exit(2);
2310 	}
2311 
2312 	/*
2313 	 * Find appropriate partition if we were requested to do so.
2314 	 */
2315 	if (suffix && !(seek_partn(fd, suffix, wbpb, seekto))) {
2316 		(void) close(fd);
2317 		exit(2);
2318 	}
2319 
2320 	if (!suffix) {
2321 		/*
2322 		 * We have one of two possibilities.  Chances are we have
2323 		 * a floppy drive.  But the user may be trying to format
2324 		 * some weird drive that we don't know about and is supplying
2325 		 * all the important values.  In that case, they should have set
2326 		 * the 'nofdisk' flag.
2327 		 *
2328 		 * If 'nofdisk' isn't set, do a floppy-specific ioctl to
2329 		 * get the remainder of our info. If the ioctl fails, we have
2330 		 * a good idea that they aren't really on a floppy.  In that
2331 		 * case, they should have given us a partition specifier.
2332 		 */
2333 		if (DontUseFdisk) {
2334 			if (!(seek_nofdisk(fd, wbpb, seekto))) {
2335 				(void) close(fd);
2336 				exit(2);
2337 			}
2338 			find_fixed_details(fd, wbpb);
2339 		} else if (ioctl(fd, FDIOGCHAR, &fdchar) == -1) {
2340 			/*
2341 			 * It is possible that we are trying to use floppy
2342 			 * specific FDIOGCHAR ioctl on USB floppy. Since sd
2343 			 * driver, by which USB floppy is handled, doesn't
2344 			 * support it, we can try to use disk DKIOCGGEOM ioctl
2345 			 * to retrieve data we need. sd driver itself
2346 			 * determines floppy disk by number of blocks
2347 			 * (<=0x1000), then it sets geometry to 80 cylinders,
2348 			 * 2 heads.
2349 			 *
2350 			 * Note that DKIOCGGEOM cannot supply us with type
2351 			 * of media (e.g. 3.5" or 5.25"). We will set it to
2352 			 * 3 (3.5") which is most probable value.
2353 			 */
2354 			if (errno == ENOTTY) {
2355 				if (ioctl(fd, DKIOCGGEOM, &dg) != -1 &&
2356 				    dg.dkg_ncyl == 80 && dg.dkg_nhead == 2) {
2357 					fdchar.fdc_ncyl = dg.dkg_ncyl;
2358 					fdchar.fdc_medium = 3;
2359 					fdchar.fdc_secptrack = dg.dkg_nsect;
2360 					fdchar.fdc_nhead = dg.dkg_nhead;
2361 					lookup_floppy(&fdchar, wbpb);
2362 				} else {
2363 					partn_lecture(actualdisk);
2364 					(void) close(fd);
2365 					exit(2);
2366 				}
2367 			}
2368 		} else {
2369 #ifdef sparc
2370 			fdchar.fdc_medium = 3;
2371 #endif
2372 			lookup_floppy(&fdchar, wbpb);
2373 		}
2374 	} else {
2375 		find_fixed_details(fd, wbpb);
2376 	}
2377 
2378 	return (fd);
2379 }
2380 
2381 /*
2382  * The following is a copy of MS-DOS 4.0 boot block.
2383  * It consists of the BIOS parameter block, and a disk
2384  * bootstrap program.
2385  *
2386  * The BIOS parameter block contains the right values
2387  * for the 3.5" high-density 1.44MB floppy format.
2388  *
2389  * This will be our default boot sector, if the user
2390  * didn't point us at a different one.
2391  *
2392  */
2393 static
2394 uchar_t DefBootSec[512] = {
2395 	0xeb, 0x3c, 0x90, 	/* 8086 short jump + displacement + NOP */
2396 	'M', 'S', 'D', 'O', 'S', '4', '.', '0',	/* OEM name & version */
2397 	0x00, 0x02, 0x01, 0x01, 0x00,
2398 	0x02, 0xe0, 0x00, 0x40, 0x0b,
2399 	0xf0, 0x09, 0x00, 0x12, 0x00,
2400 	0x02, 0x00,
2401 	0x00, 0x00, 0x00, 0x00,
2402 	0x00, 0x00, 0x00, 0x00,
2403 	0x00, 0x00,
2404 	0x29, 0x00, 0x00, 0x00, 0x00,
2405 	'N', 'O', 'N', 'A', 'M', 'E', ' ', ' ', ' ', ' ', ' ',
2406 	'F', 'A', 'T', '1', '2', ' ', ' ', ' ',
2407 	0xfa, 0x33,
2408 	0xc0, 0x8e, 0xd0, 0xbc, 0x00, 0x7c, 0x16, 0x07,
2409 	0xbb, 0x78, 0x00, 0x36, 0xc5, 0x37, 0x1e, 0x56,
2410 	0x16, 0x53, 0xbf, 0x3e, 0x7c, 0xb9, 0x0b, 0x00,
2411 	0xfc, 0xf3, 0xa4, 0x06, 0x1f, 0xc6, 0x45, 0xfe,
2412 	0x0f, 0x8b, 0x0e, 0x18, 0x7c, 0x88, 0x4d, 0xf9,
2413 	0x89, 0x47, 0x02, 0xc7, 0x07, 0x3e, 0x7c, 0xfb,
2414 	0xcd, 0x13, 0x72, 0x7c, 0x33, 0xc0, 0x39, 0x06,
2415 	0x13, 0x7c, 0x74, 0x08, 0x8b, 0x0e, 0x13, 0x7c,
2416 	0x89, 0x0e, 0x20, 0x7c, 0xa0, 0x10, 0x7c, 0xf7,
2417 	0x26, 0x16, 0x7c, 0x03, 0x06, 0x1c, 0x7c, 0x13,
2418 	0x16, 0x1e, 0x7c, 0x03, 0x06, 0x0e, 0x7c, 0x83,
2419 	0xd2, 0x00, 0xa3, 0x50, 0x7c, 0x89, 0x16, 0x52,
2420 	0x7c, 0xa3, 0x49, 0x7c, 0x89, 0x16, 0x4b, 0x7c,
2421 	0xb8, 0x20, 0x00, 0xf7, 0x26, 0x11, 0x7c, 0x8b,
2422 	0x1e, 0x0b, 0x7c, 0x03, 0xc3, 0x48, 0xf7, 0xf3,
2423 	0x01, 0x06, 0x49, 0x7c, 0x83, 0x16, 0x4b, 0x7c,
2424 	0x00, 0xbb, 0x00, 0x05, 0x8b, 0x16, 0x52, 0x7c,
2425 	0xa1, 0x50, 0x7c, 0xe8, 0x87, 0x00, 0x72, 0x20,
2426 	0xb0, 0x01, 0xe8, 0xa1, 0x00, 0x72, 0x19, 0x8b,
2427 	0xfb, 0xb9, 0x0b, 0x00, 0xbe, 0xdb, 0x7d, 0xf3,
2428 	0xa6, 0x75, 0x0d, 0x8d, 0x7f, 0x20, 0xbe, 0xe6,
2429 	0x7d, 0xb9, 0x0b, 0x00, 0xf3, 0xa6, 0x74, 0x18,
2430 	0xbe, 0x93, 0x7d, 0xe8, 0x51, 0x00, 0x32, 0xe4,
2431 	0xcd, 0x16, 0x5e, 0x1f, 0x8f, 0x04, 0x8f, 0x44,
2432 	0x02, 0xcd, 0x19, 0x58, 0x58, 0x58, 0xeb, 0xe8,
2433 	0xbb, 0x00, 0x07, 0xb9, 0x03, 0x00, 0xa1, 0x49,
2434 	0x7c, 0x8b, 0x16, 0x4b, 0x7c, 0x50, 0x52, 0x51,
2435 	0xe8, 0x3a, 0x00, 0x72, 0xe6, 0xb0, 0x01, 0xe8,
2436 	0x54, 0x00, 0x59, 0x5a, 0x58, 0x72, 0xc9, 0x05,
2437 	0x01, 0x00, 0x83, 0xd2, 0x00, 0x03, 0x1e, 0x0b,
2438 	0x7c, 0xe2, 0xe2, 0x8a, 0x2e, 0x15, 0x7c, 0x8a,
2439 	0x16, 0x24, 0x7c, 0x8b, 0x1e, 0x49, 0x7c, 0xa1,
2440 	0x4b, 0x7c, 0xea, 0x00, 0x00, 0x70, 0x00, 0xac,
2441 	0x0a, 0xc0, 0x74, 0x29, 0xb4, 0x0e, 0xbb, 0x07,
2442 	0x00, 0xcd, 0x10, 0xeb, 0xf2, 0x3b, 0x16, 0x18,
2443 	0x7c, 0x73, 0x19, 0xf7, 0x36, 0x18, 0x7c, 0xfe,
2444 	0xc2, 0x88, 0x16, 0x4f, 0x7c, 0x33, 0xd2, 0xf7,
2445 	0x36, 0x1a, 0x7c, 0x88, 0x16, 0x25, 0x7c, 0xa3,
2446 	0x4d, 0x7c, 0xf8, 0xc3, 0xf9, 0xc3, 0xb4, 0x02,
2447 	0x8b, 0x16, 0x4d, 0x7c, 0xb1, 0x06, 0xd2, 0xe6,
2448 	0x0a, 0x36, 0x4f, 0x7c, 0x8b, 0xca, 0x86, 0xe9,
2449 	0x8a, 0x16, 0x24, 0x7c, 0x8a, 0x36, 0x25, 0x7c,
2450 	0xcd, 0x13, 0xc3, 0x0d, 0x0a, 0x4e, 0x6f, 0x6e,
2451 	0x2d, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20,
2452 	0x64, 0x69, 0x73, 0x6b, 0x20, 0x6f, 0x72, 0x20,
2453 	0x64, 0x69, 0x73, 0x6b, 0x20, 0x65, 0x72, 0x72,
2454 	0x6f, 0x72, 0x0d, 0x0a, 0x52, 0x65, 0x70, 0x6c,
2455 	0x61, 0x63, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20,
2456 	0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e,
2457 	0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x77, 0x68,
2458 	0x65, 0x6e, 0x20, 0x72, 0x65, 0x61, 0x64, 0x79,
2459 	0x0d, 0x0a, 0x00, 0x49, 0x4f, 0x20, 0x20, 0x20,
2460 	0x20, 0x20, 0x20, 0x53, 0x59, 0x53, 0x4d, 0x53,
2461 	0x44, 0x4f, 0x53, 0x20, 0x20, 0x20, 0x53, 0x59,
2462 	0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2463 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
2464 };
2465 
2466 /*
2467  *  verify_bootblkfile
2468  *
2469  *	We were provided with the name of a file containing the bootblk
2470  *	to install.  Verify it has a valid boot sector as best we can. Any
2471  *	errors and we return a bad file descriptor.  Otherwise we fill up the
2472  *	provided buffer with the boot sector, return the file
2473  *	descriptor for later use and leave the file pointer just
2474  *	past the boot sector part of the boot block file.
2475  */
2476 static
2477 int
2478 verify_bootblkfile(char *fn, boot_sector_t *bs, ulong_t *blkfilesize)
2479 {
2480 	struct stat fi;
2481 	int bsfd = -1;
2482 
2483 	if (stat(fn, &fi)) {
2484 		perror(fn);
2485 	} else if (fi.st_size < BPSEC) {
2486 		(void) fprintf(stderr,
2487 		    gettext("%s: Too short to be a boot sector.\n"), fn);
2488 	} else if ((bsfd = open(fn, O_RDONLY)) < 0) {
2489 		perror(fn);
2490 	} else if (read(bsfd, bs->buf, BPSEC) < BPSEC) {
2491 		(void) close(bsfd);
2492 		bsfd = -1;
2493 		perror(gettext("Boot block read"));
2494 	} else {
2495 		if ((bs->bs.bs_signature[0] != (BOOTSECSIG & 0xFF) &&
2496 		    bs->bs.bs_signature[1] != ((BOOTSECSIG >> 8) & 0xFF)) ||
2497 #ifdef i386
2498 		    (bs->bs.bs_front.bs_jump_code[0] != OPCODE1 &&
2499 		    bs->bs.bs_front.bs_jump_code[0] != OPCODE2)
2500 #else
2501 		    (bs->bs.bs_jump_code[0] != OPCODE1 &&
2502 		    bs->bs.bs_jump_code[0] != OPCODE2)
2503 #endif
2504 		    /* CSTYLED */
2505 		    ) {
2506 			(void) close(bsfd);
2507 			bsfd = -1;
2508 			(void) fprintf(stderr,
2509 			    gettext("Boot block (%s) bogus.\n"), fn);
2510 		}
2511 		*blkfilesize = fi.st_size;
2512 	}
2513 	return (bsfd);
2514 }
2515 
2516 /*
2517  *  verify_firstfile
2518  *
2519  *	We were provided with the name of a file to be the first file
2520  *	installed on the disk.  We just need to verify it exists and
2521  *	find out how big it is.  If it doesn't exist, we print a warning
2522  *	message about how the file wasn't found.  We don't exit fatally,
2523  *	though, rather we return a size of 0 and the FAT will be built
2524  *	without installing any first file.  They can then presumably
2525  *	install the correct first file by hand.
2526  */
2527 static
2528 int
2529 verify_firstfile(char *fn, ulong_t *filesize)
2530 {
2531 	struct stat fi;
2532 	int fd = -1;
2533 
2534 	*filesize = 0;
2535 	if (stat(fn, &fi) || (fd = open(fn, O_RDONLY)) < 0) {
2536 		perror(fn);
2537 		(void) fprintf(stderr,
2538 		    gettext("Could not access requested file.  It will not\n"
2539 		    "be installed in the new file system.\n"));
2540 	} else {
2541 		*filesize = fi.st_size;
2542 	}
2543 
2544 	return (fd);
2545 }
2546 
2547 /*
2548  *  label_volume
2549  *
2550  *	Fill in BPB with volume label.
2551  */
2552 static
2553 void
2554 label_volume(char *lbl, bpb_t *wbpb)
2555 {
2556 	int ll, i;
2557 
2558 	/* Put a volume label into our BPB. */
2559 	if (!lbl)
2560 		lbl = DEFAULT_LABEL;
2561 
2562 	ll = min(11, (int)strlen(lbl));
2563 	for (i = 0; i < ll; i++) {
2564 		wbpb->ebpb.volume_label[i] = toupper(lbl[i]);
2565 	}
2566 	for (; i < 11; i++) {
2567 		wbpb->ebpb.volume_label[i] = ' ';
2568 	}
2569 }
2570 
2571 static
2572 int
2573 copy_bootblk(char *fn, boot_sector_t *bootsect, ulong_t *bootblksize)
2574 {
2575 	int bsfd = -1;
2576 
2577 	if (Verbose && fn)
2578 		(void) printf(gettext("Request to install boot "
2579 		    "block file %s.\n"), fn);
2580 	else if (Verbose)
2581 		(void) printf(gettext("Request to install DOS boot block.\n"));
2582 
2583 	/*
2584 	 *  If they want to install their own boot block, sanity check
2585 	 *  that block.
2586 	 */
2587 	if (fn) {
2588 		bsfd = verify_bootblkfile(fn, bootsect, bootblksize);
2589 		if (bsfd < 0) {
2590 			exit(3);
2591 		}
2592 		*bootblksize = roundup(*bootblksize, BPSEC);
2593 	} else {
2594 		(void) memcpy(bootsect, DefBootSec, BPSEC);
2595 		*bootblksize = BPSEC;
2596 	}
2597 
2598 	return (bsfd);
2599 }
2600 
2601 /*
2602  *  mark_cluster
2603  *
2604  *	This routine fills a FAT entry with the value supplied to it as an
2605  *	argument.  The fatp argument is assumed to be a pointer to the FAT's
2606  *	0th entry.  The clustnum is the cluster entry that should be updated.
2607  *	The value is the new value for the entry.
2608  */
2609 static
2610 void
2611 mark_cluster(uchar_t *fatp, pc_cluster32_t clustnum, uint32_t value)
2612 {
2613 	uchar_t *ep;
2614 	ulong_t idx;
2615 
2616 	idx = (Fatentsize == 32) ? clustnum * 4 :
2617 	    (Fatentsize == 16) ? clustnum * 2 : clustnum + clustnum/2;
2618 	ep = fatp + idx;
2619 
2620 	if (Fatentsize == 32) {
2621 		store_32_bits(&ep, value);
2622 	} else if (Fatentsize == 16) {
2623 		store_16_bits(&ep, value);
2624 	} else {
2625 		if (clustnum & 1) {
2626 			*ep = (*ep & 0x0f) | ((value << 4) & 0xf0);
2627 			ep++;
2628 			*ep = (value >> 4) & 0xff;
2629 		} else {
2630 			*ep++ = value & 0xff;
2631 			*ep = (*ep & 0xf0) | ((value >> 8) & 0x0f);
2632 		}
2633 	}
2634 }
2635 
2636 static
2637 uchar_t *
2638 build_fat(bpb_t *wbpb, struct fat_od_fsi *fsinfop, ulong_t bootblksize,
2639     ulong_t *fatsize, char *ffn, int *fffd, ulong_t *ffsize,
2640     pc_cluster32_t *ffstartclust)
2641 {
2642 	pc_cluster32_t nextfree, ci;
2643 	uchar_t *fatp;
2644 	ushort_t numclust, numsect;
2645 	int  remclust;
2646 
2647 	/* Alloc space for a FAT and then null it out. */
2648 	if (Verbose) {
2649 		(void) printf(gettext("BUILD FAT.\n%d sectors per fat.\n"),
2650 		    wbpb->bpb.sectors_per_fat ? wbpb->bpb.sectors_per_fat :
2651 		    wbpb->bpb32.big_sectors_per_fat);
2652 	}
2653 
2654 	if (MakeFAT32) {
2655 		*fatsize = BPSEC * wbpb->bpb32.big_sectors_per_fat;
2656 	} else {
2657 		*fatsize = BPSEC * wbpb->bpb.sectors_per_fat;
2658 	}
2659 
2660 	if (!(fatp = (uchar_t *)malloc(*fatsize))) {
2661 		perror(gettext("FAT table alloc"));
2662 		exit(4);
2663 	} else {
2664 		(void) memset(fatp, 0, *fatsize);
2665 	}
2666 
2667 	/* Build in-memory FAT */
2668 	*fatp = wbpb->bpb.media;
2669 	*(fatp + 1) = 0xFF;
2670 	*(fatp + 2) = 0xFF;
2671 
2672 	if (Fatentsize == 16) {
2673 		*(fatp + 3) = 0xFF;
2674 	} else if (Fatentsize == 32) {
2675 		*(fatp + 3) = 0x0F;
2676 		*(fatp + 4) = 0xFF;
2677 		*(fatp + 5) = 0xFF;
2678 		*(fatp + 6) = 0xFF;
2679 		*(fatp + 7) = 0x0F;
2680 	}
2681 
2682 	/*
2683 	 * Keep track of clusters used.
2684 	 */
2685 	remclust = TotalClusters;
2686 	nextfree = 2;
2687 
2688 	/*
2689 	 * Get info on first file to install, if any.
2690 	 */
2691 	if (ffn)
2692 		*fffd = verify_firstfile(ffn, ffsize);
2693 
2694 	/*
2695 	 * Compute number of clusters to preserve for bootblk overage.
2696 	 * Remember that we already wrote the first sector of the boot block.
2697 	 * These clusters are marked BAD to prevent them from being deleted
2698 	 * or used.  The first available cluster is 2, so we always offset
2699 	 * the clusters.
2700 	 */
2701 	numsect = idivceil((bootblksize - BPSEC), BPSEC);
2702 	numclust = idivceil(numsect, wbpb->bpb.sectors_per_cluster);
2703 
2704 	if (Verbose && numclust)
2705 		(void) printf(gettext("Hiding %d excess bootblk cluster(s).\n"),
2706 		    numclust);
2707 	for (ci = 0; ci < numclust; ci++)
2708 		mark_cluster(fatp, nextfree++,
2709 		    MakeFAT32 ? PCF_BADCLUSTER32 : PCF_BADCLUSTER);
2710 	remclust -= numclust;
2711 
2712 	/*
2713 	 * Reserve a cluster for the root directory on a FAT32.
2714 	 */
2715 	if (MakeFAT32) {
2716 		mark_cluster(fatp, nextfree, PCF_LASTCLUSTER32);
2717 		wbpb->bpb32.root_dir_clust = nextfree++;
2718 		remclust--;
2719 	}
2720 
2721 	/*
2722 	 * Compute and preserve number of clusters for first file.
2723 	 */
2724 	if (*fffd >= 0) {
2725 		*ffstartclust = nextfree;
2726 		numsect = idivceil(*ffsize, BPSEC);
2727 		numclust = idivceil(numsect, wbpb->bpb.sectors_per_cluster);
2728 
2729 		if (numclust > remclust) {
2730 			(void) fprintf(stderr,
2731 			    gettext("Requested first file too large to be\n"
2732 			    "installed in the new file system.\n"));
2733 			(void) close(*fffd);
2734 			*fffd = -1;
2735 			goto finish;
2736 		}
2737 
2738 		if (Verbose)
2739 			(void) printf(gettext("Reserving %d first file "
2740 			    "cluster(s).\n"), numclust);
2741 		for (ci = 0; (int)ci < (int)(numclust-1); ci++, nextfree++)
2742 			mark_cluster(fatp, nextfree, nextfree + 1);
2743 		mark_cluster(fatp, nextfree++,
2744 		    MakeFAT32 ? PCF_LASTCLUSTER32 : PCF_LASTCLUSTER);
2745 		remclust -= numclust;
2746 	}
2747 
2748 finish:
2749 	if (Verbose) {
2750 		(void) printf(gettext("First sector of FAT"));
2751 		header_for_dump();
2752 		dump_bytes(fatp, BPSEC);
2753 	}
2754 
2755 	(void) memset(fsinfop, 0, sizeof (*fsinfop));
2756 	fsinfop->fsi_leadsig = LE_32(FSI_LEADSIG);
2757 	fsinfop->fsi_strucsig = LE_32(FSI_STRUCSIG);
2758 	fsinfop->fsi_trailsig = LE_32(FSI_TRAILSIG);
2759 	fsinfop->fsi_incore.fs_free_clusters = LE_32(remclust);
2760 	fsinfop->fsi_incore.fs_next_free = LE_32(nextfree);
2761 	return (fatp);
2762 }
2763 
2764 static
2765 void
2766 dirent_time_fill(struct pcdir *dep)
2767 {
2768 	struct  timeval tv;
2769 	struct	tm	*tp;
2770 	ushort_t	dostime;
2771 	ushort_t	dosday;
2772 
2773 	(void) gettimeofday(&tv, (struct timezone *)0);
2774 	tp = localtime(&tv.tv_sec);
2775 	/* get the time & day into DOS format */
2776 	dostime = tp->tm_sec / 2;
2777 	dostime |= tp->tm_min << 5;
2778 	dostime |= tp->tm_hour << 11;
2779 	dosday = tp->tm_mday;
2780 	dosday |= (tp->tm_mon + 1) << 5;
2781 	dosday |= (tp->tm_year - 80) << 9;
2782 	dep->pcd_mtime.pct_time = htols(dostime);
2783 	dep->pcd_mtime.pct_date = htols(dosday);
2784 }
2785 
2786 static
2787 void
2788 dirent_label_fill(struct pcdir *dep, char *fn)
2789 {
2790 	int nl, i;
2791 
2792 	/*
2793 	 * We spread the volume label across both the NAME and EXT fields
2794 	 */
2795 	nl = min(PCFNAMESIZE, strlen(fn));
2796 	for (i = 0; i < nl; i++) {
2797 		dep->pcd_filename[i] = toupper(fn[i]);
2798 	}
2799 	if (i < PCFNAMESIZE) {
2800 		for (; i < PCFNAMESIZE; i++)
2801 			dep->pcd_filename[i] = ' ';
2802 		for (i = 0; i < PCFEXTSIZE; i++)
2803 			dep->pcd_ext[i] = ' ';
2804 		return;
2805 	}
2806 	nl = min(PCFEXTSIZE, strlen(fn) - PCFNAMESIZE);
2807 	for (i = 0; i < nl; i++)
2808 		dep->pcd_ext[i] = toupper(fn[i + PCFNAMESIZE]);
2809 	if (i < PCFEXTSIZE) {
2810 		for (; i < PCFEXTSIZE; i++)
2811 			dep->pcd_ext[i] = ' ';
2812 	}
2813 }
2814 
2815 static
2816 void
2817 dirent_fname_fill(struct pcdir *dep, char *fn)
2818 {
2819 	char *fname, *fext;
2820 	int nl, i;
2821 
2822 	if (fname = strrchr(fn, '/')) {
2823 		fname++;
2824 	} else {
2825 		fname = fn;
2826 	}
2827 
2828 	if (fext = strrchr(fname, '.')) {
2829 		fext++;
2830 	} else {
2831 		fext = "";
2832 	}
2833 
2834 	fname = strtok(fname, ".");
2835 
2836 	nl = min(PCFNAMESIZE, (int)strlen(fname));
2837 	for (i = 0; i < nl; i++) {
2838 		dep->pcd_filename[i] = toupper(fname[i]);
2839 	}
2840 	for (; i < PCFNAMESIZE; i++) {
2841 		dep->pcd_filename[i] = ' ';
2842 	}
2843 
2844 	nl = min(PCFEXTSIZE, (int)strlen(fext));
2845 	for (i = 0; i < nl; i++) {
2846 		dep->pcd_ext[i] = toupper(fext[i]);
2847 	}
2848 	for (; i < PCFEXTSIZE; i++) {
2849 		dep->pcd_ext[i] = ' ';
2850 	}
2851 }
2852 
2853 static
2854 uchar_t *
2855 build_rootdir(bpb_t *wbpb, char *ffn, int fffd,
2856     ulong_t ffsize, pc_cluster32_t ffstart, ulong_t *rdirsize)
2857 {
2858 	struct pcdir *rootdirp;
2859 	struct pcdir *entry;
2860 
2861 	/*
2862 	 * Build a root directory.  It will have at least one entry,
2863 	 * the volume label and a second if the first file was defined.
2864 	 */
2865 	if (MakeFAT32) {
2866 		/*
2867 		 * We devote an entire cluster to the root
2868 		 * directory on FAT32.
2869 		 */
2870 		*rdirsize = wbpb->bpb.sectors_per_cluster * BPSEC;
2871 	} else {
2872 		*rdirsize = wbpb->bpb.num_root_entries * sizeof (struct pcdir);
2873 	}
2874 	if ((rootdirp = (struct pcdir *)malloc(*rdirsize)) == NULL) {
2875 		perror(gettext("Root directory allocation"));
2876 		exit(4);
2877 	} else {
2878 		entry = rootdirp;
2879 		(void) memset((char *)rootdirp, 0, *rdirsize);
2880 	}
2881 
2882 	/* Create directory entry for first file, if there is one */
2883 	if (fffd >= 0) {
2884 		dirent_fname_fill(entry, ffn);
2885 		entry->pcd_attr = Firstfileattr;
2886 		dirent_time_fill(entry);
2887 		entry->pcd_scluster_lo = htols(ffstart);
2888 		if (MakeFAT32) {
2889 			ffstart = ffstart >> 16;
2890 			entry->un.pcd_scluster_hi = htols(ffstart);
2891 		}
2892 		entry->pcd_size = htoli(ffsize);
2893 		entry++;
2894 	}
2895 
2896 	/* Create directory entry for volume label, if there is one */
2897 	if (Label != NULL) {
2898 		dirent_label_fill(entry, Label);
2899 		entry->pcd_attr = PCA_ARCH | PCA_LABEL;
2900 		dirent_time_fill(entry);
2901 		entry->pcd_scluster_lo = 0;
2902 		if (MakeFAT32) {
2903 			entry->un.pcd_scluster_hi = 0;
2904 		}
2905 		entry->pcd_size = 0;
2906 		entry++;
2907 	}
2908 
2909 	if (Verbose) {
2910 		(void) printf(gettext("First two directory entries"));
2911 		header_for_dump();
2912 		dump_bytes((uchar_t *)rootdirp, 2 * sizeof (struct pcdir));
2913 	}
2914 
2915 	return ((uchar_t *)rootdirp);
2916 }
2917 
2918 /*
2919  * write_rest
2920  *
2921  *	Write all the bytes from the current file pointer to end of file
2922  *	in the source file out to the destination file.  The writes should
2923  *	be padded to whole clusters with 0's if necessary.
2924  */
2925 static
2926 void
2927 write_rest(bpb_t *wbpb, char *efn, int dfd, int sfd, int remaining)
2928 {
2929 	char buf[BPSEC];
2930 	ushort_t numsect, numclust;
2931 	ushort_t wnumsect, s;
2932 	int doneread = 0;
2933 	int rstat;
2934 
2935 	/*
2936 	 * Compute number of clusters required to contain remaining bytes.
2937 	 */
2938 	numsect = idivceil(remaining, BPSEC);
2939 	numclust = idivceil(numsect, wbpb->bpb.sectors_per_cluster);
2940 
2941 	wnumsect = numclust * wbpb->bpb.sectors_per_cluster;
2942 	for (s = 0; s < wnumsect; s++) {
2943 		if (!doneread) {
2944 			if ((rstat = read(sfd, buf, BPSEC)) < 0) {
2945 				perror(efn);
2946 				doneread = 1;
2947 				rstat = 0;
2948 			} else if (rstat == 0) {
2949 				doneread = 1;
2950 			}
2951 			(void) memset(&(buf[rstat]), 0, BPSEC - rstat);
2952 		}
2953 		if (write(dfd, buf, BPSEC) != BPSEC) {
2954 			(void) fprintf(stderr, gettext("Copying "));
2955 			perror(efn);
2956 		}
2957 	}
2958 }
2959 
2960 static
2961 void
2962 write_fat32_bootstuff(int fd, boot_sector_t *bsp,
2963 	struct fat_od_fsi *fsinfop, off64_t seekto)
2964 {
2965 	if (Verbose) {
2966 		(void) printf(gettext("Dump of the fs info sector"));
2967 		header_for_dump();
2968 		dump_bytes((uchar_t *)fsinfop, sizeof (*fsinfop));
2969 	}
2970 
2971 	if (!Notreally) {
2972 		/*
2973 		 * FAT32's have an FS info sector, then a backup of the boot
2974 		 * sector, and a modified backup of the FS Info sector.
2975 		 */
2976 		if (write(fd, fsinfop, sizeof (*fsinfop)) != BPSEC) {
2977 			perror(gettext("FS info sector write"));
2978 			exit(4);
2979 		}
2980 		if (lseek64(fd,	seekto + BKUP_BOOTSECT_OFFSET, SEEK_SET) < 0) {
2981 			(void) close(fd);
2982 			perror(gettext("Boot sector backup seek"));
2983 			exit(4);
2984 		}
2985 		if (write(fd, bsp->buf, sizeof (bsp->buf)) != BPSEC) {
2986 			perror(gettext("Boot sector backup write"));
2987 			exit(4);
2988 		}
2989 	}
2990 
2991 	/*
2992 	 * Second copy of fs info sector is modified to have "don't know"
2993 	 * as the number of free clusters
2994 	 */
2995 	fsinfop->fsi_incore.fs_next_free = LE_32(FSINFO_UNKNOWN);
2996 
2997 	if (Verbose) {
2998 		(void) printf(gettext("Dump of the backup fs info sector"));
2999 		header_for_dump();
3000 		dump_bytes((uchar_t *)fsinfop, sizeof (*fsinfop));
3001 	}
3002 
3003 	if (!Notreally) {
3004 		if (write(fd, fsinfop, sizeof (*fsinfop)) != BPSEC) {
3005 			perror(gettext("FS info sector backup write"));
3006 			exit(4);
3007 		}
3008 	}
3009 }
3010 
3011 static
3012 void
3013 write_bootsects(int fd, boot_sector_t *bsp, bpb_t *wbpb,
3014 	struct fat_od_fsi *fsinfop, off64_t seekto)
3015 {
3016 	if (MakeFAT32) {
3017 		/* Copy our BPB into bootsec structure */
3018 #ifdef i386
3019 		(void) memcpy(&(bsp->bs32.bs_front.bs_bpb), &(wbpb->bpb),
3020 		    sizeof (wbpb->bpb));
3021 		(void) memcpy(&(bsp->bs32.bs_bpb32), &(wbpb->bpb32),
3022 		    sizeof (wbpb->bpb32));
3023 		(void) memcpy(&(bsp->bs32.bs_ebpb), &(wbpb->ebpb),
3024 		    sizeof (wbpb->ebpb));
3025 #else
3026 		swap_pack_bpb32cpy(&(bsp->bs32), wbpb);
3027 #endif
3028 	} else {
3029 		/* Copy our BPB into bootsec structure */
3030 #ifdef i386
3031 		(void) memcpy(&(bsp->bs.bs_front.bs_bpb), &(wbpb->bpb),
3032 		    sizeof (wbpb->bpb));
3033 		(void) memcpy(&(bsp->bs.bs_ebpb), &(wbpb->ebpb),
3034 		    sizeof (wbpb->ebpb));
3035 #else
3036 		swap_pack_bpbcpy(&(bsp->bs), wbpb);
3037 #endif
3038 
3039 		/* Copy SUN BPB extensions into bootsec structure */
3040 		if (SunBPBfields) {
3041 #ifdef i386
3042 			(void) memcpy(&(bsp->bs.bs_sebpb), &(wbpb->sunbpb),
3043 			    sizeof (wbpb->sunbpb));
3044 #else
3045 			swap_pack_sebpbcpy(&(bsp->bs), wbpb);
3046 #endif
3047 		}
3048 	}
3049 
3050 	/* Write boot sector */
3051 	if (!Notreally && write(fd, bsp->buf, sizeof (bsp->buf)) != BPSEC) {
3052 		perror(gettext("Boot sector write"));
3053 		exit(4);
3054 	}
3055 
3056 	if (Verbose) {
3057 		(void) printf(gettext("Dump of the boot sector"));
3058 		header_for_dump();
3059 		dump_bytes(bsp->buf, sizeof (bsp->buf));
3060 	}
3061 
3062 	if (MakeFAT32)
3063 		write_fat32_bootstuff(fd, bsp, fsinfop, seekto);
3064 }
3065 
3066 static
3067 void
3068 write_fat(int fd, off64_t seekto, char *fn, char *lbl, char *ffn, bpb_t *wbpb)
3069 {
3070 	struct fat_od_fsi fsinfo;
3071 	pc_cluster32_t ffsc;
3072 	boot_sector_t bootsect;
3073 	uchar_t *fatp, *rdirp;
3074 	ulong_t bootblksize, fatsize, rdirsize, ffsize;
3075 	int bsfd = -1;
3076 	int fffd = -1;
3077 
3078 	compute_file_area_size(wbpb);
3079 
3080 	bsfd = copy_bootblk(fn, &bootsect, &bootblksize);
3081 	label_volume(lbl, wbpb);
3082 
3083 	if (Verbose)
3084 		(void) printf(gettext("Building FAT.\n"));
3085 	fatp = build_fat(wbpb, &fsinfo, bootblksize, &fatsize,
3086 	    ffn, &fffd, &ffsize, &ffsc);
3087 
3088 	write_bootsects(fd, &bootsect, wbpb, &fsinfo, seekto);
3089 
3090 	if (lseek64(fd,
3091 	    seekto + (BPSEC * wbpb->bpb.resv_sectors), SEEK_SET) < 0) {
3092 		(void) close(fd);
3093 		perror(gettext("Seek to end of reserved sectors"));
3094 		exit(4);
3095 	}
3096 
3097 	/* Write FAT */
3098 	if (Verbose)
3099 		(void) printf(gettext("Writing FAT(s). %d bytes times %d.\n"),
3100 		    fatsize, wbpb->bpb.num_fats);
3101 	if (!Notreally) {
3102 		int nf, wb;
3103 		for (nf = 0; nf < (int)wbpb->bpb.num_fats; nf++)
3104 			if ((wb = write(fd, fatp, fatsize)) != fatsize) {
3105 				perror(gettext("FAT write"));
3106 				exit(4);
3107 			} else {
3108 				if (Verbose)
3109 					(void) printf(
3110 					    gettext("Wrote %d bytes\n"), wb);
3111 			}
3112 	}
3113 	free(fatp);
3114 
3115 	if (Verbose)
3116 		(void) printf(gettext("Building root directory.\n"));
3117 	rdirp = build_rootdir(wbpb, ffn, fffd, ffsize, ffsc, &rdirsize);
3118 
3119 	/*
3120 	 *  In non FAT32, root directory exists outside of the file area
3121 	 */
3122 	if (!MakeFAT32) {
3123 		if (Verbose)
3124 			(void) printf(gettext("Writing root directory. "
3125 			    "%d bytes.\n"), rdirsize);
3126 		if (!Notreally) {
3127 			if (write(fd, rdirp, rdirsize) != rdirsize) {
3128 				perror(gettext("Root directory write"));
3129 				exit(4);
3130 			}
3131 		}
3132 		free(rdirp);
3133 	}
3134 
3135 	/*
3136 	 * Now write anything that needs to be in the file space.
3137 	 */
3138 	if (bootblksize > BPSEC) {
3139 		if (Verbose)
3140 			(void) printf(gettext("Writing remainder of "
3141 			    "boot block.\n"));
3142 		if (!Notreally)
3143 			write_rest(wbpb, fn, fd, bsfd, bootblksize - BPSEC);
3144 	}
3145 
3146 	if (MakeFAT32) {
3147 		if (Verbose)
3148 			(void) printf(gettext("Writing root directory. "
3149 			    "%d bytes.\n"), rdirsize);
3150 		if (!Notreally) {
3151 			if (write(fd, rdirp, rdirsize) != rdirsize) {
3152 				perror(gettext("Root directory write"));
3153 				exit(4);
3154 			}
3155 		}
3156 		free(rdirp);
3157 	}
3158 
3159 	if (fffd >= 0) {
3160 		if (Verbose)
3161 			(void) printf(gettext("Writing first file.\n"));
3162 		if (!Notreally)
3163 			write_rest(wbpb, ffn, fd, fffd, ffsize);
3164 	}
3165 }
3166 
3167 static
3168 char *LegalOpts[] = {
3169 #define	NFLAG 0
3170 	"N",
3171 #define	VFLAG 1
3172 	"v",
3173 #define	RFLAG 2
3174 	"r",
3175 #define	HFLAG 3
3176 	"h",
3177 #define	SFLAG 4
3178 	"s",
3179 #define	SUNFLAG 5
3180 	"S",
3181 #define	LABFLAG 6
3182 	"b",
3183 #define	BTRFLAG 7
3184 	"B",
3185 #define	INITFLAG 8
3186 	"i",
3187 #define	SZFLAG 9
3188 	"size",
3189 #define	SECTFLAG 10
3190 	"nsect",
3191 #define	TRKFLAG 11
3192 	"ntrack",
3193 #define	SPCFLAG 12
3194 	"spc",
3195 #define	BPFFLAG 13
3196 	"fat",
3197 #define	FFLAG 14
3198 	"f",
3199 #define	DFLAG 15
3200 	"d",
3201 #define	NOFDISKFLAG 16
3202 	"nofdisk",
3203 #define	RESRVFLAG 17
3204 	"reserve",
3205 #define	HIDDENFLAG 18
3206 	"hidden",
3207 	NULL
3208 };
3209 
3210 static
3211 void
3212 bad_arg(char *option)
3213 {
3214 	(void) fprintf(stderr,
3215 	    gettext("Unrecognized option %s.\n"), option);
3216 	usage();
3217 	exit(2);
3218 }
3219 
3220 static
3221 void
3222 missing_arg(char *option)
3223 {
3224 	(void) fprintf(stderr,
3225 	    gettext("Option %s requires a value.\n"), option);
3226 	usage();
3227 	exit(3);
3228 }
3229 
3230 static
3231 void
3232 parse_suboptions(char *optsstr)
3233 {
3234 	char *value;
3235 	int c;
3236 
3237 	while (*optsstr != '\0') {
3238 		switch (c = getsubopt(&optsstr, LegalOpts, &value)) {
3239 		case NFLAG:
3240 			Notreally++;
3241 			break;
3242 		case VFLAG:
3243 			Verbose++;
3244 			break;
3245 		case RFLAG:
3246 			Firstfileattr |= 0x01;
3247 			break;
3248 		case HFLAG:
3249 			Firstfileattr |= 0x02;
3250 			break;
3251 		case SFLAG:
3252 			Firstfileattr |= 0x04;
3253 			break;
3254 		case SUNFLAG:
3255 			SunBPBfields = 1;
3256 			break;
3257 		case LABFLAG:
3258 			if (value == NULL) {
3259 				missing_arg(LegalOpts[c]);
3260 			} else {
3261 				Label = value;
3262 			}
3263 			break;
3264 		case BTRFLAG:
3265 			if (value == NULL) {
3266 				missing_arg(LegalOpts[c]);
3267 			} else {
3268 				BootBlkFn = value;
3269 			}
3270 			break;
3271 		case INITFLAG:
3272 			if (value == NULL) {
3273 				missing_arg(LegalOpts[c]);
3274 			} else {
3275 				FirstFn = value;
3276 			}
3277 			break;
3278 		case SZFLAG:
3279 			if (value == NULL) {
3280 				missing_arg(LegalOpts[c]);
3281 			} else {
3282 				TotSize = atoi(value);
3283 				GetSize = 0;
3284 			}
3285 			break;
3286 		case SECTFLAG:
3287 			if (value == NULL) {
3288 				missing_arg(LegalOpts[c]);
3289 			} else {
3290 				SecPerTrk = atoi(value);
3291 				GetSPT = 0;
3292 			}
3293 			break;
3294 		case TRKFLAG:
3295 			if (value == NULL) {
3296 				missing_arg(LegalOpts[c]);
3297 			} else {
3298 				TrkPerCyl = atoi(value);
3299 				GetTPC = 0;
3300 			}
3301 			break;
3302 		case SPCFLAG:
3303 			if (value == NULL) {
3304 				missing_arg(LegalOpts[c]);
3305 			} else {
3306 				SecPerClust = atoi(value);
3307 				GetSPC = 0;
3308 			}
3309 			break;
3310 		case BPFFLAG:
3311 			if (value == NULL) {
3312 				missing_arg(LegalOpts[c]);
3313 			} else {
3314 				BitsPerFAT = atoi(value);
3315 				GetBPF = 0;
3316 			}
3317 			break;
3318 		case NOFDISKFLAG:
3319 			DontUseFdisk = 1;
3320 			break;
3321 		case RESRVFLAG:
3322 			if (value == NULL) {
3323 				missing_arg(LegalOpts[c]);
3324 			} else {
3325 				Resrvd = atoi(value);
3326 				GetResrvd = 0;
3327 			}
3328 			break;
3329 		case HIDDENFLAG:
3330 			if (value == NULL) {
3331 				missing_arg(LegalOpts[c]);
3332 			} else {
3333 				RelOffset = atoi(value);
3334 				GetOffset = 0;
3335 			}
3336 			break;
3337 		case FFLAG:
3338 			if (value == NULL) {
3339 				missing_arg(LegalOpts[c]);
3340 			} else {
3341 				DiskName = value;
3342 				Outputtofile = 1;
3343 			}
3344 			break;
3345 		case DFLAG:
3346 			if (value == NULL) {
3347 				missing_arg(LegalOpts[c]);
3348 			} else {
3349 				Imagesize = atoi(value);
3350 			}
3351 			break;
3352 		default:
3353 			bad_arg(value);
3354 			break;
3355 		}
3356 	}
3357 }
3358 
3359 static
3360 void
3361 sanity_check_options(int argc, int optind)
3362 {
3363 	if (GetFsParams) {
3364 		if (argc - optind != 1)
3365 			usage();
3366 		return;
3367 	}
3368 
3369 	if (DontUseFdisk && GetOffset) {
3370 		/* Set default relative offset of zero */
3371 		RelOffset = 0;
3372 	}
3373 
3374 	if (BitsPerFAT == 32)
3375 		MakeFAT32 = 1;
3376 
3377 	if (Outputtofile && (argc - optind)) {
3378 		usage();
3379 	} else if (Outputtofile && !DiskName) {
3380 		usage();
3381 	} else if (!Outputtofile && (argc - optind != 1)) {
3382 		usage();
3383 	} else if (SunBPBfields && !BootBlkFn) {
3384 		(void) fprintf(stderr,
3385 		    gettext("Use of the 'S' option requires that\n"
3386 		    "the 'B=' option also be used.\n\n"));
3387 		usage();
3388 	} else if (Firstfileattr != 0x20 && !FirstFn) {
3389 		(void) fprintf(stderr,
3390 		    gettext("Use of the 'r', 'h', or 's' options requires\n"
3391 		    "that the 'i=' option also be used.\n\n"));
3392 		usage();
3393 	} else if (!GetOffset && !DontUseFdisk) {
3394 		(void) fprintf(stderr,
3395 		    gettext("Use of the 'hidden' option requires that\n"
3396 		    "the 'nofdisk' option also be used.\n\n"));
3397 		usage();
3398 	} else if (DontUseFdisk && GetSize) {
3399 		(void) fprintf(stderr,
3400 		    gettext("Use of the 'nofdisk' option requires that\n"
3401 		    "the 'size=' option also be used.\n\n"));
3402 		usage();
3403 	} else if (!GetBPF &&
3404 	    BitsPerFAT != 12 && BitsPerFAT != 16 && BitsPerFAT != 32) {
3405 		(void) fprintf(stderr, gettext("Invalid Bits/Fat value."
3406 		    " Must be 12, 16 or 32.\n"));
3407 		exit(2);
3408 	} else if (!GetSPC && !powerofx_le_y(2, 128, SecPerClust)) {
3409 		(void) fprintf(stderr,
3410 		    gettext("Invalid Sectors/Cluster value.  Must be a "
3411 		    "power of 2 between 1 and 128.\n"));
3412 		exit(2);
3413 	} else if (!GetResrvd && (Resrvd < 1 || Resrvd > 0xffff)) {
3414 		(void) fprintf(stderr,
3415 		    gettext("Invalid number of reserved sectors.  "
3416 		    "Must be at least 1 but\nno larger than 65535."));
3417 		exit(2);
3418 	} else if (!GetResrvd && MakeFAT32 &&
3419 	    (Resrvd < 32 || Resrvd > 0xffff)) {
3420 		(void) fprintf(stderr,
3421 		    gettext("Invalid number of reserved sectors.  "
3422 		    "Must be at least 32 but\nno larger than 65535."));
3423 		exit(2);
3424 	} else if (Imagesize != 3 && Imagesize != 5) {
3425 		usage();
3426 	}
3427 }
3428 
3429 int
3430 main(int argc, char **argv)
3431 {
3432 	off64_t AbsBootSect = 0;
3433 	bpb_t dskparamblk;
3434 	char *string;
3435 	int  fd;
3436 	int  c;
3437 
3438 	(void) setlocale(LC_ALL, "");
3439 
3440 #if !defined(TEXT_DOMAIN)
3441 #define	TEXT_DOMAIN "SYS_TEST"
3442 #endif
3443 	(void) textdomain(TEXT_DOMAIN);
3444 
3445 	while ((c = getopt(argc, argv, "F:Vmo:")) != EOF) {
3446 		switch (c) {
3447 		case 'F':
3448 			string = optarg;
3449 			if (strcmp(string, "pcfs") != 0)
3450 				usage();
3451 			break;
3452 		case 'V':
3453 			{
3454 				char	*opt_text;
3455 				int	opt_count;
3456 
3457 				(void) fprintf(stdout,
3458 				    gettext("mkfs -F pcfs "));
3459 				for (opt_count = 1; opt_count < argc;
3460 				    opt_count++) {
3461 					opt_text = argv[opt_count];
3462 					if (opt_text)
3463 						(void) fprintf(stdout,
3464 						    " %s ", opt_text);
3465 				}
3466 				(void) fprintf(stdout, "\n");
3467 			}
3468 			break;
3469 		case 'm':
3470 			GetFsParams++;
3471 			break;
3472 		case 'o':
3473 			string = optarg;
3474 			parse_suboptions(string);
3475 			break;
3476 		}
3477 	}
3478 
3479 	sanity_check_options(argc, optind);
3480 
3481 	if (!Outputtofile)
3482 		DiskName = argv[optind];
3483 
3484 	(void) memset(&dskparamblk, 0, sizeof (dskparamblk));
3485 
3486 	if (GetFsParams) {
3487 		fd = open_and_examine(DiskName, &dskparamblk);
3488 	} else {
3489 		fd = open_and_seek(DiskName, &dskparamblk, &AbsBootSect);
3490 		if (ask_nicely(DiskName))
3491 			write_fat(fd, AbsBootSect, BootBlkFn, Label,
3492 			    FirstFn, &dskparamblk);
3493 	}
3494 	(void) close(fd);
3495 	return (0);
3496 }
3497