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