xref: /illumos-gate/usr/src/cmd/fs.d/pcfs/mkfs/mkfs.c (revision 0576819e)
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  *	Values taken from a table found on p. 407 of "Windows 98
1305  *	Professional Reference", by Bruce A. Hallberg & Joe
1306  *	Casad. (ISBN 0-56205-786-3) I believe they've taken their
1307  *	table directly from the MSDN docs.
1308  *
1309  *	FAT32 information taken from "Partition Magic 6.0", User Guide, p67.
1310  *	It uses strange values as 8.01, 16.02 and 32.04; the windows support
1311  *	site uses 8G and 16G.
1312  */
1313 static
1314 void
1315 compute_cluster_size(bpb_t *wbpb)
1316 {
1317 	ulong_t volsize, maxclusters;
1318 	ulong_t spc, spf;
1319 	ulong_t rds, tmpval1, tmpval2;
1320 	ulong_t disksz;
1321 	ulong_t fatsz;
1322 	volsize = wbpb->bpb.sectors_in_volume ? wbpb->bpb.sectors_in_volume :
1323 		wbpb->bpb.sectors_in_logical_volume;
1324 	volsize -= wbpb->bpb.resv_sectors;
1325 
1326 	if (GetSPC) {
1327 		if (MakeFAT32) {
1328 			if (volsize < 0x10428) {
1329 				spc = 0; /* too small, trigger an error */
1330 			} else if (volsize <= 0x82000) {	/* 260MB */
1331 				spc = 1;
1332 			} else if (volsize <= 0x1000000) {	/* 8G */
1333 				spc = 8;
1334 			} else if (volsize <= 0x2000000) {	/* 16G */
1335 				spc = 16;
1336 			} else if (volsize <= 0x4000000) {	/* 32G */
1337 				spc = 32;
1338 			} else {
1339 				spc = 64;
1340 			}
1341 		} else {
1342 			/* FAT16 Table */
1343 			if (volsize <= 0x20D0) {		/* 4.1M */
1344 				spc = 0; /* too small, trigger an error */
1345 			} else if (volsize <= 0x7FA8) {		/* 16M */
1346 				spc = 2;
1347 			} else if (volsize <= 0x40000) {	/* 128M */
1348 				spc = 4;
1349 			} else if (volsize <= 0x80000) {	/* 256M */
1350 				spc = 8;
1351 			} else if (volsize <= 0x100000) {	/* 512M */
1352 				spc = 16;
1353 			/* Entries beyond here are only if FAT16 is forced */
1354 			} else if (volsize <= 0x200000) {	/* 1G */
1355 				spc = 32;
1356 			} else if (volsize <= 0x400000) {	/* 2G */
1357 				spc = 64;
1358 			} else if (volsize <= 0x800000) {	/* 4G */
1359 				spc = 128;
1360 			} else {
1361 				(void) fprintf(stderr, gettext(
1362 				    "Partition too large for a FAT!\n"));
1363 				exit(4);
1364 			}
1365 		}
1366 	} else {
1367 		spc = SecPerClust;
1368 	}
1369 
1370 	/*
1371 	 * RootDirSectors = ((BPB_RootEntCnt * 32) +
1372 	 *	(BPB_BytsPerSec  1)) / BPB_BytsPerSec;
1373 	 */
1374 	rds = ((wbpb->bpb.num_root_entries * 32) +
1375 		(wbpb->bpb.bytes_sector - 1)) /
1376 		wbpb->bpb.bytes_sector;
1377 
1378 	if (GetBPF) {
1379 		if (MakeFAT32)
1380 			Fatentsize = 32;
1381 		else
1382 			Fatentsize = 16;
1383 	} else {
1384 		Fatentsize = BitsPerFAT;
1385 
1386 		if (Fatentsize == 12 &&
1387 			(volsize - rds) >= DOS_F12MAXC * spc) {
1388 			/*
1389 			 * 4228473 No way to non-interactively make a
1390 			 *	   pcfs filesystem
1391 			 *
1392 			 *	If we don't have an input TTY, or we aren't
1393 			 *	really doing anything, then don't ask
1394 			 *	questions.  Assume a yes answer to any
1395 			 *	questions we would ask.
1396 			 */
1397 			if (Notreally || !isatty(fileno(stdin))) {
1398 			    (void) printf(
1399 				gettext("Volume too large for 12 bit FAT,"
1400 				" increasing to 16 bit FAT size.\n"));
1401 			    (void) fflush(stdout);
1402 			    Fatentsize = 16;
1403 			} else {
1404 			    (void) printf(
1405 				gettext("Volume too large for a 12 bit FAT.\n"
1406 					"Increase to 16 bit FAT "
1407 					"and continue (y/n)? "));
1408 			    (void) fflush(stdout);
1409 			    if (yes())
1410 				Fatentsize = 16;
1411 			    else
1412 				exit(5);
1413 			}
1414 		}
1415 	}
1416 	wbpb->bpb.sectors_per_cluster = spc;
1417 
1418 	if (!GetFsParams && FdiskFATsize < 0) {
1419 		(void) printf(
1420 		    gettext("Cannot verify chosen/computed FAT "
1421 			"entry size (%d bits) with FDISK table.\n"
1422 			"FDISK table has an unknown file system "
1423 			"type for this device.  Giving up...\n"),
1424 			Fatentsize, Fatentsize);
1425 		exit(6);
1426 	} else if (!GetFsParams && FdiskFATsize && FdiskFATsize != Fatentsize) {
1427 		(void) printf(
1428 		    gettext("Chosen/computed FAT entry size (%d bits) "
1429 			"does not match FDISK table (%d bits).\n"),
1430 			Fatentsize, FdiskFATsize);
1431 		(void) printf(
1432 		    gettext("Use -o fat=%d to build a FAT "
1433 			"that matches the FDISK entry.\n"), FdiskFATsize);
1434 		exit(6);
1435 	}
1436 	set_fat_string(wbpb, Fatentsize);
1437 	/*
1438 	 * Compure the FAT sizes according to algorithm from Microsoft:
1439 	 *
1440 	 * RootDirSectors = ((BPB_RootEntCnt * 32) +
1441 	 *	(BPB_BytsPerSec  1)) / BPB_BytsPerSec;
1442 	 * TmpVal1 = DskSize - (BPB_ResvdSecCnt + RootDirSectors);
1443 	 * TmpVal2 = (256 * BPB_SecPerClus) + BPB_NumFATs;
1444 	 * If (FATType == FAT32)
1445 	 * 	TmpVal2 = TmpVal2 / 2;
1446 	 * FATSz = (TMPVal1 + (TmpVal2  1)) / TmpVal2;
1447 	 * If (FATType == FAT32) {
1448 	 * 	BPB_FATSz16 = 0;
1449 	 *	BPB_FATSz32 = FATSz;
1450 	 * } else {
1451 	 *	BPB_FATSz16 = LOWORD(FATSz);
1452 	 * 	// there is no BPB_FATSz32 in a FAT16 BPB
1453 	 * }
1454 	 */
1455 	tmpval1 = volsize - (wbpb->bpb.resv_sectors + rds);
1456 
1457 	tmpval2 = (256 * wbpb->bpb.sectors_per_cluster) +
1458 		wbpb->bpb.num_fats;
1459 
1460 	if (Fatentsize == 32)
1461 		tmpval2 = tmpval2 / 2;
1462 
1463 	fatsz = (tmpval1 + (tmpval2 - 1)) / tmpval2;
1464 
1465 	/* Compute a sector/fat figure */
1466 	switch (Fatentsize) {
1467 	case 32:
1468 		wbpb->bpb.sectors_per_fat = 0;
1469 		wbpb->bpb32.big_sectors_per_fat = fatsz;
1470 		if (Verbose)
1471 		    printf("compute_cluster_size: Sectors per FAT32 = %d\n",
1472 				    wbpb->bpb32.big_sectors_per_fat);
1473 		break;
1474 	case 12:
1475 	default:	/* 16 bit FAT */
1476 		wbpb->bpb.sectors_per_fat = (ushort_t)(fatsz & 0x0000FFFF);
1477 		if (Verbose)
1478 		    printf("compute_cluster_size: Sectors per FAT16 = %d\n",
1479 			wbpb->bpb.sectors_per_fat);
1480 		break;
1481 	}
1482 }
1483 
1484 static
1485 void
1486 find_fixed_details(int fd, bpb_t *wbpb)
1487 {
1488 	struct dk_geom dginfo;
1489 
1490 	/*
1491 	 *  Look up the last remaining bits of info we need
1492 	 *  that is specific to the hard drive using a disk ioctl.
1493 	 */
1494 	if (GetSPT || GetTPC) {
1495 		if ((ioctl(fd, DKIOCG_VIRTGEOM, &dginfo)) == -1) {
1496 		    if ((ioctl(fd, DKIOCG_PHYGEOM, &dginfo)) == -1) {
1497 			if ((ioctl(fd, DKIOCGGEOM, &dginfo)) == -1) {
1498 			    (void) close(fd);
1499 			    perror(
1500 				gettext("Drive geometry lookup (need "
1501 				    "tracks/cylinder and/or sectors/track"));
1502 			    exit(2);
1503 			}
1504 		    }
1505 		}
1506 	}
1507 
1508 	wbpb->bpb.heads = (GetTPC ? dginfo.dkg_nhead : TrkPerCyl);
1509 	wbpb->bpb.sectors_per_track = (GetSPT ? dginfo.dkg_nsect : SecPerTrk);
1510 
1511 	if (Verbose) {
1512 		if (GetTPC) {
1513 		    (void) printf(
1514 			gettext("DKIOCG determined number of heads = %d\n"),
1515 			dginfo.dkg_nhead);
1516 		}
1517 		if (GetSPT) {
1518 		    (void) printf(
1519 			gettext("DKIOCG determined sectors per track = %d\n"),
1520 			dginfo.dkg_nsect);
1521 		}
1522 	}
1523 
1524 	/*
1525 	 * XXX - MAY need an additional flag (or flags) to set media
1526 	 * and physical drive number fields.  That in the case of weird
1527 	 * floppies that have to go through 'nofdisk' route for formatting.
1528 	 */
1529 	wbpb->bpb.media = 0xF8;
1530 	if (MakeFAT32)
1531 		wbpb->bpb.num_root_entries = 0;
1532 	else
1533 		wbpb->bpb.num_root_entries = 512;
1534 	wbpb->ebpb.phys_drive_num = 0x80;
1535 	compute_cluster_size(wbpb);
1536 }
1537 
1538 static
1539 char *
1540 stat_actual_disk(char *diskname, struct stat *info, char **suffix)
1541 {
1542 	char *actualdisk;
1543 
1544 	if (stat(diskname, info)) {
1545 		/*
1546 		 *  Device named on command line doesn't exist.  That
1547 		 *  probably means there is a partition-specifying
1548 		 *  suffix attached to the actual disk name.
1549 		 */
1550 		actualdisk = strtok(strdup(diskname), ":");
1551 		if (*suffix = strchr(diskname, ':'))
1552 			(*suffix)++;
1553 
1554 		if (stat(actualdisk, info)) {
1555 			perror(actualdisk);
1556 			exit(2);
1557 		}
1558 	} else {
1559 		actualdisk = strdup(diskname);
1560 	}
1561 
1562 	return (actualdisk);
1563 }
1564 
1565 static
1566 void
1567 compute_file_area_size(bpb_t *wbpb)
1568 {
1569 	int FATSz;
1570 	int TotSec;
1571 	int DataSec;
1572 	int RootDirSectors = ((wbpb->bpb.num_root_entries * 32) +
1573 			(wbpb->bpb.bytes_sector - 1)) /
1574 			wbpb->bpb.bytes_sector;
1575 
1576 	if (wbpb->bpb.sectors_per_fat) {
1577 		/*
1578 		 * Good old FAT12 or FAT16
1579 		 */
1580 		FATSz = wbpb->bpb.sectors_per_fat;
1581 		TotSec = wbpb->bpb.sectors_in_volume;
1582 	} else {
1583 		/*
1584 		 *  FAT32
1585 		 */
1586 		FATSz = wbpb->bpb32.big_sectors_per_fat;
1587 		TotSec = wbpb->bpb.sectors_in_logical_volume;
1588 	}
1589 
1590 	DataSec = TotSec - (wbpb->bpb.resv_sectors +
1591 			(wbpb->bpb.num_fats * FATSz) +
1592 			RootDirSectors);
1593 
1594 
1595 	/*
1596 	 * Now change sectors to clusters
1597 	 */
1598 	TotalClusters = DataSec / wbpb->bpb.sectors_per_cluster;
1599 
1600 	if (Verbose)
1601 		(void) printf(gettext("Disk has a file area of %d "
1602 		    "allocation units,\neach with %d sectors = %d "
1603 		    "bytes.\n"), TotalClusters, wbpb->bpb.sectors_per_cluster,
1604 		    TotalClusters * wbpb->bpb.sectors_per_cluster * BPSEC);
1605 }
1606 
1607 #ifndef i386
1608 /*
1609  *  swap_pack_{bpb,bpb32,sebpb}cpy
1610  *
1611  *	If not on an x86 we assume the structures making up the bpb
1612  *	were not packed and that longs and shorts need to be byte swapped
1613  *	(we've kept everything in host order up until now).  A new architecture
1614  *	might not need to swap or might not need to pack, in which case
1615  *	new routines will have to be written.  Of course if an architecture
1616  *	supports both packing and little-endian host order, it can follow the
1617  *	same path as the x86 code.
1618  */
1619 static
1620 void
1621 swap_pack_bpbcpy(struct _boot_sector *bsp, bpb_t *wbpb)
1622 {
1623 	uchar_t *fillp;
1624 
1625 	fillp = (uchar_t *)&(bsp->bs_filler[ORIG_BPB_START_INDEX]);
1626 
1627 	store_16_bits(&fillp, wbpb->bpb.bytes_sector);
1628 	*fillp++ = wbpb->bpb.sectors_per_cluster;
1629 	store_16_bits(&fillp, wbpb->bpb.resv_sectors);
1630 	*fillp++ = wbpb->bpb.num_fats;
1631 	store_16_bits(&fillp, wbpb->bpb.num_root_entries);
1632 	store_16_bits(&fillp, wbpb->bpb.sectors_in_volume);
1633 	*fillp++ = wbpb->bpb.media;
1634 	store_16_bits(&fillp, wbpb->bpb.sectors_per_fat);
1635 	store_16_bits(&fillp, wbpb->bpb.sectors_per_track);
1636 	store_16_bits(&fillp, wbpb->bpb.heads);
1637 	store_32_bits(&fillp, wbpb->bpb.hidden_sectors);
1638 	store_32_bits(&fillp, wbpb->bpb.sectors_in_logical_volume);
1639 
1640 	*fillp++ = wbpb->ebpb.phys_drive_num;
1641 	*fillp++ = wbpb->ebpb.reserved;
1642 	*fillp++ = wbpb->ebpb.ext_signature;
1643 	store_32_bits(&fillp, wbpb->ebpb.volume_id);
1644 	(void) strncpy((char *)fillp, (char *)wbpb->ebpb.volume_label, 11);
1645 	fillp += 11;
1646 	(void) strncpy((char *)fillp, (char *)wbpb->ebpb.type, 8);
1647 }
1648 
1649 static
1650 void
1651 swap_pack_bpb32cpy(struct _boot_sector32 *bsp, bpb_t *wbpb)
1652 {
1653 	uchar_t *fillp;
1654 	int r;
1655 
1656 	fillp = (uchar_t *)&(bsp->bs_filler[ORIG_BPB_START_INDEX]);
1657 
1658 	store_16_bits(&fillp, wbpb->bpb.bytes_sector);
1659 	*fillp++ = wbpb->bpb.sectors_per_cluster;
1660 	store_16_bits(&fillp, wbpb->bpb.resv_sectors);
1661 	*fillp++ = wbpb->bpb.num_fats;
1662 	store_16_bits(&fillp, wbpb->bpb.num_root_entries);
1663 	store_16_bits(&fillp, wbpb->bpb.sectors_in_volume);
1664 	*fillp++ = wbpb->bpb.media;
1665 	store_16_bits(&fillp, wbpb->bpb.sectors_per_fat);
1666 	store_16_bits(&fillp, wbpb->bpb.sectors_per_track);
1667 	store_16_bits(&fillp, wbpb->bpb.heads);
1668 	store_32_bits(&fillp, wbpb->bpb.hidden_sectors);
1669 	store_32_bits(&fillp, wbpb->bpb.sectors_in_logical_volume);
1670 
1671 	store_32_bits(&fillp, wbpb->bpb32.big_sectors_per_fat);
1672 	store_16_bits(&fillp, wbpb->bpb32.ext_flags);
1673 	*fillp++ = wbpb->bpb32.fs_vers_lo;
1674 	*fillp++ = wbpb->bpb32.fs_vers_hi;
1675 	store_32_bits(&fillp, wbpb->bpb32.root_dir_clust);
1676 	store_16_bits(&fillp, wbpb->bpb32.fsinfosec);
1677 	store_16_bits(&fillp, wbpb->bpb32.backupboot);
1678 	for (r = 0; r < 6; r++)
1679 		store_16_bits(&fillp, wbpb->bpb32.reserved[r]);
1680 
1681 	*fillp++ = wbpb->ebpb.phys_drive_num;
1682 	*fillp++ = wbpb->ebpb.reserved;
1683 	*fillp++ = wbpb->ebpb.ext_signature;
1684 	store_32_bits(&fillp, wbpb->ebpb.volume_id);
1685 	(void) strncpy((char *)fillp, (char *)wbpb->ebpb.volume_label, 11);
1686 	fillp += 11;
1687 	(void) strncpy((char *)fillp, (char *)wbpb->ebpb.type, 8);
1688 }
1689 
1690 static
1691 void
1692 swap_pack_sebpbcpy(struct _boot_sector *bsp, bpb_t *wbpb)
1693 {
1694 	uchar_t *fillp;
1695 
1696 	fillp = bsp->bs_sun_bpb;
1697 	store_16_bits(&fillp, wbpb->sunbpb.bs_offset_high);
1698 	store_16_bits(&fillp, wbpb->sunbpb.bs_offset_low);
1699 }
1700 
1701 static
1702 void
1703 swap_pack_grabbpb(bpb_t *wbpb, struct _boot_sector *bsp)
1704 {
1705 	uchar_t *grabp;
1706 
1707 	grabp = (uchar_t *)&(bsp->bs_filler[ORIG_BPB_START_INDEX]);
1708 
1709 	((uchar_t *)&(wbpb->bpb.bytes_sector))[1] = *grabp++;
1710 	((uchar_t *)&(wbpb->bpb.bytes_sector))[0] = *grabp++;
1711 	wbpb->bpb.sectors_per_cluster = *grabp++;
1712 	((uchar_t *)&(wbpb->bpb.resv_sectors))[1] = *grabp++;
1713 	((uchar_t *)&(wbpb->bpb.resv_sectors))[0] = *grabp++;
1714 	wbpb->bpb.num_fats = *grabp++;
1715 	((uchar_t *)&(wbpb->bpb.num_root_entries))[1] = *grabp++;
1716 	((uchar_t *)&(wbpb->bpb.num_root_entries))[0] = *grabp++;
1717 	((uchar_t *)&(wbpb->bpb.sectors_in_volume))[1] = *grabp++;
1718 	((uchar_t *)&(wbpb->bpb.sectors_in_volume))[0] = *grabp++;
1719 	wbpb->bpb.media = *grabp++;
1720 	((uchar_t *)&(wbpb->bpb.sectors_per_fat))[1] = *grabp++;
1721 	((uchar_t *)&(wbpb->bpb.sectors_per_fat))[0] = *grabp++;
1722 	((uchar_t *)&(wbpb->bpb.sectors_per_track))[1] = *grabp++;
1723 	((uchar_t *)&(wbpb->bpb.sectors_per_track))[0] = *grabp++;
1724 	((uchar_t *)&(wbpb->bpb.heads))[1] = *grabp++;
1725 	((uchar_t *)&(wbpb->bpb.heads))[0] = *grabp++;
1726 	((uchar_t *)&(wbpb->bpb.hidden_sectors))[3] = *grabp++;
1727 	((uchar_t *)&(wbpb->bpb.hidden_sectors))[2] = *grabp++;
1728 	((uchar_t *)&(wbpb->bpb.hidden_sectors))[1] = *grabp++;
1729 	((uchar_t *)&(wbpb->bpb.hidden_sectors))[0] = *grabp++;
1730 	((uchar_t *)&(wbpb->bpb.sectors_in_logical_volume))[3] = *grabp++;
1731 	((uchar_t *)&(wbpb->bpb.sectors_in_logical_volume))[2] = *grabp++;
1732 	((uchar_t *)&(wbpb->bpb.sectors_in_logical_volume))[1] = *grabp++;
1733 	((uchar_t *)&(wbpb->bpb.sectors_in_logical_volume))[0] = *grabp++;
1734 	wbpb->ebpb.phys_drive_num = *grabp++;
1735 	wbpb->ebpb.reserved = *grabp++;
1736 	wbpb->ebpb.ext_signature = *grabp++;
1737 	((uchar_t *)&(wbpb->ebpb.volume_id))[3] = *grabp++;
1738 	((uchar_t *)&(wbpb->ebpb.volume_id))[2] = *grabp++;
1739 	((uchar_t *)&(wbpb->ebpb.volume_id))[1] = *grabp++;
1740 	((uchar_t *)&(wbpb->ebpb.volume_id))[0] = *grabp++;
1741 
1742 	(void) strncpy((char *)wbpb->ebpb.volume_label, (char *)grabp, 11);
1743 	grabp += 11;
1744 	(void) strncpy((char *)wbpb->ebpb.type, (char *)grabp, 8);
1745 }
1746 
1747 static
1748 void
1749 swap_pack_grabsebpb(bpb_t *wbpb, struct _boot_sector *bsp)
1750 {
1751 	uchar_t *grabp;
1752 
1753 	grabp = bsp->bs_sun_bpb;
1754 	((uchar_t *)&(wbpb->sunbpb.bs_offset_high))[1] = *grabp++;
1755 	((uchar_t *)&(wbpb->sunbpb.bs_offset_high))[0] = *grabp++;
1756 	((uchar_t *)&(wbpb->sunbpb.bs_offset_low))[1] = *grabp++;
1757 	((uchar_t *)&(wbpb->sunbpb.bs_offset_low))[0] = *grabp++;
1758 }
1759 
1760 static
1761 void
1762 swap_pack_grab32bpb(bpb_t *wbpb, struct _boot_sector *bsp)
1763 {
1764 	uchar_t *grabp;
1765 
1766 	grabp = (uchar_t *)&(bsp->bs_filler[BPB_32_START_INDEX]);
1767 
1768 	((uchar_t *)&(wbpb->bpb32.big_sectors_per_fat))[3] = *grabp++;
1769 	((uchar_t *)&(wbpb->bpb32.big_sectors_per_fat))[2] = *grabp++;
1770 	((uchar_t *)&(wbpb->bpb32.big_sectors_per_fat))[1] = *grabp++;
1771 	((uchar_t *)&(wbpb->bpb32.big_sectors_per_fat))[0] = *grabp++;
1772 	((uchar_t *)&(wbpb->bpb32.ext_flags))[1] = *grabp++;
1773 	((uchar_t *)&(wbpb->bpb32.ext_flags))[0] = *grabp++;
1774 	wbpb->bpb32.fs_vers_lo = *grabp++;
1775 	wbpb->bpb32.fs_vers_hi = *grabp++;
1776 	((uchar_t *)&(wbpb->bpb32.root_dir_clust))[3] = *grabp++;
1777 	((uchar_t *)&(wbpb->bpb32.root_dir_clust))[2] = *grabp++;
1778 	((uchar_t *)&(wbpb->bpb32.root_dir_clust))[1] = *grabp++;
1779 	((uchar_t *)&(wbpb->bpb32.root_dir_clust))[0] = *grabp++;
1780 	((uchar_t *)&(wbpb->bpb32.fsinfosec))[1] = *grabp++;
1781 	((uchar_t *)&(wbpb->bpb32.fsinfosec))[0] = *grabp++;
1782 	((uchar_t *)&(wbpb->bpb32.backupboot))[1] = *grabp++;
1783 	((uchar_t *)&(wbpb->bpb32.backupboot))[0] = *grabp++;
1784 	((uchar_t *)&(wbpb->bpb32.reserved[0]))[1] = *grabp++;
1785 	((uchar_t *)&(wbpb->bpb32.reserved[0]))[0] = *grabp++;
1786 	((uchar_t *)&(wbpb->bpb32.reserved[1]))[1] = *grabp++;
1787 	((uchar_t *)&(wbpb->bpb32.reserved[1]))[0] = *grabp++;
1788 	((uchar_t *)&(wbpb->bpb32.reserved[2]))[1] = *grabp++;
1789 	((uchar_t *)&(wbpb->bpb32.reserved[2]))[0] = *grabp++;
1790 	((uchar_t *)&(wbpb->bpb32.reserved[3]))[1] = *grabp++;
1791 	((uchar_t *)&(wbpb->bpb32.reserved[3]))[0] = *grabp++;
1792 	((uchar_t *)&(wbpb->bpb32.reserved[4]))[1] = *grabp++;
1793 	((uchar_t *)&(wbpb->bpb32.reserved[4]))[0] = *grabp++;
1794 	((uchar_t *)&(wbpb->bpb32.reserved[5]))[1] = *grabp++;
1795 	((uchar_t *)&(wbpb->bpb32.reserved[5]))[0] = *grabp++;
1796 }
1797 #endif	/* ! i386 */
1798 
1799 static
1800 void
1801 dashm_bail(int fd)
1802 {
1803 	(void) fprintf(stderr,
1804 		gettext("This media does not appear to be "
1805 			"formatted with a FAT file system.\n"));
1806 	(void) close(fd);
1807 	exit(6);
1808 }
1809 
1810 /*
1811  *  read_existing_bpb
1812  *
1813  *	Grab the first sector, which we think is a bios parameter block.
1814  *	If it looks bad, bail.  Otherwise fill in the parameter struct
1815  *	fields that matter.
1816  */
1817 static
1818 void
1819 read_existing_bpb(int fd, bpb_t *wbpb)
1820 {
1821 	boot_sector_t ubpb;
1822 
1823 	if (read(fd, ubpb.buf, BPSEC) < BPSEC) {
1824 		perror(gettext("Read BIOS parameter block "
1825 			"from previously formatted media"));
1826 		(void) close(fd);
1827 		exit(6);
1828 	}
1829 
1830 	if (ltohs(ubpb.mb.signature) != BOOTSECSIG) {
1831 		dashm_bail(fd);
1832 	}
1833 
1834 #ifdef i386
1835 	(void) memcpy(&(wbpb->bpb), &(ubpb.bs.bs_front.bs_bpb),
1836 		sizeof (wbpb->bpb));
1837 	(void) memcpy(&(wbpb->ebpb), &(ubpb.bs.bs_ebpb), sizeof (wbpb->ebpb));
1838 #else
1839 	swap_pack_grabbpb(wbpb, &(ubpb.bs));
1840 #endif
1841 	if (SunBPBfields) {
1842 #ifdef i386
1843 		(void) memcpy(&(wbpb->sunbpb), &(ubpb.bs.bs_sebpb),
1844 			sizeof (wbpb->sunbpb));
1845 #else
1846 		swap_pack_grabsebpb(wbpb, &(ubpb.bs));
1847 #endif
1848 	}
1849 	if (wbpb->bpb.bytes_sector != BPSEC) {
1850 		(void) fprintf(stderr,
1851 		    gettext("Bogus bytes per sector value.\n"));
1852 		if (!powerofx_le_y(2, BPSEC * 8, wbpb->bpb.bytes_sector)) {
1853 			(void) fprintf(stderr,
1854 			    gettext("The device name may be missing a "
1855 				    "logical drive specifier.\n"));
1856 			(void) close(fd);
1857 			exit(6);
1858 		} else {
1859 			(void) fprintf(stderr,
1860 			    gettext("Do not know how to build FATs with a\n"
1861 				    "non-standard sector size. Standard "
1862 				    "size is %d bytes,\nyour sector size "
1863 				    "is %d bytes.\n"), BPSEC,
1864 				    wbpb->bpb.bytes_sector);
1865 			(void) close(fd);
1866 			exit(6);
1867 		}
1868 	}
1869 	if (!(powerofx_le_y(2, 128, wbpb->bpb.sectors_per_cluster))) {
1870 		(void) fprintf(stderr,
1871 		    gettext("Bogus sectors per cluster value.\n"));
1872 		(void) fprintf(stderr,
1873 		    gettext("The device name may be missing a "
1874 			"logical drive specifier.\n"));
1875 		(void) close(fd);
1876 		exit(6);
1877 	}
1878 
1879 	if (wbpb->bpb.sectors_per_fat == 0) {
1880 #ifdef i386
1881 		(void) memcpy(&(wbpb->bpb32), &(ubpb.bs32.bs_bpb32),
1882 			sizeof (wbpb->bpb32));
1883 #else
1884 		swap_pack_grab32bpb(wbpb, &(ubpb.bs));
1885 #endif
1886 		compute_file_area_size(wbpb);
1887 		if ((wbpb->bpb32.big_sectors_per_fat * BPSEC / 4) >=
1888 		    TotalClusters) {
1889 			MakeFAT32 = 1;
1890 		} else {
1891 			dashm_bail(fd);
1892 		}
1893 	} else {
1894 		compute_file_area_size(wbpb);
1895 	}
1896 }
1897 
1898 /*
1899  *  compare_existing_with_computed
1900  *
1901  *	We use this function when we the user specifies the -m option.
1902  *	We compute and look up things like we would if they had asked
1903  *	us to make the fs, and compare that to what's already layed down
1904  *	in the existing fs.  If there's a difference we can tell them what
1905  *	options to specify in order to reproduce their existing layout.
1906  *	Note that they still may not get an exact duplicate, because we
1907  *	don't, for example, preserve their existing boot code.  We think
1908  *	we've got all the fields that matter covered, though.
1909  *
1910  *	XXX - We're basically ignoring sbpb at this point.  I'm unsure
1911  *	if we'll ever care about those fields, in terms of the -m option.
1912  */
1913 static
1914 void
1915 compare_existing_with_computed(int fd, char *suffix,
1916     bpb_t *wbpb, int *prtsize, int *prtspc, int *prtbpf, int *prtnsect,
1917     int *prtntrk, int *prtfdisk, int *prthidden, int *prtrsrvd, int *dashos)
1918 {
1919 	struct dk_geom	dginfo;
1920 	struct fd_char	fdchar;
1921 	bpb_t		compare;
1922 	int		fd_ioctl_worked = 0;
1923 	int		fatents;
1924 
1925 	/*
1926 	 *  For all non-floppy cases we expect to find a 16-bit FAT
1927 	 */
1928 	int expectfatsize = 16;
1929 
1930 	compare = *wbpb;
1931 
1932 	if (!suffix) {
1933 		if (ioctl(fd, FDIOGCHAR, &fdchar) != -1) {
1934 			expectfatsize = 12;
1935 			fd_ioctl_worked++;
1936 		}
1937 	}
1938 
1939 	if (fd_ioctl_worked) {
1940 #ifdef sparc
1941 		fdchar.fdc_medium = 3;
1942 #endif
1943 		GetSize = GetSPT = GetSPC = GetTPC = GetBPF = 1;
1944 		lookup_floppy(&fdchar, &compare);
1945 		if (compare.bpb.heads != wbpb->bpb.heads) {
1946 			(*prtntrk)++;
1947 			(*dashos)++;
1948 		}
1949 		if (compare.bpb.sectors_per_track !=
1950 		    wbpb->bpb.sectors_per_track) {
1951 			(*prtnsect)++;
1952 			(*dashos)++;
1953 		}
1954 	} else {
1955 		int dk_ioctl_worked = 1;
1956 
1957 		if (!suffix) {
1958 			(*prtfdisk)++;
1959 			(*prtsize)++;
1960 			*dashos += 2;
1961 		}
1962 		if ((ioctl(fd, DKIOCG_VIRTGEOM, &dginfo)) == -1) {
1963 		    if ((ioctl(fd, DKIOCG_PHYGEOM, &dginfo)) == -1) {
1964 			if ((ioctl(fd, DKIOCGGEOM, &dginfo)) == -1) {
1965 				*prtnsect = *prtntrk = 1;
1966 				*dashos += 2;
1967 				dk_ioctl_worked = 0;
1968 			}
1969 		    }
1970 		}
1971 		if (dk_ioctl_worked) {
1972 			if (dginfo.dkg_nhead != wbpb->bpb.heads) {
1973 				(*prtntrk)++;
1974 				(*dashos)++;
1975 			}
1976 			if (dginfo.dkg_nsect !=
1977 			    wbpb->bpb.sectors_per_track) {
1978 				(*prtnsect)++;
1979 				(*dashos)++;
1980 			}
1981 		}
1982 		GetBPF = GetSPC = 1;
1983 		compute_cluster_size(&compare);
1984 	}
1985 
1986 	if (!*prtfdisk && TotSize != wbpb->bpb.sectors_in_volume &&
1987 		TotSize != wbpb->bpb.sectors_in_logical_volume) {
1988 		(*dashos)++;
1989 		(*prtsize)++;
1990 	}
1991 
1992 	if (compare.bpb.sectors_per_cluster != wbpb->bpb.sectors_per_cluster) {
1993 		(*dashos)++;
1994 		(*prtspc)++;
1995 	}
1996 
1997 	if (compare.bpb.hidden_sectors != wbpb->bpb.hidden_sectors) {
1998 		(*dashos)++;
1999 		(*prthidden)++;
2000 	}
2001 
2002 	if (compare.bpb.resv_sectors != wbpb->bpb.resv_sectors) {
2003 		(*dashos)++;
2004 		(*prtrsrvd)++;
2005 	}
2006 
2007 	/*
2008 	 * Compute approximate Fatentsize.  It's approximate because the
2009 	 * size of the FAT may not be exactly a multiple of the number of
2010 	 * clusters.  It should be close, though.
2011 	 */
2012 	if (MakeFAT32) {
2013 		Fatentsize = 32;
2014 		(*dashos)++;
2015 		(*prtbpf)++;
2016 	} else {
2017 		fatents = wbpb->bpb.sectors_per_fat * BPSEC * 2 / 3;
2018 		if (fatents >= TotalClusters && wbpb->ebpb.type[4] == '2')
2019 			Fatentsize = 12;
2020 		else
2021 			Fatentsize = 16;
2022 		if (Fatentsize != expectfatsize) {
2023 			(*dashos)++;
2024 			(*prtbpf)++;
2025 		}
2026 	}
2027 }
2028 
2029 static
2030 void
2031 print_reproducing_command(int fd, char *actualdisk, char *suffix, bpb_t *wbpb)
2032 {
2033 	int needcomma = 0;
2034 	int prthidden = 0;
2035 	int prtrsrvd = 0;
2036 	int prtfdisk = 0;
2037 	int prtnsect = 0;
2038 	int prtntrk = 0;
2039 	int prtsize = 0;
2040 	int prtbpf = 0;
2041 	int prtspc = 0;
2042 	int dashos = 0;
2043 	int ll, i;
2044 
2045 	compare_existing_with_computed(fd, suffix, wbpb,
2046 	    &prtsize, &prtspc, &prtbpf, &prtnsect, &prtntrk,
2047 	    &prtfdisk, &prthidden, &prtrsrvd, &dashos);
2048 
2049 	/*
2050 	 *  Print out the command line they can use to reproduce the
2051 	 *  file system.
2052 	 */
2053 	(void) printf("mkfs -F pcfs");
2054 
2055 	ll = min(11, (int)strlen((char *)wbpb->ebpb.volume_label));
2056 	/*
2057 	 * First, eliminate trailing spaces. Now compare the name against
2058 	 * our default label.  If there's a match we don't need to print
2059 	 * any label info.
2060 	 */
2061 	i = ll;
2062 	while (wbpb->ebpb.volume_label[--i] == ' ');
2063 	ll = i;
2064 
2065 	if (ll == strlen(DEFAULT_LABEL) - 1) {
2066 		char cmpbuf[11];
2067 
2068 		(void) strcpy(cmpbuf, DEFAULT_LABEL);
2069 		for (i = ll; i >= 0; i--) {
2070 			if (cmpbuf[i] !=
2071 			    toupper((int)(wbpb->ebpb.volume_label[i]))) {
2072 				break;
2073 			}
2074 		}
2075 		if (i < 0)
2076 			ll = i;
2077 	}
2078 
2079 	if (ll >= 0) {
2080 		(void) printf(" -o ");
2081 		(void) printf("b=\"");
2082 		for (i = 0; i <= ll; i++) {
2083 			(void) printf("%c", wbpb->ebpb.volume_label[i]);
2084 		}
2085 		(void) printf("\"");
2086 		needcomma++;
2087 	} else if (dashos) {
2088 		(void) printf(" -o ");
2089 	}
2090 
2091 #define	NEXT_DASH_O	dashos--; needcomma++; continue
2092 
2093 	while (dashos) {
2094 		if (needcomma) {
2095 			(void) printf(",");
2096 			needcomma = 0;
2097 		}
2098 		if (prtfdisk) {
2099 			(void) printf("nofdisk");
2100 			prtfdisk--;
2101 			NEXT_DASH_O;
2102 		}
2103 		if (prtsize) {
2104 			(void) printf("size=%u", wbpb->bpb.sectors_in_volume ?
2105 			    wbpb->bpb.sectors_in_volume :
2106 			    wbpb->bpb.sectors_in_logical_volume);
2107 			prtsize--;
2108 			NEXT_DASH_O;
2109 		}
2110 		if (prtnsect) {
2111 			(void) printf("nsect=%d", wbpb->bpb.sectors_per_track);
2112 			prtnsect--;
2113 			NEXT_DASH_O;
2114 		}
2115 		if (prtspc) {
2116 			(void) printf("spc=%d", wbpb->bpb.sectors_per_cluster);
2117 			prtspc--;
2118 			NEXT_DASH_O;
2119 		}
2120 		if (prtntrk) {
2121 			(void) printf("ntrack=%d", wbpb->bpb.heads);
2122 			prtntrk--;
2123 			NEXT_DASH_O;
2124 		}
2125 		if (prtbpf) {
2126 			(void) printf("fat=%d", Fatentsize);
2127 			prtbpf--;
2128 			NEXT_DASH_O;
2129 		}
2130 		if (prthidden) {
2131 			(void) printf("hidden=%u", wbpb->bpb.hidden_sectors);
2132 			prthidden--;
2133 			NEXT_DASH_O;
2134 		}
2135 		if (prtrsrvd) {
2136 			(void) printf("reserve=%d", wbpb->bpb.resv_sectors);
2137 			prtrsrvd--;
2138 			NEXT_DASH_O;
2139 		}
2140 	}
2141 
2142 	(void) printf(" %s%c%c\n", actualdisk,
2143 	    suffix ? ':' : '\0', suffix ? *suffix : '\0');
2144 }
2145 
2146 /*
2147  *  open_and_examine
2148  *
2149  *	Open the requested 'dev_name'.  Seek to point where
2150  *	we'd expect to find boot sectors, etc., based on any ':partition'
2151  *	attachments to the dev_name.
2152  *
2153  *	Examine the fields of any existing boot sector and display best
2154  *	approximation of how this fs could be reproduced with this command.
2155  */
2156 static
2157 int
2158 open_and_examine(char *dn, bpb_t *wbpb)
2159 {
2160 	struct stat di;
2161 	off64_t ignored;
2162 	char *actualdisk = NULL;
2163 	char *suffix = NULL;
2164 	int fd;
2165 
2166 	if (Verbose)
2167 		(void) printf(gettext("Opening destination device/file.\n"));
2168 
2169 	actualdisk = stat_actual_disk(dn, &di, &suffix);
2170 
2171 	/*
2172 	 *  Destination exists, now find more about it.
2173 	 */
2174 	if (!(S_ISCHR(di.st_mode))) {
2175 		(void) fprintf(stderr,
2176 		    gettext("\n%s: device name must be a "
2177 			"character special device.\n"), actualdisk);
2178 		exit(2);
2179 	} else if ((fd = open(actualdisk, O_RDWR | O_EXCL)) < 0) {
2180 		perror(actualdisk);
2181 		exit(2);
2182 	}
2183 
2184 	/*
2185 	 * Find appropriate partition if we were requested to do so.
2186 	 */
2187 	if (suffix && !(seek_partn(fd, suffix, wbpb, &ignored))) {
2188 		(void) close(fd);
2189 		exit(2);
2190 	}
2191 
2192 	read_existing_bpb(fd, wbpb);
2193 	print_reproducing_command(fd, actualdisk, suffix, wbpb);
2194 
2195 	return (fd);
2196 }
2197 
2198 /*
2199  *  open_and_seek
2200  *
2201  *	Open the requested 'dev_name'.  Seek to point where
2202  *	we'll write boot sectors, etc., based on any ':partition'
2203  *	attachments to the dev_name.
2204  *
2205  *	By the time we are finished here, the entire BPB will be
2206  *	filled in, excepting the volume label.
2207  */
2208 static
2209 int
2210 open_and_seek(char *dn, bpb_t *wbpb, off64_t *seekto)
2211 {
2212 	struct fd_char fdchar;
2213 	struct dk_geom dg;
2214 	struct stat di;
2215 	char *actualdisk = NULL;
2216 	char *suffix = NULL;
2217 	int fd;
2218 
2219 	if (Verbose)
2220 		(void) printf(gettext("Opening destination device/file.\n"));
2221 
2222 	/*
2223 	 * We hold these truths to be self evident, all BPBs we create
2224 	 * will have these values in these fields.
2225 	 */
2226 	wbpb->bpb.num_fats = 2;
2227 	wbpb->bpb.bytes_sector = BPSEC;
2228 
2229 	/*
2230 	 * Assign or use supplied numbers for hidden and
2231 	 * reserved sectors in the file system.
2232 	 */
2233 	if (GetResrvd)
2234 		if (MakeFAT32)
2235 			wbpb->bpb.resv_sectors = 32;
2236 		else
2237 			wbpb->bpb.resv_sectors = 1;
2238 	else
2239 		wbpb->bpb.resv_sectors = Resrvd;
2240 
2241 	wbpb->ebpb.ext_signature = 0x29; /* Magic number for modern format */
2242 	wbpb->ebpb.volume_id = 0;
2243 
2244 	if (MakeFAT32)
2245 		fill_fat32_bpb(wbpb);
2246 
2247 	/*
2248 	 * If all output goes to a simple file, call a routine to setup
2249 	 * that scenario. Otherwise, try to find the device.
2250 	 */
2251 	if (Outputtofile)
2252 		return (fd = prepare_image_file(dn, wbpb));
2253 
2254 	actualdisk = stat_actual_disk(dn, &di, &suffix);
2255 
2256 	/*
2257 	 * Sanity check.  If we've been provided a partition-specifying
2258 	 * suffix, we shouldn't also have been told to ignore the
2259 	 * fdisk table.
2260 	 */
2261 	if (DontUseFdisk && suffix) {
2262 		(void) fprintf(stderr,
2263 		    gettext("Using 'nofdisk' option precludes "
2264 			    "appending logical drive\nspecifier "
2265 			    "to the device name.\n"));
2266 		exit(2);
2267 	}
2268 
2269 	/*
2270 	 *  Destination exists, now find more about it.
2271 	 */
2272 	if (!(S_ISCHR(di.st_mode))) {
2273 		(void) fprintf(stderr,
2274 		    gettext("\n%s: device name must indicate a "
2275 			"character special device.\n"), actualdisk);
2276 		exit(2);
2277 	} else if ((fd = open(actualdisk, O_RDWR | O_EXCL)) < 0) {
2278 		perror(actualdisk);
2279 		exit(2);
2280 	}
2281 
2282 	/*
2283 	 * Find appropriate partition if we were requested to do so.
2284 	 */
2285 	if (suffix && !(seek_partn(fd, suffix, wbpb, seekto))) {
2286 		(void) close(fd);
2287 		exit(2);
2288 	}
2289 
2290 	if (!suffix) {
2291 		/*
2292 		 * We have one of two possibilities.  Chances are we have
2293 		 * a floppy drive.  But the user may be trying to format
2294 		 * some weird drive that we don't know about and is supplying
2295 		 * all the important values.  In that case, they should have set
2296 		 * the 'nofdisk' flag.
2297 		 *
2298 		 * If 'nofdisk' isn't set, do a floppy-specific ioctl to
2299 		 * get the remainder of our info. If the ioctl fails, we have
2300 		 * a good idea that they aren't really on a floppy.  In that
2301 		 * case, they should have given us a partition specifier.
2302 		 */
2303 		if (DontUseFdisk) {
2304 			if (!(seek_nofdisk(fd, wbpb, seekto))) {
2305 				(void) close(fd);
2306 				exit(2);
2307 			}
2308 			find_fixed_details(fd, wbpb);
2309 		} else if (ioctl(fd, FDIOGCHAR, &fdchar) == -1) {
2310 			/*
2311 			 * It is possible that we are trying to use floppy
2312 			 * specific FDIOGCHAR ioctl on USB floppy. Since sd
2313 			 * driver, by which USB floppy is handled, doesn't
2314 			 * support it, we can try to use disk DKIOCGGEOM ioctl
2315 			 * to retrieve data we need. sd driver itself
2316 			 * determines floppy disk by number of blocks
2317 			 * (<=0x1000), then it sets geometry to 80 cylinders,
2318 			 * 2 heads.
2319 			 *
2320 			 * Note that DKIOCGGEOM cannot supply us with type
2321 			 * of media (e.g. 3.5" or 5.25"). We will set it to
2322 			 * 3 (3.5") which is most probable value.
2323 			 */
2324 			if (errno == ENOTTY) {
2325 				if (ioctl(fd, DKIOCGGEOM, &dg) != -1 &&
2326 				    dg.dkg_ncyl == 80 && dg.dkg_nhead == 2) {
2327 					fdchar.fdc_ncyl = dg.dkg_ncyl;
2328 					fdchar.fdc_medium = 3;
2329 					fdchar.fdc_secptrack = dg.dkg_nsect;
2330 					fdchar.fdc_nhead = dg.dkg_nhead;
2331 					lookup_floppy(&fdchar, wbpb);
2332 				} else {
2333 					partn_lecture(actualdisk);
2334 					(void) close(fd);
2335 					exit(2);
2336 				}
2337 			}
2338 		} else {
2339 #ifdef sparc
2340 			fdchar.fdc_medium = 3;
2341 #endif
2342 			lookup_floppy(&fdchar, wbpb);
2343 		}
2344 	} else {
2345 		find_fixed_details(fd, wbpb);
2346 	}
2347 
2348 	return (fd);
2349 }
2350 
2351 /*
2352  * The following is a copy of MS-DOS 4.0 boot block.
2353  * It consists of the BIOS parameter block, and a disk
2354  * bootstrap program.
2355  *
2356  * The BIOS parameter block contains the right values
2357  * for the 3.5" high-density 1.44MB floppy format.
2358  *
2359  * This will be our default boot sector, if the user
2360  * didn't point us at a different one.
2361  *
2362  */
2363 static
2364 uchar_t DefBootSec[512] = {
2365 	0xeb, 0x3c, 0x90, 	/* 8086 short jump + displacement + NOP */
2366 	'M', 'S', 'D', 'O', 'S', '4', '.', '0',	/* OEM name & version */
2367 	0x00, 0x02, 0x01, 0x01, 0x00,
2368 	0x02, 0xe0, 0x00, 0x40, 0x0b,
2369 	0xf0, 0x09, 0x00, 0x12, 0x00,
2370 	0x02, 0x00,
2371 	0x00, 0x00, 0x00, 0x00,
2372 	0x00, 0x00, 0x00, 0x00,
2373 	0x00, 0x00,
2374 	0x29, 0x00, 0x00, 0x00, 0x00,
2375 	'N', 'O', 'N', 'A', 'M', 'E', ' ', ' ', ' ', ' ', ' ',
2376 	'F', 'A', 'T', '1', '2', ' ', ' ', ' ',
2377 	0xfa, 0x33,
2378 	0xc0, 0x8e, 0xd0, 0xbc, 0x00, 0x7c, 0x16, 0x07,
2379 	0xbb, 0x78, 0x00, 0x36, 0xc5, 0x37, 0x1e, 0x56,
2380 	0x16, 0x53, 0xbf, 0x3e, 0x7c, 0xb9, 0x0b, 0x00,
2381 	0xfc, 0xf3, 0xa4, 0x06, 0x1f, 0xc6, 0x45, 0xfe,
2382 	0x0f, 0x8b, 0x0e, 0x18, 0x7c, 0x88, 0x4d, 0xf9,
2383 	0x89, 0x47, 0x02, 0xc7, 0x07, 0x3e, 0x7c, 0xfb,
2384 	0xcd, 0x13, 0x72, 0x7c, 0x33, 0xc0, 0x39, 0x06,
2385 	0x13, 0x7c, 0x74, 0x08, 0x8b, 0x0e, 0x13, 0x7c,
2386 	0x89, 0x0e, 0x20, 0x7c, 0xa0, 0x10, 0x7c, 0xf7,
2387 	0x26, 0x16, 0x7c, 0x03, 0x06, 0x1c, 0x7c, 0x13,
2388 	0x16, 0x1e, 0x7c, 0x03, 0x06, 0x0e, 0x7c, 0x83,
2389 	0xd2, 0x00, 0xa3, 0x50, 0x7c, 0x89, 0x16, 0x52,
2390 	0x7c, 0xa3, 0x49, 0x7c, 0x89, 0x16, 0x4b, 0x7c,
2391 	0xb8, 0x20, 0x00, 0xf7, 0x26, 0x11, 0x7c, 0x8b,
2392 	0x1e, 0x0b, 0x7c, 0x03, 0xc3, 0x48, 0xf7, 0xf3,
2393 	0x01, 0x06, 0x49, 0x7c, 0x83, 0x16, 0x4b, 0x7c,
2394 	0x00, 0xbb, 0x00, 0x05, 0x8b, 0x16, 0x52, 0x7c,
2395 	0xa1, 0x50, 0x7c, 0xe8, 0x87, 0x00, 0x72, 0x20,
2396 	0xb0, 0x01, 0xe8, 0xa1, 0x00, 0x72, 0x19, 0x8b,
2397 	0xfb, 0xb9, 0x0b, 0x00, 0xbe, 0xdb, 0x7d, 0xf3,
2398 	0xa6, 0x75, 0x0d, 0x8d, 0x7f, 0x20, 0xbe, 0xe6,
2399 	0x7d, 0xb9, 0x0b, 0x00, 0xf3, 0xa6, 0x74, 0x18,
2400 	0xbe, 0x93, 0x7d, 0xe8, 0x51, 0x00, 0x32, 0xe4,
2401 	0xcd, 0x16, 0x5e, 0x1f, 0x8f, 0x04, 0x8f, 0x44,
2402 	0x02, 0xcd, 0x19, 0x58, 0x58, 0x58, 0xeb, 0xe8,
2403 	0xbb, 0x00, 0x07, 0xb9, 0x03, 0x00, 0xa1, 0x49,
2404 	0x7c, 0x8b, 0x16, 0x4b, 0x7c, 0x50, 0x52, 0x51,
2405 	0xe8, 0x3a, 0x00, 0x72, 0xe6, 0xb0, 0x01, 0xe8,
2406 	0x54, 0x00, 0x59, 0x5a, 0x58, 0x72, 0xc9, 0x05,
2407 	0x01, 0x00, 0x83, 0xd2, 0x00, 0x03, 0x1e, 0x0b,
2408 	0x7c, 0xe2, 0xe2, 0x8a, 0x2e, 0x15, 0x7c, 0x8a,
2409 	0x16, 0x24, 0x7c, 0x8b, 0x1e, 0x49, 0x7c, 0xa1,
2410 	0x4b, 0x7c, 0xea, 0x00, 0x00, 0x70, 0x00, 0xac,
2411 	0x0a, 0xc0, 0x74, 0x29, 0xb4, 0x0e, 0xbb, 0x07,
2412 	0x00, 0xcd, 0x10, 0xeb, 0xf2, 0x3b, 0x16, 0x18,
2413 	0x7c, 0x73, 0x19, 0xf7, 0x36, 0x18, 0x7c, 0xfe,
2414 	0xc2, 0x88, 0x16, 0x4f, 0x7c, 0x33, 0xd2, 0xf7,
2415 	0x36, 0x1a, 0x7c, 0x88, 0x16, 0x25, 0x7c, 0xa3,
2416 	0x4d, 0x7c, 0xf8, 0xc3, 0xf9, 0xc3, 0xb4, 0x02,
2417 	0x8b, 0x16, 0x4d, 0x7c, 0xb1, 0x06, 0xd2, 0xe6,
2418 	0x0a, 0x36, 0x4f, 0x7c, 0x8b, 0xca, 0x86, 0xe9,
2419 	0x8a, 0x16, 0x24, 0x7c, 0x8a, 0x36, 0x25, 0x7c,
2420 	0xcd, 0x13, 0xc3, 0x0d, 0x0a, 0x4e, 0x6f, 0x6e,
2421 	0x2d, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20,
2422 	0x64, 0x69, 0x73, 0x6b, 0x20, 0x6f, 0x72, 0x20,
2423 	0x64, 0x69, 0x73, 0x6b, 0x20, 0x65, 0x72, 0x72,
2424 	0x6f, 0x72, 0x0d, 0x0a, 0x52, 0x65, 0x70, 0x6c,
2425 	0x61, 0x63, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20,
2426 	0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e,
2427 	0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x77, 0x68,
2428 	0x65, 0x6e, 0x20, 0x72, 0x65, 0x61, 0x64, 0x79,
2429 	0x0d, 0x0a, 0x00, 0x49, 0x4f, 0x20, 0x20, 0x20,
2430 	0x20, 0x20, 0x20, 0x53, 0x59, 0x53, 0x4d, 0x53,
2431 	0x44, 0x4f, 0x53, 0x20, 0x20, 0x20, 0x53, 0x59,
2432 	0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2433 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
2434 };
2435 
2436 /*
2437  *  verify_bootblkfile
2438  *
2439  *	We were provided with the name of a file containing the bootblk
2440  *	to install.  Verify it has a valid boot sector as best we can. Any
2441  *	errors and we return a bad file descriptor.  Otherwise we fill up the
2442  *	provided buffer with the boot sector, return the file
2443  *	descriptor for later use and leave the file pointer just
2444  *	past the boot sector part of the boot block file.
2445  */
2446 static
2447 int
2448 verify_bootblkfile(char *fn, boot_sector_t *bs, ulong_t *blkfilesize)
2449 {
2450 	struct stat fi;
2451 	int bsfd = -1;
2452 
2453 	if (stat(fn, &fi)) {
2454 		perror(fn);
2455 	} else if (fi.st_size < BPSEC) {
2456 		(void) fprintf(stderr,
2457 		    gettext("%s: Too short to be a boot sector.\n"), fn);
2458 	} else if ((bsfd = open(fn, O_RDONLY)) < 0) {
2459 		perror(fn);
2460 	} else if (read(bsfd, bs->buf, BPSEC) < BPSEC) {
2461 		(void) close(bsfd);
2462 		bsfd = -1;
2463 		perror(gettext("Boot block read"));
2464 	} else {
2465 #ifdef i386
2466 		if ((bs->bs.bs_front.bs_jump_code[0] != OPCODE1 &&
2467 		    bs->bs.bs_front.bs_jump_code[0] != OPCODE2) ||
2468 #else
2469 		if ((bs->bs.bs_jump_code[0] != OPCODE1 &&
2470 		    bs->bs.bs_jump_code[0] != OPCODE2) ||
2471 #endif
2472 		    (bs->bs.bs_signature[0] != (BOOTSECSIG & 0xFF) &&
2473 		    bs->bs.bs_signature[1] != ((BOOTSECSIG >> 8) & 0xFF))) {
2474 			(void) close(bsfd);
2475 			bsfd = -1;
2476 			(void) fprintf(stderr,
2477 			    gettext("Boot block (%s) bogus.\n"), fn);
2478 		}
2479 		*blkfilesize = fi.st_size;
2480 	}
2481 	return (bsfd);
2482 }
2483 
2484 /*
2485  *  verify_firstfile
2486  *
2487  *	We were provided with the name of a file to be the first file
2488  *	installed on the disk.  We just need to verify it exists and
2489  *	find out how big it is.  If it doesn't exist, we print a warning
2490  *	message about how the file wasn't found.  We don't exit fatally,
2491  *	though, rather we return a size of 0 and the FAT will be built
2492  *	without installing any first file.  They can then presumably
2493  *	install the correct first file by hand.
2494  */
2495 static
2496 int
2497 verify_firstfile(char *fn, ulong_t *filesize)
2498 {
2499 	struct stat fi;
2500 	int fd = -1;
2501 
2502 	*filesize = 0;
2503 	if (stat(fn, &fi) || (fd = open(fn, O_RDONLY)) < 0) {
2504 		perror(fn);
2505 		(void) fprintf(stderr,
2506 		    gettext("Could not access requested file.  It will not\n"
2507 			    "be installed in the new file system.\n"));
2508 	} else {
2509 		*filesize = fi.st_size;
2510 	}
2511 
2512 	return (fd);
2513 }
2514 
2515 /*
2516  *  label_volume
2517  *
2518  *	Fill in BPB with volume label.
2519  */
2520 static
2521 void
2522 label_volume(char *lbl, bpb_t *wbpb)
2523 {
2524 	int ll, i;
2525 
2526 	/* Put a volume label into our BPB. */
2527 	if (!lbl)
2528 		lbl = DEFAULT_LABEL;
2529 
2530 	ll = min(11, (int)strlen(lbl));
2531 	for (i = 0; i < ll; i++) {
2532 		wbpb->ebpb.volume_label[i] = toupper(lbl[i]);
2533 	}
2534 	for (; i < 11; i++) {
2535 		wbpb->ebpb.volume_label[i] = ' ';
2536 	}
2537 }
2538 
2539 static
2540 int
2541 copy_bootblk(char *fn, boot_sector_t *bootsect, ulong_t *bootblksize)
2542 {
2543 	int bsfd = -1;
2544 
2545 	if (Verbose && fn)
2546 		(void) printf(gettext("Request to install boot "
2547 		    "block file %s.\n"), fn);
2548 	else if (Verbose)
2549 		(void) printf(gettext("Request to install DOS boot block.\n"));
2550 
2551 	/*
2552 	 *  If they want to install their own boot block, sanity check
2553 	 *  that block.
2554 	 */
2555 	if (fn) {
2556 		bsfd = verify_bootblkfile(fn, bootsect, bootblksize);
2557 		if (bsfd < 0) {
2558 			exit(3);
2559 		}
2560 		*bootblksize = roundup(*bootblksize, BPSEC);
2561 	} else {
2562 		(void) memcpy(bootsect, DefBootSec, BPSEC);
2563 		*bootblksize = BPSEC;
2564 	}
2565 
2566 	return (bsfd);
2567 }
2568 
2569 /*
2570  *  mark_cluster
2571  *
2572  *	This routine fills a FAT entry with the value supplied to it as an
2573  *	argument.  The fatp argument is assumed to be a pointer to the FAT's
2574  *	0th entry.  The clustnum is the cluster entry that should be updated.
2575  *	The value is the new value for the entry.
2576  */
2577 static
2578 void
2579 mark_cluster(uchar_t *fatp, pc_cluster32_t clustnum, uint32_t value)
2580 {
2581 	uchar_t *ep;
2582 	ulong_t idx;
2583 
2584 	idx = (Fatentsize == 32) ? clustnum * 4 :
2585 		(Fatentsize == 16) ? clustnum * 2 : clustnum + clustnum/2;
2586 	ep = fatp + idx;
2587 
2588 	if (Fatentsize == 32) {
2589 		store_32_bits(&ep, value);
2590 	} else if (Fatentsize == 16) {
2591 		store_16_bits(&ep, value);
2592 	} else {
2593 		if (clustnum & 1) {
2594 			*ep = (*ep & 0x0f) | ((value << 4) & 0xf0);
2595 			ep++;
2596 			*ep = (value >> 4) & 0xff;
2597 		} else {
2598 			*ep++ = value & 0xff;
2599 			*ep = (*ep & 0xf0) | ((value >> 8) & 0x0f);
2600 		}
2601 	}
2602 }
2603 
2604 static
2605 uchar_t *
2606 build_fat(bpb_t *wbpb, struct fat32_boot_fsinfo *fsinfop, ulong_t bootblksize,
2607     ulong_t *fatsize, char *ffn, int *fffd, ulong_t *ffsize,
2608     pc_cluster32_t *ffstartclust)
2609 {
2610 	pc_cluster32_t nextfree, ci;
2611 	uchar_t *fatp;
2612 	ushort_t numclust, numsect;
2613 	int  remclust;
2614 
2615 	/* Alloc space for a FAT and then null it out. */
2616 	if (Verbose) {
2617 		(void) printf(gettext("BUILD FAT.\n%d sectors per fat.\n"),
2618 		    wbpb->bpb.sectors_per_fat ? wbpb->bpb.sectors_per_fat :
2619 			wbpb->bpb32.big_sectors_per_fat);
2620 	}
2621 
2622 	if (MakeFAT32) {
2623 		*fatsize = BPSEC * wbpb->bpb32.big_sectors_per_fat;
2624 	} else {
2625 		*fatsize = BPSEC * wbpb->bpb.sectors_per_fat;
2626 	}
2627 
2628 	if (!(fatp = (uchar_t *)malloc(*fatsize))) {
2629 		perror(gettext("FAT table alloc"));
2630 		exit(4);
2631 	} else {
2632 		(void) memset(fatp, 0, *fatsize);
2633 	}
2634 
2635 	/* Build in-memory FAT */
2636 	*fatp = wbpb->bpb.media;
2637 	*(fatp + 1) = 0xFF;
2638 	*(fatp + 2) = 0xFF;
2639 
2640 	if (Fatentsize == 16) {
2641 		*(fatp + 3) = 0xFF;
2642 	} else if (Fatentsize == 32) {
2643 		*(fatp + 3) = 0x0F;
2644 		*(fatp + 4) = 0xFF;
2645 		*(fatp + 5) = 0xFF;
2646 		*(fatp + 6) = 0xFF;
2647 		*(fatp + 7) = 0x0F;
2648 	}
2649 
2650 	/*
2651 	 * Keep track of clusters used.
2652 	 */
2653 	remclust = TotalClusters;
2654 	nextfree = 2;
2655 
2656 	/*
2657 	 * Get info on first file to install, if any.
2658 	 */
2659 	if (ffn)
2660 		*fffd = verify_firstfile(ffn, ffsize);
2661 
2662 	/*
2663 	 * Compute number of clusters to preserve for bootblk overage.
2664 	 * Remember that we already wrote the first sector of the boot block.
2665 	 * These clusters are marked BAD to prevent them from being deleted
2666 	 * or used.  The first available cluster is 2, so we always offset
2667 	 * the clusters.
2668 	 */
2669 	numsect = idivceil((bootblksize - BPSEC), BPSEC);
2670 	numclust = idivceil(numsect, wbpb->bpb.sectors_per_cluster);
2671 
2672 	if (Verbose && numclust)
2673 		(void) printf(gettext("Hiding %d excess bootblk cluster(s).\n"),
2674 		    numclust);
2675 	for (ci = 0; ci < numclust; ci++)
2676 		mark_cluster(fatp, nextfree++,
2677 			MakeFAT32 ? PCF_BADCLUSTER32 : PCF_BADCLUSTER);
2678 	remclust -= numclust;
2679 
2680 	/*
2681 	 * Reserve a cluster for the root directory on a FAT32.
2682 	 */
2683 	if (MakeFAT32) {
2684 		mark_cluster(fatp, nextfree, PCF_LASTCLUSTER32);
2685 		wbpb->bpb32.root_dir_clust = nextfree++;
2686 		remclust--;
2687 	}
2688 
2689 	/*
2690 	 * Compute and preserve number of clusters for first file.
2691 	 */
2692 	if (*fffd >= 0) {
2693 		*ffstartclust = nextfree;
2694 		numsect = idivceil(*ffsize, BPSEC);
2695 		numclust = idivceil(numsect, wbpb->bpb.sectors_per_cluster);
2696 
2697 		if (numclust > remclust) {
2698 			(void) fprintf(stderr,
2699 				gettext("Requested first file too large to be\n"
2700 					"installed in the new file system.\n"));
2701 			(void) close(*fffd);
2702 			*fffd = -1;
2703 			goto finish;
2704 		}
2705 
2706 		if (Verbose)
2707 			(void) printf(gettext("Reserving %d first file "
2708 			    "cluster(s).\n"), numclust);
2709 		for (ci = 0; (int)ci < (int)(numclust-1); ci++, nextfree++)
2710 			mark_cluster(fatp, nextfree, nextfree + 1);
2711 		mark_cluster(fatp, nextfree++,
2712 			MakeFAT32 ? PCF_LASTCLUSTER32 : PCF_LASTCLUSTER);
2713 		remclust -= numclust;
2714 	}
2715 
2716 finish:
2717 	if (Verbose) {
2718 		(void) printf(gettext("First sector of FAT"));
2719 		header_for_dump();
2720 		dump_bytes(fatp, BPSEC);
2721 	}
2722 
2723 	fsinfop->fs_signature = FAT32_FS_SIGN;
2724 	fsinfop->fs_free_clusters = remclust;
2725 	fsinfop->fs_next_cluster = nextfree;
2726 	return (fatp);
2727 }
2728 
2729 static
2730 void
2731 dirent_time_fill(struct pcdir *dep)
2732 {
2733 	struct  timeval tv;
2734 	struct	tm	*tp;
2735 	ushort_t	dostime;
2736 	ushort_t	dosday;
2737 
2738 	(void) gettimeofday(&tv, (struct timezone *)0);
2739 	tp = localtime(&tv.tv_sec);
2740 	/* get the time & day into DOS format */
2741 	dostime = tp->tm_sec / 2;
2742 	dostime |= tp->tm_min << 5;
2743 	dostime |= tp->tm_hour << 11;
2744 	dosday = tp->tm_mday;
2745 	dosday |= (tp->tm_mon + 1) << 5;
2746 	dosday |= (tp->tm_year - 80) << 9;
2747 	dep->pcd_mtime.pct_time = htols(dostime);
2748 	dep->pcd_mtime.pct_date = htols(dosday);
2749 }
2750 
2751 static
2752 void
2753 dirent_label_fill(struct pcdir *dep, char *fn)
2754 {
2755 	int nl, i;
2756 
2757 	/*
2758 	 * We spread the volume label across both the NAME and EXT fields
2759 	 */
2760 	nl = min(PCFNAMESIZE, strlen(fn));
2761 	for (i = 0; i < nl; i++) {
2762 		dep->pcd_filename[i] = toupper(fn[i]);
2763 	}
2764 	if (i < PCFNAMESIZE) {
2765 		for (; i < PCFNAMESIZE; i++)
2766 			dep->pcd_filename[i] = ' ';
2767 		for (i = 0; i < PCFEXTSIZE; i++)
2768 			dep->pcd_ext[i] = ' ';
2769 		return;
2770 	}
2771 	nl = min(PCFEXTSIZE, strlen(fn) - PCFNAMESIZE);
2772 	for (i = 0; i < nl; i++)
2773 		dep->pcd_ext[i] = toupper(fn[i + PCFNAMESIZE]);
2774 	if (i < PCFEXTSIZE) {
2775 		for (; i < PCFEXTSIZE; i++)
2776 			dep->pcd_ext[i] = ' ';
2777 	}
2778 }
2779 
2780 static
2781 void
2782 dirent_fname_fill(struct pcdir *dep, char *fn)
2783 {
2784 	char *fname, *fext;
2785 	int nl, i;
2786 
2787 	if (fname = strrchr(fn, '/')) {
2788 		fname++;
2789 	} else {
2790 		fname = fn;
2791 	}
2792 
2793 	if (fext = strrchr(fname, '.')) {
2794 		fext++;
2795 	} else {
2796 		fext = "";
2797 	}
2798 
2799 	fname = strtok(fname, ".");
2800 
2801 	nl = min(PCFNAMESIZE, (int)strlen(fname));
2802 	for (i = 0; i < nl; i++) {
2803 		dep->pcd_filename[i] = toupper(fname[i]);
2804 	}
2805 	for (; i < PCFNAMESIZE; i++) {
2806 		dep->pcd_filename[i] = ' ';
2807 	}
2808 
2809 	nl = min(PCFEXTSIZE, (int)strlen(fext));
2810 	for (i = 0; i < nl; i++) {
2811 		dep->pcd_ext[i] = toupper(fext[i]);
2812 	}
2813 	for (; i < PCFEXTSIZE; i++) {
2814 		dep->pcd_ext[i] = ' ';
2815 	}
2816 }
2817 
2818 static
2819 uchar_t *
2820 build_rootdir(bpb_t *wbpb, char *ffn, int fffd,
2821     ulong_t ffsize, pc_cluster32_t ffstart, ulong_t *rdirsize)
2822 {
2823 	struct pcdir *rootdirp;
2824 	struct pcdir *entry;
2825 
2826 	/*
2827 	 * Build a root directory.  It will have at least one entry,
2828 	 * the volume label and a second if the first file was defined.
2829 	 */
2830 	if (MakeFAT32) {
2831 		/*
2832 		 * We devote an entire cluster to the root
2833 		 * directory on FAT32.
2834 		 */
2835 		*rdirsize = wbpb->bpb.sectors_per_cluster * BPSEC;
2836 	} else {
2837 		*rdirsize = wbpb->bpb.num_root_entries * sizeof (struct pcdir);
2838 	}
2839 	if ((rootdirp = (struct pcdir *)malloc(*rdirsize)) == NULL) {
2840 		perror(gettext("Root directory allocation"));
2841 		exit(4);
2842 	} else {
2843 		entry = rootdirp;
2844 		(void) memset((char *)rootdirp, 0, *rdirsize);
2845 	}
2846 
2847 	/* Create directory entry for first file, if there is one */
2848 	if (fffd >= 0) {
2849 		dirent_fname_fill(entry, ffn);
2850 		entry->pcd_attr = Firstfileattr;
2851 		dirent_time_fill(entry);
2852 		entry->pcd_scluster_lo = htols(ffstart);
2853 		if (MakeFAT32) {
2854 			ffstart = ffstart >> 16;
2855 			entry->un.pcd_scluster_hi = htols(ffstart);
2856 		}
2857 		entry->pcd_size = htoli(ffsize);
2858 		entry++;
2859 	}
2860 
2861 	/* Create directory entry for volume label, if there is one */
2862 	if (Label != NULL) {
2863 		dirent_label_fill(entry, Label);
2864 		entry->pcd_attr = PCA_ARCH | PCA_LABEL;
2865 		dirent_time_fill(entry);
2866 		entry->pcd_scluster_lo = 0;
2867 		if (MakeFAT32) {
2868 			entry->un.pcd_scluster_hi = 0;
2869 		}
2870 		entry->pcd_size = 0;
2871 		entry++;
2872 	}
2873 
2874 	if (Verbose) {
2875 		(void) printf(gettext("First two directory entries"));
2876 		header_for_dump();
2877 		dump_bytes((uchar_t *)rootdirp, 2 * sizeof (struct pcdir));
2878 	}
2879 
2880 	return ((uchar_t *)rootdirp);
2881 }
2882 
2883 /*
2884  * write_rest
2885  *
2886  *	Write all the bytes from the current file pointer to end of file
2887  *	in the source file out to the destination file.  The writes should
2888  *	be padded to whole clusters with 0's if necessary.
2889  */
2890 static
2891 void
2892 write_rest(bpb_t *wbpb, char *efn, int dfd, int sfd, int remaining)
2893 {
2894 	char buf[BPSEC];
2895 	ushort_t numsect, numclust;
2896 	ushort_t wnumsect, s;
2897 	int doneread = 0;
2898 	int rstat;
2899 
2900 	/*
2901 	 * Compute number of clusters required to contain remaining bytes.
2902 	 */
2903 	numsect = idivceil(remaining, BPSEC);
2904 	numclust = idivceil(numsect, wbpb->bpb.sectors_per_cluster);
2905 
2906 	wnumsect = numclust * wbpb->bpb.sectors_per_cluster;
2907 	for (s = 0; s < wnumsect; s++) {
2908 		if (!doneread) {
2909 			if ((rstat = read(sfd, buf, BPSEC)) < 0) {
2910 				perror(efn);
2911 				doneread = 1;
2912 				rstat = 0;
2913 			} else if (rstat == 0) {
2914 				doneread = 1;
2915 			}
2916 			(void) memset(&(buf[rstat]), 0, BPSEC - rstat);
2917 		}
2918 		if (write(dfd, buf, BPSEC) != BPSEC) {
2919 			(void) fprintf(stderr, gettext("Copying "));
2920 			perror(efn);
2921 		}
2922 	}
2923 }
2924 
2925 static
2926 void
2927 write_fat32_bootstuff(int fd, boot_sector_t *bsp,
2928 	struct fat32_boot_fsinfo *fsinfop, off64_t seekto)
2929 {
2930 	uchar_t fsinfobuf[BPSEC];
2931 	uchar_t *fp;
2932 
2933 	/*
2934 	 * Construct the fsinfo buf
2935 	 */
2936 	(void) memset(fsinfobuf, 0, BPSEC);
2937 	/*
2938 	 * Not sure if this magic signature at the beginning of the
2939 	 * sector is necessary for us to create or not.  For now, I'm
2940 	 * going to assume it is, since I haven't seen any Windows FAT32s
2941 	 * without it.
2942 	 */
2943 	fsinfobuf[0] = 'R';
2944 	fsinfobuf[1] = 'R';
2945 	fsinfobuf[2] = 'a';
2946 	fsinfobuf[3] = 'A';
2947 	/*
2948 	 * We also appear to want the magic Boot sector signature at the
2949 	 * end of the sector.
2950 	 */
2951 	fsinfobuf[BPSEC - 2] = BOOTSECSIG & 0xFF;
2952 	fsinfobuf[BPSEC - 1] = (BOOTSECSIG >> 8) & 0xFF;
2953 
2954 	fp = &(fsinfobuf[FAT32_BOOT_FSINFO_OFF]);
2955 	fp += 4;	/* skip first reserved field */
2956 	store_32_bits(&fp, fsinfop->fs_signature);
2957 	store_32_bits(&fp, fsinfop->fs_free_clusters);
2958 	store_32_bits(&fp, fsinfop->fs_next_cluster);
2959 
2960 	if (Verbose) {
2961 		(void) printf(gettext("Dump of the fs info sector"));
2962 		header_for_dump();
2963 		dump_bytes(fsinfobuf, sizeof (fsinfobuf));
2964 	}
2965 
2966 	if (!Notreally) {
2967 		/*
2968 		 * FAT32's have an FS info sector, then a backup of the boot
2969 		 * sector, and a modified backup of the FS Info sector.
2970 		 */
2971 		if (write(fd, fsinfobuf, sizeof (fsinfobuf)) != BPSEC) {
2972 			perror(gettext("FS info sector write"));
2973 			exit(4);
2974 		}
2975 		if (lseek64(fd,	seekto + BKUP_BOOTSECT_OFFSET, SEEK_SET) < 0) {
2976 			(void) close(fd);
2977 			perror(gettext("Boot sector backup seek"));
2978 			exit(4);
2979 		}
2980 		if (write(fd, bsp->buf, sizeof (bsp->buf)) != BPSEC) {
2981 			perror(gettext("Boot sector backup write"));
2982 			exit(4);
2983 		}
2984 	}
2985 
2986 	/*
2987 	 * Second copy of fs info sector is modified to have "don't know"
2988 	 * as the number of free clusters
2989 	 */
2990 	fp = &(fsinfobuf[FAT32_BOOT_FSINFO_OFF]);
2991 	fp += 8;	/* skip first reserved field and signature */
2992 	store_32_bits(&fp, -1);
2993 
2994 	if (Verbose) {
2995 		(void) printf(gettext("Dump of the backup fs info sector"));
2996 		header_for_dump();
2997 		dump_bytes(fsinfobuf, sizeof (fsinfobuf));
2998 	}
2999 
3000 	if (!Notreally) {
3001 		if (write(fd, fsinfobuf, sizeof (fsinfobuf)) != BPSEC) {
3002 			perror(gettext("FS info sector backup write"));
3003 			exit(4);
3004 		}
3005 	}
3006 }
3007 
3008 static
3009 void
3010 write_bootsects(int fd, boot_sector_t *bsp, bpb_t *wbpb,
3011 	struct fat32_boot_fsinfo *fsinfop, off64_t seekto)
3012 {
3013 	if (MakeFAT32) {
3014 		/* Copy our BPB into bootsec structure */
3015 #ifdef i386
3016 		(void) memcpy(&(bsp->bs32.bs_front.bs_bpb), &(wbpb->bpb),
3017 			    sizeof (wbpb->bpb));
3018 		(void) memcpy(&(bsp->bs32.bs_bpb32), &(wbpb->bpb32),
3019 			    sizeof (wbpb->bpb32));
3020 		(void) memcpy(&(bsp->bs32.bs_ebpb), &(wbpb->ebpb),
3021 			    sizeof (wbpb->ebpb));
3022 #else
3023 		swap_pack_bpb32cpy(&(bsp->bs32), wbpb);
3024 #endif
3025 	} else {
3026 		/* Copy our BPB into bootsec structure */
3027 #ifdef i386
3028 		(void) memcpy(&(bsp->bs.bs_front.bs_bpb), &(wbpb->bpb),
3029 			    sizeof (wbpb->bpb));
3030 		(void) memcpy(&(bsp->bs.bs_ebpb), &(wbpb->ebpb),
3031 			    sizeof (wbpb->ebpb));
3032 #else
3033 		swap_pack_bpbcpy(&(bsp->bs), wbpb);
3034 #endif
3035 
3036 		/* Copy SUN BPB extensions into bootsec structure */
3037 		if (SunBPBfields) {
3038 #ifdef i386
3039 			(void) memcpy(&(bsp->bs.bs_sebpb), &(wbpb->sunbpb),
3040 				sizeof (wbpb->sunbpb));
3041 #else
3042 			swap_pack_sebpbcpy(&(bsp->bs), wbpb);
3043 #endif
3044 		}
3045 	}
3046 
3047 	/* Write boot sector */
3048 	if (!Notreally && write(fd, bsp->buf, sizeof (bsp->buf)) != BPSEC) {
3049 		perror(gettext("Boot sector write"));
3050 		exit(4);
3051 	}
3052 
3053 	if (Verbose) {
3054 		(void) printf(gettext("Dump of the boot sector"));
3055 		header_for_dump();
3056 		dump_bytes(bsp->buf, sizeof (bsp->buf));
3057 	}
3058 
3059 	if (MakeFAT32)
3060 		write_fat32_bootstuff(fd, bsp, fsinfop, seekto);
3061 }
3062 
3063 static
3064 void
3065 write_fat(int fd, off64_t seekto, char *fn, char *lbl, char *ffn, bpb_t *wbpb)
3066 {
3067 	struct fat32_boot_fsinfo fsinfo;
3068 	pc_cluster32_t ffsc;
3069 	boot_sector_t bootsect;
3070 	uchar_t *fatp, *rdirp;
3071 	ulong_t bootblksize, fatsize, rdirsize, ffsize;
3072 	int bsfd = -1;
3073 	int fffd = -1;
3074 
3075 	compute_file_area_size(wbpb);
3076 
3077 	bsfd = copy_bootblk(fn, &bootsect, &bootblksize);
3078 	label_volume(lbl, wbpb);
3079 
3080 	if (Verbose)
3081 		(void) printf(gettext("Building FAT.\n"));
3082 	fatp = build_fat(wbpb, &fsinfo, bootblksize, &fatsize,
3083 	    ffn, &fffd, &ffsize, &ffsc);
3084 
3085 	write_bootsects(fd, &bootsect, wbpb, &fsinfo, seekto);
3086 
3087 	if (lseek64(fd,
3088 	    seekto + (BPSEC * wbpb->bpb.resv_sectors), SEEK_SET) < 0) {
3089 		(void) close(fd);
3090 		perror(gettext("Seek to end of reserved sectors"));
3091 		exit(4);
3092 	}
3093 
3094 	/* Write FAT */
3095 	if (Verbose)
3096 		(void) printf(gettext("Writing FAT(s). %d bytes times %d.\n"),
3097 		    fatsize, wbpb->bpb.num_fats);
3098 	if (!Notreally) {
3099 		int nf, wb;
3100 		for (nf = 0; nf < (int)wbpb->bpb.num_fats; nf++)
3101 			if ((wb = write(fd, fatp, fatsize)) != fatsize) {
3102 				perror(gettext("FAT write"));
3103 				exit(4);
3104 			} else {
3105 				if (Verbose)
3106 					(void) printf(
3107 					    gettext("Wrote %d bytes\n"), wb);
3108 			}
3109 	}
3110 	free(fatp);
3111 
3112 	if (Verbose)
3113 		(void) printf(gettext("Building root directory.\n"));
3114 	rdirp = build_rootdir(wbpb, ffn, fffd, ffsize, ffsc, &rdirsize);
3115 
3116 	/*
3117 	 *  In non FAT32, root directory exists outside of the file area
3118 	 */
3119 	if (!MakeFAT32) {
3120 		if (Verbose)
3121 		    (void) printf(
3122 			gettext("Writing root directory. "
3123 				"%d bytes.\n"), rdirsize);
3124 		if (!Notreally) {
3125 			if (write(fd, rdirp, rdirsize) != rdirsize) {
3126 				perror(gettext("Root directory write"));
3127 				exit(4);
3128 			}
3129 		}
3130 		free(rdirp);
3131 	}
3132 
3133 	/*
3134 	 * Now write anything that needs to be in the file space.
3135 	 */
3136 	if (bootblksize > BPSEC) {
3137 		if (Verbose)
3138 			(void) printf(gettext("Writing remainder of "
3139 				"boot block.\n"));
3140 		if (!Notreally)
3141 			write_rest(wbpb, fn, fd, bsfd, bootblksize - BPSEC);
3142 	}
3143 
3144 	if (MakeFAT32) {
3145 		if (Verbose)
3146 		    (void) printf(
3147 			gettext("Writing root directory. "
3148 				"%d bytes.\n"), rdirsize);
3149 		if (!Notreally) {
3150 			if (write(fd, rdirp, rdirsize) != rdirsize) {
3151 				perror(gettext("Root directory write"));
3152 				exit(4);
3153 			}
3154 		}
3155 		free(rdirp);
3156 	}
3157 
3158 	if (fffd >= 0) {
3159 		if (Verbose)
3160 			(void) printf(gettext("Writing first file.\n"));
3161 		if (!Notreally)
3162 			write_rest(wbpb, ffn, fd, fffd, ffsize);
3163 	}
3164 }
3165 
3166 static
3167 char *LegalOpts[] = {
3168 #define	NFLAG 0
3169 	"N",
3170 #define	VFLAG 1
3171 	"v",
3172 #define	RFLAG 2
3173 	"r",
3174 #define	HFLAG 3
3175 	"h",
3176 #define	SFLAG 4
3177 	"s",
3178 #define	SUNFLAG 5
3179 	"S",
3180 #define	LABFLAG 6
3181 	"b",
3182 #define	BTRFLAG 7
3183 	"B",
3184 #define	INITFLAG 8
3185 	"i",
3186 #define	SZFLAG 9
3187 	"size",
3188 #define	SECTFLAG 10
3189 	"nsect",
3190 #define	TRKFLAG 11
3191 	"ntrack",
3192 #define	SPCFLAG 12
3193 	"spc",
3194 #define	BPFFLAG 13
3195 	"fat",
3196 #define	FFLAG 14
3197 	"f",
3198 #define	DFLAG 15
3199 	"d",
3200 #define	NOFDISKFLAG 16
3201 	"nofdisk",
3202 #define	RESRVFLAG 17
3203 	"reserve",
3204 #define	HIDDENFLAG 18
3205 	"hidden",
3206 	NULL
3207 };
3208 
3209 static
3210 void
3211 bad_arg(char *option)
3212 {
3213 	(void) fprintf(stderr,
3214 		gettext("Unrecognized option %s.\n"), option);
3215 	usage();
3216 	exit(2);
3217 }
3218 
3219 static
3220 void
3221 missing_arg(char *option)
3222 {
3223 	(void) fprintf(stderr,
3224 		gettext("Option %s requires a value.\n"), option);
3225 	usage();
3226 	exit(3);
3227 }
3228 
3229 static
3230 void
3231 parse_suboptions(char *optsstr)
3232 {
3233 	char *value;
3234 	int c;
3235 
3236 	while (*optsstr != '\0') {
3237 		switch (c = getsubopt(&optsstr, LegalOpts, &value)) {
3238 		case NFLAG:
3239 			Notreally++;
3240 			break;
3241 		case VFLAG:
3242 			Verbose++;
3243 			break;
3244 		case RFLAG:
3245 			Firstfileattr |= 0x01;
3246 			break;
3247 		case HFLAG:
3248 			Firstfileattr |= 0x02;
3249 			break;
3250 		case SFLAG:
3251 			Firstfileattr |= 0x04;
3252 			break;
3253 		case SUNFLAG:
3254 			SunBPBfields = 1;
3255 			break;
3256 		case LABFLAG:
3257 			if (value == NULL) {
3258 				missing_arg(LegalOpts[c]);
3259 			} else {
3260 				Label = value;
3261 			}
3262 			break;
3263 		case BTRFLAG:
3264 			if (value == NULL) {
3265 				missing_arg(LegalOpts[c]);
3266 			} else {
3267 				BootBlkFn = value;
3268 			}
3269 			break;
3270 		case INITFLAG:
3271 			if (value == NULL) {
3272 				missing_arg(LegalOpts[c]);
3273 			} else {
3274 				FirstFn = value;
3275 			}
3276 			break;
3277 		case SZFLAG:
3278 			if (value == NULL) {
3279 				missing_arg(LegalOpts[c]);
3280 			} else {
3281 				TotSize = atoi(value);
3282 				GetSize = 0;
3283 			}
3284 			break;
3285 		case SECTFLAG:
3286 			if (value == NULL) {
3287 				missing_arg(LegalOpts[c]);
3288 			} else {
3289 				SecPerTrk = atoi(value);
3290 				GetSPT = 0;
3291 			}
3292 			break;
3293 		case TRKFLAG:
3294 			if (value == NULL) {
3295 				missing_arg(LegalOpts[c]);
3296 			} else {
3297 				TrkPerCyl = atoi(value);
3298 				GetTPC = 0;
3299 			}
3300 			break;
3301 		case SPCFLAG:
3302 			if (value == NULL) {
3303 				missing_arg(LegalOpts[c]);
3304 			} else {
3305 				SecPerClust = atoi(value);
3306 				GetSPC = 0;
3307 			}
3308 			break;
3309 		case BPFFLAG:
3310 			if (value == NULL) {
3311 				missing_arg(LegalOpts[c]);
3312 			} else {
3313 				BitsPerFAT = atoi(value);
3314 				GetBPF = 0;
3315 			}
3316 			break;
3317 		case NOFDISKFLAG:
3318 			DontUseFdisk = 1;
3319 			break;
3320 		case RESRVFLAG:
3321 			if (value == NULL) {
3322 				missing_arg(LegalOpts[c]);
3323 			} else {
3324 				Resrvd = atoi(value);
3325 				GetResrvd = 0;
3326 			}
3327 			break;
3328 		case HIDDENFLAG:
3329 			if (value == NULL) {
3330 				missing_arg(LegalOpts[c]);
3331 			} else {
3332 				RelOffset = atoi(value);
3333 				GetOffset = 0;
3334 			}
3335 			break;
3336 		case FFLAG:
3337 			if (value == NULL) {
3338 				missing_arg(LegalOpts[c]);
3339 			} else {
3340 				DiskName = value;
3341 				Outputtofile = 1;
3342 			}
3343 			break;
3344 		case DFLAG:
3345 			if (value == NULL) {
3346 				missing_arg(LegalOpts[c]);
3347 			} else {
3348 				Imagesize = atoi(value);
3349 			}
3350 			break;
3351 		default:
3352 			bad_arg(value);
3353 			break;
3354 		}
3355 	}
3356 }
3357 
3358 static
3359 void
3360 sanity_check_options(int argc, int optind)
3361 {
3362 	if (GetFsParams) {
3363 		if (argc - optind != 1)
3364 			usage();
3365 		return;
3366 	}
3367 
3368 	if (DontUseFdisk && GetOffset) {
3369 		/* Set default relative offset of zero */
3370 		RelOffset = 0;
3371 	}
3372 
3373 	if (BitsPerFAT == 32)
3374 		MakeFAT32 = 1;
3375 
3376 	if (Outputtofile && (argc - optind)) {
3377 		usage();
3378 	} else if (Outputtofile && !DiskName) {
3379 		usage();
3380 	} else if (!Outputtofile && (argc - optind != 1)) {
3381 		usage();
3382 	} else if (SunBPBfields && !BootBlkFn) {
3383 		(void) fprintf(stderr,
3384 		    gettext("Use of the 'S' option requires that\n"
3385 			    "the 'B=' option also be used.\n\n"));
3386 		usage();
3387 	} else if (Firstfileattr != 0x20 && !FirstFn) {
3388 		(void) fprintf(stderr,
3389 		    gettext("Use of the 'r', 'h', or 's' options requires\n"
3390 			    "that the 'i=' option also be used.\n\n"));
3391 		usage();
3392 	} else if (!GetOffset && !DontUseFdisk) {
3393 		(void) fprintf(stderr,
3394 		    gettext("Use of the 'hidden' option requires that\n"
3395 			    "the 'nofdisk' option also be used.\n\n"));
3396 		usage();
3397 	} else if (DontUseFdisk && GetSize) {
3398 		(void) fprintf(stderr,
3399 		    gettext("Use of the 'nofdisk' option requires that\n"
3400 			    "the 'size=' option also be used.\n\n"));
3401 		usage();
3402 	} else if (!GetBPF &&
3403 		    BitsPerFAT != 12 && BitsPerFAT != 16 && BitsPerFAT != 32) {
3404 		(void) fprintf(stderr,
3405 		    gettext("Invalid Bits/Fat value."
3406 			    "  Must be 12, 16 or 32.\n"));
3407 		exit(2);
3408 	} else if (!GetSPC && !powerofx_le_y(2, 128, SecPerClust)) {
3409 		(void) fprintf(stderr,
3410 		    gettext("Invalid Sectors/Cluster value.  Must be a "
3411 			    "power of 2 between 1 and 128.\n"));
3412 		exit(2);
3413 	} else if (!GetResrvd && (Resrvd < 1 || Resrvd > 0xffff)) {
3414 		(void) fprintf(stderr,
3415 		    gettext("Invalid number of reserved sectors.  "
3416 			"Must be at least 1 but\nno larger than 65535."));
3417 		exit(2);
3418 	} else if (!GetResrvd && MakeFAT32 &&
3419 		    (Resrvd < 32 || Resrvd > 0xffff)) {
3420 		(void) fprintf(stderr,
3421 		    gettext("Invalid number of reserved sectors.  "
3422 			"Must be at least 32 but\nno larger than 65535."));
3423 		exit(2);
3424 	} else if (Imagesize != 3 && Imagesize != 5) {
3425 		usage();
3426 	}
3427 }
3428 
3429 int
3430 main(int argc, char **argv)
3431 {
3432 	off64_t AbsBootSect = 0;
3433 	bpb_t dskparamblk;
3434 	char *string;
3435 	int  fd;
3436 	int  c;
3437 
3438 	(void) setlocale(LC_ALL, "");
3439 
3440 #if !defined(TEXT_DOMAIN)
3441 #define	TEXT_DOMAIN "SYS_TEST"
3442 #endif
3443 	(void) textdomain(TEXT_DOMAIN);
3444 
3445 	while ((c = getopt(argc, argv, "F:Vmo:")) != EOF) {
3446 		switch (c) {
3447 		case 'F':
3448 			string = optarg;
3449 			if (strcmp(string, "pcfs") != 0)
3450 				usage();
3451 			break;
3452 		case 'V':
3453 			{
3454 				char	*opt_text;
3455 				int	opt_count;
3456 
3457 				(void) fprintf(stdout,
3458 				    gettext("mkfs -F pcfs "));
3459 				for (opt_count = 1; opt_count < argc;
3460 								opt_count++) {
3461 					opt_text = argv[opt_count];
3462 					if (opt_text)
3463 					    (void) fprintf(stdout, " %s ",
3464 								opt_text);
3465 				}
3466 				(void) fprintf(stdout, "\n");
3467 			}
3468 			break;
3469 		case 'm':
3470 			GetFsParams++;
3471 			break;
3472 		case 'o':
3473 			string = optarg;
3474 			parse_suboptions(string);
3475 			break;
3476 		}
3477 	}
3478 
3479 	sanity_check_options(argc, optind);
3480 
3481 	if (!Outputtofile)
3482 		DiskName = argv[optind];
3483 
3484 	(void) memset(&dskparamblk, 0, sizeof (dskparamblk));
3485 
3486 	if (GetFsParams) {
3487 		fd = open_and_examine(DiskName, &dskparamblk);
3488 	} else {
3489 		fd = open_and_seek(DiskName, &dskparamblk, &AbsBootSect);
3490 		if (ask_nicely(DiskName))
3491 			write_fat(fd, AbsBootSect, BootBlkFn, Label,
3492 			    FirstFn, &dskparamblk);
3493 	}
3494 	(void) close(fd);
3495 	return (0);
3496 }
3497