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