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