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