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