xref: /illumos-gate/usr/src/cmd/fs.d/pcfs/mkfs/mkfs.c (revision f127cb91)
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 		    ) {
2505 			(void) close(bsfd);
2506 			bsfd = -1;
2507 			(void) fprintf(stderr,
2508 			    gettext("Boot block (%s) bogus.\n"), fn);
2509 		}
2510 		*blkfilesize = fi.st_size;
2511 	}
2512 	return (bsfd);
2513 }
2514 
2515 /*
2516  *  verify_firstfile
2517  *
2518  *	We were provided with the name of a file to be the first file
2519  *	installed on the disk.  We just need to verify it exists and
2520  *	find out how big it is.  If it doesn't exist, we print a warning
2521  *	message about how the file wasn't found.  We don't exit fatally,
2522  *	though, rather we return a size of 0 and the FAT will be built
2523  *	without installing any first file.  They can then presumably
2524  *	install the correct first file by hand.
2525  */
2526 static
2527 int
2528 verify_firstfile(char *fn, ulong_t *filesize)
2529 {
2530 	struct stat fi;
2531 	int fd = -1;
2532 
2533 	*filesize = 0;
2534 	if (stat(fn, &fi) || (fd = open(fn, O_RDONLY)) < 0) {
2535 		perror(fn);
2536 		(void) fprintf(stderr,
2537 		    gettext("Could not access requested file.  It will not\n"
2538 		    "be installed in the new file system.\n"));
2539 	} else {
2540 		*filesize = fi.st_size;
2541 	}
2542 
2543 	return (fd);
2544 }
2545 
2546 /*
2547  *  label_volume
2548  *
2549  *	Fill in BPB with volume label.
2550  */
2551 static
2552 void
2553 label_volume(char *lbl, bpb_t *wbpb)
2554 {
2555 	int ll, i;
2556 
2557 	/* Put a volume label into our BPB. */
2558 	if (!lbl)
2559 		lbl = DEFAULT_LABEL;
2560 
2561 	ll = min(11, (int)strlen(lbl));
2562 	for (i = 0; i < ll; i++) {
2563 		wbpb->ebpb.volume_label[i] = toupper(lbl[i]);
2564 	}
2565 	for (; i < 11; i++) {
2566 		wbpb->ebpb.volume_label[i] = ' ';
2567 	}
2568 }
2569 
2570 static
2571 int
2572 copy_bootblk(char *fn, boot_sector_t *bootsect, ulong_t *bootblksize)
2573 {
2574 	int bsfd = -1;
2575 
2576 	if (Verbose && fn)
2577 		(void) printf(gettext("Request to install boot "
2578 		    "block file %s.\n"), fn);
2579 	else if (Verbose)
2580 		(void) printf(gettext("Request to install DOS boot block.\n"));
2581 
2582 	/*
2583 	 *  If they want to install their own boot block, sanity check
2584 	 *  that block.
2585 	 */
2586 	if (fn) {
2587 		bsfd = verify_bootblkfile(fn, bootsect, bootblksize);
2588 		if (bsfd < 0) {
2589 			exit(3);
2590 		}
2591 		*bootblksize = roundup(*bootblksize, BPSEC);
2592 	} else {
2593 		(void) memcpy(bootsect, DefBootSec, BPSEC);
2594 		*bootblksize = BPSEC;
2595 	}
2596 
2597 	return (bsfd);
2598 }
2599 
2600 /*
2601  *  mark_cluster
2602  *
2603  *	This routine fills a FAT entry with the value supplied to it as an
2604  *	argument.  The fatp argument is assumed to be a pointer to the FAT's
2605  *	0th entry.  The clustnum is the cluster entry that should be updated.
2606  *	The value is the new value for the entry.
2607  */
2608 static
2609 void
2610 mark_cluster(uchar_t *fatp, pc_cluster32_t clustnum, uint32_t value)
2611 {
2612 	uchar_t *ep;
2613 	ulong_t idx;
2614 
2615 	idx = (Fatentsize == 32) ? clustnum * 4 :
2616 	    (Fatentsize == 16) ? clustnum * 2 : clustnum + clustnum/2;
2617 	ep = fatp + idx;
2618 
2619 	if (Fatentsize == 32) {
2620 		store_32_bits(&ep, value);
2621 	} else if (Fatentsize == 16) {
2622 		store_16_bits(&ep, value);
2623 	} else {
2624 		if (clustnum & 1) {
2625 			*ep = (*ep & 0x0f) | ((value << 4) & 0xf0);
2626 			ep++;
2627 			*ep = (value >> 4) & 0xff;
2628 		} else {
2629 			*ep++ = value & 0xff;
2630 			*ep = (*ep & 0xf0) | ((value >> 8) & 0x0f);
2631 		}
2632 	}
2633 }
2634 
2635 static
2636 uchar_t *
2637 build_fat(bpb_t *wbpb, struct fat_od_fsi *fsinfop, ulong_t bootblksize,
2638     ulong_t *fatsize, char *ffn, int *fffd, ulong_t *ffsize,
2639     pc_cluster32_t *ffstartclust)
2640 {
2641 	pc_cluster32_t nextfree, ci;
2642 	uchar_t *fatp;
2643 	ushort_t numclust, numsect;
2644 	int  remclust;
2645 
2646 	/* Alloc space for a FAT and then null it out. */
2647 	if (Verbose) {
2648 		(void) printf(gettext("BUILD FAT.\n%d sectors per fat.\n"),
2649 		    wbpb->bpb.sectors_per_fat ? wbpb->bpb.sectors_per_fat :
2650 		    wbpb->bpb32.big_sectors_per_fat);
2651 	}
2652 
2653 	if (MakeFAT32) {
2654 		*fatsize = BPSEC * wbpb->bpb32.big_sectors_per_fat;
2655 	} else {
2656 		*fatsize = BPSEC * wbpb->bpb.sectors_per_fat;
2657 	}
2658 
2659 	if (!(fatp = (uchar_t *)malloc(*fatsize))) {
2660 		perror(gettext("FAT table alloc"));
2661 		exit(4);
2662 	} else {
2663 		(void) memset(fatp, 0, *fatsize);
2664 	}
2665 
2666 	/* Build in-memory FAT */
2667 	*fatp = wbpb->bpb.media;
2668 	*(fatp + 1) = 0xFF;
2669 	*(fatp + 2) = 0xFF;
2670 
2671 	if (Fatentsize == 16) {
2672 		*(fatp + 3) = 0xFF;
2673 	} else if (Fatentsize == 32) {
2674 		*(fatp + 3) = 0x0F;
2675 		*(fatp + 4) = 0xFF;
2676 		*(fatp + 5) = 0xFF;
2677 		*(fatp + 6) = 0xFF;
2678 		*(fatp + 7) = 0x0F;
2679 	}
2680 
2681 	/*
2682 	 * Keep track of clusters used.
2683 	 */
2684 	remclust = TotalClusters;
2685 	nextfree = 2;
2686 
2687 	/*
2688 	 * Get info on first file to install, if any.
2689 	 */
2690 	if (ffn)
2691 		*fffd = verify_firstfile(ffn, ffsize);
2692 
2693 	/*
2694 	 * Compute number of clusters to preserve for bootblk overage.
2695 	 * Remember that we already wrote the first sector of the boot block.
2696 	 * These clusters are marked BAD to prevent them from being deleted
2697 	 * or used.  The first available cluster is 2, so we always offset
2698 	 * the clusters.
2699 	 */
2700 	numsect = idivceil((bootblksize - BPSEC), BPSEC);
2701 	numclust = idivceil(numsect, wbpb->bpb.sectors_per_cluster);
2702 
2703 	if (Verbose && numclust)
2704 		(void) printf(gettext("Hiding %d excess bootblk cluster(s).\n"),
2705 		    numclust);
2706 	for (ci = 0; ci < numclust; ci++)
2707 		mark_cluster(fatp, nextfree++,
2708 		    MakeFAT32 ? PCF_BADCLUSTER32 : PCF_BADCLUSTER);
2709 	remclust -= numclust;
2710 
2711 	/*
2712 	 * Reserve a cluster for the root directory on a FAT32.
2713 	 */
2714 	if (MakeFAT32) {
2715 		mark_cluster(fatp, nextfree, PCF_LASTCLUSTER32);
2716 		wbpb->bpb32.root_dir_clust = nextfree++;
2717 		remclust--;
2718 	}
2719 
2720 	/*
2721 	 * Compute and preserve number of clusters for first file.
2722 	 */
2723 	if (*fffd >= 0) {
2724 		*ffstartclust = nextfree;
2725 		numsect = idivceil(*ffsize, BPSEC);
2726 		numclust = idivceil(numsect, wbpb->bpb.sectors_per_cluster);
2727 
2728 		if (numclust > remclust) {
2729 			(void) fprintf(stderr,
2730 			    gettext("Requested first file too large to be\n"
2731 			    "installed in the new file system.\n"));
2732 			(void) close(*fffd);
2733 			*fffd = -1;
2734 			goto finish;
2735 		}
2736 
2737 		if (Verbose)
2738 			(void) printf(gettext("Reserving %d first file "
2739 			    "cluster(s).\n"), numclust);
2740 		for (ci = 0; (int)ci < (int)(numclust-1); ci++, nextfree++)
2741 			mark_cluster(fatp, nextfree, nextfree + 1);
2742 		mark_cluster(fatp, nextfree++,
2743 		    MakeFAT32 ? PCF_LASTCLUSTER32 : PCF_LASTCLUSTER);
2744 		remclust -= numclust;
2745 	}
2746 
2747 finish:
2748 	if (Verbose) {
2749 		(void) printf(gettext("First sector of FAT"));
2750 		header_for_dump();
2751 		dump_bytes(fatp, BPSEC);
2752 	}
2753 
2754 	(void) memset(fsinfop, 0, sizeof (*fsinfop));
2755 	fsinfop->fsi_leadsig = LE_32(FSI_LEADSIG);
2756 	fsinfop->fsi_strucsig = LE_32(FSI_STRUCSIG);
2757 	fsinfop->fsi_trailsig = LE_32(FSI_TRAILSIG);
2758 	fsinfop->fsi_incore.fs_free_clusters = LE_32(remclust);
2759 	fsinfop->fsi_incore.fs_next_free = LE_32(nextfree);
2760 	return (fatp);
2761 }
2762 
2763 static
2764 void
2765 dirent_time_fill(struct pcdir *dep)
2766 {
2767 	struct  timeval tv;
2768 	struct	tm	*tp;
2769 	ushort_t	dostime;
2770 	ushort_t	dosday;
2771 
2772 	(void) gettimeofday(&tv, (struct timezone *)0);
2773 	tp = localtime(&tv.tv_sec);
2774 	/* get the time & day into DOS format */
2775 	dostime = tp->tm_sec / 2;
2776 	dostime |= tp->tm_min << 5;
2777 	dostime |= tp->tm_hour << 11;
2778 	dosday = tp->tm_mday;
2779 	dosday |= (tp->tm_mon + 1) << 5;
2780 	dosday |= (tp->tm_year - 80) << 9;
2781 	dep->pcd_mtime.pct_time = htols(dostime);
2782 	dep->pcd_mtime.pct_date = htols(dosday);
2783 }
2784 
2785 static
2786 void
2787 dirent_label_fill(struct pcdir *dep, char *fn)
2788 {
2789 	int nl, i;
2790 
2791 	/*
2792 	 * We spread the volume label across both the NAME and EXT fields
2793 	 */
2794 	nl = min(PCFNAMESIZE, strlen(fn));
2795 	for (i = 0; i < nl; i++) {
2796 		dep->pcd_filename[i] = toupper(fn[i]);
2797 	}
2798 	if (i < PCFNAMESIZE) {
2799 		for (; i < PCFNAMESIZE; i++)
2800 			dep->pcd_filename[i] = ' ';
2801 		for (i = 0; i < PCFEXTSIZE; i++)
2802 			dep->pcd_ext[i] = ' ';
2803 		return;
2804 	}
2805 	nl = min(PCFEXTSIZE, strlen(fn) - PCFNAMESIZE);
2806 	for (i = 0; i < nl; i++)
2807 		dep->pcd_ext[i] = toupper(fn[i + PCFNAMESIZE]);
2808 	if (i < PCFEXTSIZE) {
2809 		for (; i < PCFEXTSIZE; i++)
2810 			dep->pcd_ext[i] = ' ';
2811 	}
2812 }
2813 
2814 static
2815 void
2816 dirent_fname_fill(struct pcdir *dep, char *fn)
2817 {
2818 	char *fname, *fext;
2819 	int nl, i;
2820 
2821 	if (fname = strrchr(fn, '/')) {
2822 		fname++;
2823 	} else {
2824 		fname = fn;
2825 	}
2826 
2827 	if (fext = strrchr(fname, '.')) {
2828 		fext++;
2829 	} else {
2830 		fext = "";
2831 	}
2832 
2833 	fname = strtok(fname, ".");
2834 
2835 	nl = min(PCFNAMESIZE, (int)strlen(fname));
2836 	for (i = 0; i < nl; i++) {
2837 		dep->pcd_filename[i] = toupper(fname[i]);
2838 	}
2839 	for (; i < PCFNAMESIZE; i++) {
2840 		dep->pcd_filename[i] = ' ';
2841 	}
2842 
2843 	nl = min(PCFEXTSIZE, (int)strlen(fext));
2844 	for (i = 0; i < nl; i++) {
2845 		dep->pcd_ext[i] = toupper(fext[i]);
2846 	}
2847 	for (; i < PCFEXTSIZE; i++) {
2848 		dep->pcd_ext[i] = ' ';
2849 	}
2850 }
2851 
2852 static
2853 uchar_t *
2854 build_rootdir(bpb_t *wbpb, char *ffn, int fffd,
2855     ulong_t ffsize, pc_cluster32_t ffstart, ulong_t *rdirsize)
2856 {
2857 	struct pcdir *rootdirp;
2858 	struct pcdir *entry;
2859 
2860 	/*
2861 	 * Build a root directory.  It will have at least one entry,
2862 	 * the volume label and a second if the first file was defined.
2863 	 */
2864 	if (MakeFAT32) {
2865 		/*
2866 		 * We devote an entire cluster to the root
2867 		 * directory on FAT32.
2868 		 */
2869 		*rdirsize = wbpb->bpb.sectors_per_cluster * BPSEC;
2870 	} else {
2871 		*rdirsize = wbpb->bpb.num_root_entries * sizeof (struct pcdir);
2872 	}
2873 	if ((rootdirp = (struct pcdir *)malloc(*rdirsize)) == NULL) {
2874 		perror(gettext("Root directory allocation"));
2875 		exit(4);
2876 	} else {
2877 		entry = rootdirp;
2878 		(void) memset((char *)rootdirp, 0, *rdirsize);
2879 	}
2880 
2881 	/* Create directory entry for first file, if there is one */
2882 	if (fffd >= 0) {
2883 		dirent_fname_fill(entry, ffn);
2884 		entry->pcd_attr = Firstfileattr;
2885 		dirent_time_fill(entry);
2886 		entry->pcd_scluster_lo = htols(ffstart);
2887 		if (MakeFAT32) {
2888 			ffstart = ffstart >> 16;
2889 			entry->un.pcd_scluster_hi = htols(ffstart);
2890 		}
2891 		entry->pcd_size = htoli(ffsize);
2892 		entry++;
2893 	}
2894 
2895 	/* Create directory entry for volume label, if there is one */
2896 	if (Label != NULL) {
2897 		dirent_label_fill(entry, Label);
2898 		entry->pcd_attr = PCA_ARCH | PCA_LABEL;
2899 		dirent_time_fill(entry);
2900 		entry->pcd_scluster_lo = 0;
2901 		if (MakeFAT32) {
2902 			entry->un.pcd_scluster_hi = 0;
2903 		}
2904 		entry->pcd_size = 0;
2905 		entry++;
2906 	}
2907 
2908 	if (Verbose) {
2909 		(void) printf(gettext("First two directory entries"));
2910 		header_for_dump();
2911 		dump_bytes((uchar_t *)rootdirp, 2 * sizeof (struct pcdir));
2912 	}
2913 
2914 	return ((uchar_t *)rootdirp);
2915 }
2916 
2917 /*
2918  * write_rest
2919  *
2920  *	Write all the bytes from the current file pointer to end of file
2921  *	in the source file out to the destination file.  The writes should
2922  *	be padded to whole clusters with 0's if necessary.
2923  */
2924 static
2925 void
2926 write_rest(bpb_t *wbpb, char *efn, int dfd, int sfd, int remaining)
2927 {
2928 	char buf[BPSEC];
2929 	ushort_t numsect, numclust;
2930 	ushort_t wnumsect, s;
2931 	int doneread = 0;
2932 	int rstat;
2933 
2934 	/*
2935 	 * Compute number of clusters required to contain remaining bytes.
2936 	 */
2937 	numsect = idivceil(remaining, BPSEC);
2938 	numclust = idivceil(numsect, wbpb->bpb.sectors_per_cluster);
2939 
2940 	wnumsect = numclust * wbpb->bpb.sectors_per_cluster;
2941 	for (s = 0; s < wnumsect; s++) {
2942 		if (!doneread) {
2943 			if ((rstat = read(sfd, buf, BPSEC)) < 0) {
2944 				perror(efn);
2945 				doneread = 1;
2946 				rstat = 0;
2947 			} else if (rstat == 0) {
2948 				doneread = 1;
2949 			}
2950 			(void) memset(&(buf[rstat]), 0, BPSEC - rstat);
2951 		}
2952 		if (write(dfd, buf, BPSEC) != BPSEC) {
2953 			(void) fprintf(stderr, gettext("Copying "));
2954 			perror(efn);
2955 		}
2956 	}
2957 }
2958 
2959 static
2960 void
2961 write_fat32_bootstuff(int fd, boot_sector_t *bsp,
2962 	struct fat_od_fsi *fsinfop, off64_t seekto)
2963 {
2964 	if (Verbose) {
2965 		(void) printf(gettext("Dump of the fs info sector"));
2966 		header_for_dump();
2967 		dump_bytes((uchar_t *)fsinfop, sizeof (*fsinfop));
2968 	}
2969 
2970 	if (!Notreally) {
2971 		/*
2972 		 * FAT32's have an FS info sector, then a backup of the boot
2973 		 * sector, and a modified backup of the FS Info sector.
2974 		 */
2975 		if (write(fd, fsinfop, sizeof (*fsinfop)) != BPSEC) {
2976 			perror(gettext("FS info sector write"));
2977 			exit(4);
2978 		}
2979 		if (lseek64(fd,	seekto + BKUP_BOOTSECT_OFFSET, SEEK_SET) < 0) {
2980 			(void) close(fd);
2981 			perror(gettext("Boot sector backup seek"));
2982 			exit(4);
2983 		}
2984 		if (write(fd, bsp->buf, sizeof (bsp->buf)) != BPSEC) {
2985 			perror(gettext("Boot sector backup write"));
2986 			exit(4);
2987 		}
2988 	}
2989 
2990 	/*
2991 	 * Second copy of fs info sector is modified to have "don't know"
2992 	 * as the number of free clusters
2993 	 */
2994 	fsinfop->fsi_incore.fs_next_free = LE_32(FSINFO_UNKNOWN);
2995 
2996 	if (Verbose) {
2997 		(void) printf(gettext("Dump of the backup fs info sector"));
2998 		header_for_dump();
2999 		dump_bytes((uchar_t *)fsinfop, sizeof (*fsinfop));
3000 	}
3001 
3002 	if (!Notreally) {
3003 		if (write(fd, fsinfop, sizeof (*fsinfop)) != BPSEC) {
3004 			perror(gettext("FS info sector backup write"));
3005 			exit(4);
3006 		}
3007 	}
3008 }
3009 
3010 static
3011 void
3012 write_bootsects(int fd, boot_sector_t *bsp, bpb_t *wbpb,
3013 	struct fat_od_fsi *fsinfop, off64_t seekto)
3014 {
3015 	if (MakeFAT32) {
3016 		/* Copy our BPB into bootsec structure */
3017 #ifdef i386
3018 		(void) memcpy(&(bsp->bs32.bs_front.bs_bpb), &(wbpb->bpb),
3019 		    sizeof (wbpb->bpb));
3020 		(void) memcpy(&(bsp->bs32.bs_bpb32), &(wbpb->bpb32),
3021 		    sizeof (wbpb->bpb32));
3022 		(void) memcpy(&(bsp->bs32.bs_ebpb), &(wbpb->ebpb),
3023 		    sizeof (wbpb->ebpb));
3024 #else
3025 		swap_pack_bpb32cpy(&(bsp->bs32), wbpb);
3026 #endif
3027 	} else {
3028 		/* Copy our BPB into bootsec structure */
3029 #ifdef i386
3030 		(void) memcpy(&(bsp->bs.bs_front.bs_bpb), &(wbpb->bpb),
3031 		    sizeof (wbpb->bpb));
3032 		(void) memcpy(&(bsp->bs.bs_ebpb), &(wbpb->ebpb),
3033 		    sizeof (wbpb->ebpb));
3034 #else
3035 		swap_pack_bpbcpy(&(bsp->bs), wbpb);
3036 #endif
3037 
3038 		/* Copy SUN BPB extensions into bootsec structure */
3039 		if (SunBPBfields) {
3040 #ifdef i386
3041 			(void) memcpy(&(bsp->bs.bs_sebpb), &(wbpb->sunbpb),
3042 			    sizeof (wbpb->sunbpb));
3043 #else
3044 			swap_pack_sebpbcpy(&(bsp->bs), wbpb);
3045 #endif
3046 		}
3047 	}
3048 
3049 	/* Write boot sector */
3050 	if (!Notreally && write(fd, bsp->buf, sizeof (bsp->buf)) != BPSEC) {
3051 		perror(gettext("Boot sector write"));
3052 		exit(4);
3053 	}
3054 
3055 	if (Verbose) {
3056 		(void) printf(gettext("Dump of the boot sector"));
3057 		header_for_dump();
3058 		dump_bytes(bsp->buf, sizeof (bsp->buf));
3059 	}
3060 
3061 	if (MakeFAT32)
3062 		write_fat32_bootstuff(fd, bsp, fsinfop, seekto);
3063 }
3064 
3065 static
3066 void
3067 write_fat(int fd, off64_t seekto, char *fn, char *lbl, char *ffn, bpb_t *wbpb)
3068 {
3069 	struct fat_od_fsi fsinfo;
3070 	pc_cluster32_t ffsc;
3071 	boot_sector_t bootsect;
3072 	uchar_t *fatp, *rdirp;
3073 	ulong_t bootblksize, fatsize, rdirsize, ffsize;
3074 	int bsfd = -1;
3075 	int fffd = -1;
3076 
3077 	compute_file_area_size(wbpb);
3078 
3079 	bsfd = copy_bootblk(fn, &bootsect, &bootblksize);
3080 	label_volume(lbl, wbpb);
3081 
3082 	if (Verbose)
3083 		(void) printf(gettext("Building FAT.\n"));
3084 	fatp = build_fat(wbpb, &fsinfo, bootblksize, &fatsize,
3085 	    ffn, &fffd, &ffsize, &ffsc);
3086 
3087 	write_bootsects(fd, &bootsect, wbpb, &fsinfo, seekto);
3088 
3089 	if (lseek64(fd,
3090 	    seekto + (BPSEC * wbpb->bpb.resv_sectors), SEEK_SET) < 0) {
3091 		(void) close(fd);
3092 		perror(gettext("Seek to end of reserved sectors"));
3093 		exit(4);
3094 	}
3095 
3096 	/* Write FAT */
3097 	if (Verbose)
3098 		(void) printf(gettext("Writing FAT(s). %d bytes times %d.\n"),
3099 		    fatsize, wbpb->bpb.num_fats);
3100 	if (!Notreally) {
3101 		int nf, wb;
3102 		for (nf = 0; nf < (int)wbpb->bpb.num_fats; nf++)
3103 			if ((wb = write(fd, fatp, fatsize)) != fatsize) {
3104 				perror(gettext("FAT write"));
3105 				exit(4);
3106 			} else {
3107 				if (Verbose)
3108 					(void) printf(
3109 					    gettext("Wrote %d bytes\n"), wb);
3110 			}
3111 	}
3112 	free(fatp);
3113 
3114 	if (Verbose)
3115 		(void) printf(gettext("Building root directory.\n"));
3116 	rdirp = build_rootdir(wbpb, ffn, fffd, ffsize, ffsc, &rdirsize);
3117 
3118 	/*
3119 	 *  In non FAT32, root directory exists outside of the file area
3120 	 */
3121 	if (!MakeFAT32) {
3122 		if (Verbose)
3123 			(void) printf(gettext("Writing root directory. "
3124 			    "%d bytes.\n"), rdirsize);
3125 		if (!Notreally) {
3126 			if (write(fd, rdirp, rdirsize) != rdirsize) {
3127 				perror(gettext("Root directory write"));
3128 				exit(4);
3129 			}
3130 		}
3131 		free(rdirp);
3132 	}
3133 
3134 	/*
3135 	 * Now write anything that needs to be in the file space.
3136 	 */
3137 	if (bootblksize > BPSEC) {
3138 		if (Verbose)
3139 			(void) printf(gettext("Writing remainder of "
3140 			    "boot block.\n"));
3141 		if (!Notreally)
3142 			write_rest(wbpb, fn, fd, bsfd, bootblksize - BPSEC);
3143 	}
3144 
3145 	if (MakeFAT32) {
3146 		if (Verbose)
3147 			(void) printf(gettext("Writing root directory. "
3148 			    "%d bytes.\n"), rdirsize);
3149 		if (!Notreally) {
3150 			if (write(fd, rdirp, rdirsize) != rdirsize) {
3151 				perror(gettext("Root directory write"));
3152 				exit(4);
3153 			}
3154 		}
3155 		free(rdirp);
3156 	}
3157 
3158 	if (fffd >= 0) {
3159 		if (Verbose)
3160 			(void) printf(gettext("Writing first file.\n"));
3161 		if (!Notreally)
3162 			write_rest(wbpb, ffn, fd, fffd, ffsize);
3163 	}
3164 }
3165 
3166 static
3167 char *LegalOpts[] = {
3168 #define	NFLAG 0
3169 	"N",
3170 #define	VFLAG 1
3171 	"v",
3172 #define	RFLAG 2
3173 	"r",
3174 #define	HFLAG 3
3175 	"h",
3176 #define	SFLAG 4
3177 	"s",
3178 #define	SUNFLAG 5
3179 	"S",
3180 #define	LABFLAG 6
3181 	"b",
3182 #define	BTRFLAG 7
3183 	"B",
3184 #define	INITFLAG 8
3185 	"i",
3186 #define	SZFLAG 9
3187 	"size",
3188 #define	SECTFLAG 10
3189 	"nsect",
3190 #define	TRKFLAG 11
3191 	"ntrack",
3192 #define	SPCFLAG 12
3193 	"spc",
3194 #define	BPFFLAG 13
3195 	"fat",
3196 #define	FFLAG 14
3197 	"f",
3198 #define	DFLAG 15
3199 	"d",
3200 #define	NOFDISKFLAG 16
3201 	"nofdisk",
3202 #define	RESRVFLAG 17
3203 	"reserve",
3204 #define	HIDDENFLAG 18
3205 	"hidden",
3206 	NULL
3207 };
3208 
3209 static
3210 void
3211 bad_arg(char *option)
3212 {
3213 	(void) fprintf(stderr,
3214 	    gettext("Unrecognized option %s.\n"), option);
3215 	usage();
3216 	exit(2);
3217 }
3218 
3219 static
3220 void
3221 missing_arg(char *option)
3222 {
3223 	(void) fprintf(stderr,
3224 	    gettext("Option %s requires a value.\n"), option);
3225 	usage();
3226 	exit(3);
3227 }
3228 
3229 static
3230 void
3231 parse_suboptions(char *optsstr)
3232 {
3233 	char *value;
3234 	int c;
3235 
3236 	while (*optsstr != '\0') {
3237 		switch (c = getsubopt(&optsstr, LegalOpts, &value)) {
3238 		case NFLAG:
3239 			Notreally++;
3240 			break;
3241 		case VFLAG:
3242 			Verbose++;
3243 			break;
3244 		case RFLAG:
3245 			Firstfileattr |= 0x01;
3246 			break;
3247 		case HFLAG:
3248 			Firstfileattr |= 0x02;
3249 			break;
3250 		case SFLAG:
3251 			Firstfileattr |= 0x04;
3252 			break;
3253 		case SUNFLAG:
3254 			SunBPBfields = 1;
3255 			break;
3256 		case LABFLAG:
3257 			if (value == NULL) {
3258 				missing_arg(LegalOpts[c]);
3259 			} else {
3260 				Label = value;
3261 			}
3262 			break;
3263 		case BTRFLAG:
3264 			if (value == NULL) {
3265 				missing_arg(LegalOpts[c]);
3266 			} else {
3267 				BootBlkFn = value;
3268 			}
3269 			break;
3270 		case INITFLAG:
3271 			if (value == NULL) {
3272 				missing_arg(LegalOpts[c]);
3273 			} else {
3274 				FirstFn = value;
3275 			}
3276 			break;
3277 		case SZFLAG:
3278 			if (value == NULL) {
3279 				missing_arg(LegalOpts[c]);
3280 			} else {
3281 				TotSize = atoi(value);
3282 				GetSize = 0;
3283 			}
3284 			break;
3285 		case SECTFLAG:
3286 			if (value == NULL) {
3287 				missing_arg(LegalOpts[c]);
3288 			} else {
3289 				SecPerTrk = atoi(value);
3290 				GetSPT = 0;
3291 			}
3292 			break;
3293 		case TRKFLAG:
3294 			if (value == NULL) {
3295 				missing_arg(LegalOpts[c]);
3296 			} else {
3297 				TrkPerCyl = atoi(value);
3298 				GetTPC = 0;
3299 			}
3300 			break;
3301 		case SPCFLAG:
3302 			if (value == NULL) {
3303 				missing_arg(LegalOpts[c]);
3304 			} else {
3305 				SecPerClust = atoi(value);
3306 				GetSPC = 0;
3307 			}
3308 			break;
3309 		case BPFFLAG:
3310 			if (value == NULL) {
3311 				missing_arg(LegalOpts[c]);
3312 			} else {
3313 				BitsPerFAT = atoi(value);
3314 				GetBPF = 0;
3315 			}
3316 			break;
3317 		case NOFDISKFLAG:
3318 			DontUseFdisk = 1;
3319 			break;
3320 		case RESRVFLAG:
3321 			if (value == NULL) {
3322 				missing_arg(LegalOpts[c]);
3323 			} else {
3324 				Resrvd = atoi(value);
3325 				GetResrvd = 0;
3326 			}
3327 			break;
3328 		case HIDDENFLAG:
3329 			if (value == NULL) {
3330 				missing_arg(LegalOpts[c]);
3331 			} else {
3332 				RelOffset = atoi(value);
3333 				GetOffset = 0;
3334 			}
3335 			break;
3336 		case FFLAG:
3337 			if (value == NULL) {
3338 				missing_arg(LegalOpts[c]);
3339 			} else {
3340 				DiskName = value;
3341 				Outputtofile = 1;
3342 			}
3343 			break;
3344 		case DFLAG:
3345 			if (value == NULL) {
3346 				missing_arg(LegalOpts[c]);
3347 			} else {
3348 				Imagesize = atoi(value);
3349 			}
3350 			break;
3351 		default:
3352 			bad_arg(value);
3353 			break;
3354 		}
3355 	}
3356 }
3357 
3358 static
3359 void
3360 sanity_check_options(int argc, int optind)
3361 {
3362 	if (GetFsParams) {
3363 		if (argc - optind != 1)
3364 			usage();
3365 		return;
3366 	}
3367 
3368 	if (DontUseFdisk && GetOffset) {
3369 		/* Set default relative offset of zero */
3370 		RelOffset = 0;
3371 	}
3372 
3373 	if (BitsPerFAT == 32)
3374 		MakeFAT32 = 1;
3375 
3376 	if (Outputtofile && (argc - optind)) {
3377 		usage();
3378 	} else if (Outputtofile && !DiskName) {
3379 		usage();
3380 	} else if (!Outputtofile && (argc - optind != 1)) {
3381 		usage();
3382 	} else if (SunBPBfields && !BootBlkFn) {
3383 		(void) fprintf(stderr,
3384 		    gettext("Use of the 'S' option requires that\n"
3385 		    "the 'B=' option also be used.\n\n"));
3386 		usage();
3387 	} else if (Firstfileattr != 0x20 && !FirstFn) {
3388 		(void) fprintf(stderr,
3389 		    gettext("Use of the 'r', 'h', or 's' options requires\n"
3390 		    "that the 'i=' option also be used.\n\n"));
3391 		usage();
3392 	} else if (!GetOffset && !DontUseFdisk) {
3393 		(void) fprintf(stderr,
3394 		    gettext("Use of the 'hidden' option requires that\n"
3395 		    "the 'nofdisk' option also be used.\n\n"));
3396 		usage();
3397 	} else if (DontUseFdisk && GetSize) {
3398 		(void) fprintf(stderr,
3399 		    gettext("Use of the 'nofdisk' option requires that\n"
3400 		    "the 'size=' option also be used.\n\n"));
3401 		usage();
3402 	} else if (!GetBPF &&
3403 	    BitsPerFAT != 12 && BitsPerFAT != 16 && BitsPerFAT != 32) {
3404 		(void) fprintf(stderr, gettext("Invalid Bits/Fat value."
3405 		    " Must be 12, 16 or 32.\n"));
3406 		exit(2);
3407 	} else if (!GetSPC && !powerofx_le_y(2, 128, SecPerClust)) {
3408 		(void) fprintf(stderr,
3409 		    gettext("Invalid Sectors/Cluster value.  Must be a "
3410 		    "power of 2 between 1 and 128.\n"));
3411 		exit(2);
3412 	} else if (!GetResrvd && (Resrvd < 1 || Resrvd > 0xffff)) {
3413 		(void) fprintf(stderr,
3414 		    gettext("Invalid number of reserved sectors.  "
3415 		    "Must be at least 1 but\nno larger than 65535."));
3416 		exit(2);
3417 	} else if (!GetResrvd && MakeFAT32 &&
3418 	    (Resrvd < 32 || Resrvd > 0xffff)) {
3419 		(void) fprintf(stderr,
3420 		    gettext("Invalid number of reserved sectors.  "
3421 		    "Must be at least 32 but\nno larger than 65535."));
3422 		exit(2);
3423 	} else if (Imagesize != 3 && Imagesize != 5) {
3424 		usage();
3425 	}
3426 }
3427 
3428 int
3429 main(int argc, char **argv)
3430 {
3431 	off64_t AbsBootSect = 0;
3432 	bpb_t dskparamblk;
3433 	char *string;
3434 	int  fd;
3435 	int  c;
3436 
3437 	(void) setlocale(LC_ALL, "");
3438 
3439 #if !defined(TEXT_DOMAIN)
3440 #define	TEXT_DOMAIN "SYS_TEST"
3441 #endif
3442 	(void) textdomain(TEXT_DOMAIN);
3443 
3444 	while ((c = getopt(argc, argv, "F:Vmo:")) != EOF) {
3445 		switch (c) {
3446 		case 'F':
3447 			string = optarg;
3448 			if (strcmp(string, "pcfs") != 0)
3449 				usage();
3450 			break;
3451 		case 'V':
3452 			{
3453 				char	*opt_text;
3454 				int	opt_count;
3455 
3456 				(void) fprintf(stdout,
3457 				    gettext("mkfs -F pcfs "));
3458 				for (opt_count = 1; opt_count < argc;
3459 				    opt_count++) {
3460 					opt_text = argv[opt_count];
3461 					if (opt_text)
3462 						(void) fprintf(stdout,
3463 						    " %s ", opt_text);
3464 				}
3465 				(void) fprintf(stdout, "\n");
3466 			}
3467 			break;
3468 		case 'm':
3469 			GetFsParams++;
3470 			break;
3471 		case 'o':
3472 			string = optarg;
3473 			parse_suboptions(string);
3474 			break;
3475 		}
3476 	}
3477 
3478 	sanity_check_options(argc, optind);
3479 
3480 	if (!Outputtofile)
3481 		DiskName = argv[optind];
3482 
3483 	(void) memset(&dskparamblk, 0, sizeof (dskparamblk));
3484 
3485 	if (GetFsParams) {
3486 		fd = open_and_examine(DiskName, &dskparamblk);
3487 	} else {
3488 		fd = open_and_seek(DiskName, &dskparamblk, &AbsBootSect);
3489 		if (ask_nicely(DiskName))
3490 			write_fat(fd, AbsBootSect, BootBlkFn, Label,
3491 			    FirstFn, &dskparamblk);
3492 	}
3493 	(void) close(fd);
3494 	return (0);
3495 }
3496