xref: /titanic_51/usr/src/cmd/fs.d/pcfs/mkfs/mkfs.c (revision 2b4993b1947ac64f1b382c6922267bb50910e758)
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 dk_geom dg;
2184 	struct stat di;
2185 	char *actualdisk = NULL;
2186 	char *suffix = NULL;
2187 	int fd;
2188 
2189 	if (Verbose)
2190 		(void) printf(gettext("Opening destination device/file.\n"));
2191 
2192 	/*
2193 	 * We hold these truths to be self evident, all BPBs we create
2194 	 * will have these values in these fields.
2195 	 */
2196 	wbpb->bpb.num_fats = 2;
2197 	wbpb->bpb.bytes_sector = BPSEC;
2198 
2199 	/*
2200 	 * Assign or use supplied numbers for hidden and
2201 	 * reserved sectors in the file system.
2202 	 */
2203 	if (GetResrvd)
2204 		if (MakeFAT32)
2205 			wbpb->bpb.resv_sectors = 32;
2206 		else
2207 			wbpb->bpb.resv_sectors = 1;
2208 	else
2209 		wbpb->bpb.resv_sectors = Resrvd;
2210 
2211 	wbpb->ebpb.ext_signature = 0x29; /* Magic number for modern format */
2212 	wbpb->ebpb.volume_id = 0;
2213 
2214 	if (MakeFAT32)
2215 		fill_fat32_bpb(wbpb);
2216 
2217 	/*
2218 	 * If all output goes to a simple file, call a routine to setup
2219 	 * that scenario. Otherwise, try to find the device.
2220 	 */
2221 	if (Outputtofile)
2222 		return (fd = prepare_image_file(dn, wbpb));
2223 
2224 	actualdisk = stat_actual_disk(dn, &di, &suffix);
2225 
2226 	/*
2227 	 * Sanity check.  If we've been provided a partition-specifying
2228 	 * suffix, we shouldn't also have been told to ignore the
2229 	 * fdisk table.
2230 	 */
2231 	if (DontUseFdisk && suffix) {
2232 		(void) fprintf(stderr,
2233 		    gettext("Using 'nofdisk' option precludes "
2234 			    "appending logical drive\nspecifier "
2235 			    "to the device name.\n"));
2236 		exit(2);
2237 	}
2238 
2239 	/*
2240 	 *  Destination exists, now find more about it.
2241 	 */
2242 	if (!(S_ISCHR(di.st_mode))) {
2243 		(void) fprintf(stderr,
2244 		    gettext("\n%s: device name must indicate a "
2245 			"character special device.\n"), actualdisk);
2246 		exit(2);
2247 	} else if ((fd = open(actualdisk, O_RDWR | O_EXCL)) < 0) {
2248 		perror(actualdisk);
2249 		exit(2);
2250 	}
2251 
2252 	/*
2253 	 * Find appropriate partition if we were requested to do so.
2254 	 */
2255 	if (suffix && !(seek_partn(fd, suffix, wbpb, seekto))) {
2256 		(void) close(fd);
2257 		exit(2);
2258 	}
2259 
2260 	if (!suffix) {
2261 		/*
2262 		 * We have one of two possibilities.  Chances are we have
2263 		 * a floppy drive.  But the user may be trying to format
2264 		 * some weird drive that we don't know about and is supplying
2265 		 * all the important values.  In that case, they should have set
2266 		 * the 'nofdisk' flag.
2267 		 *
2268 		 * If 'nofdisk' isn't set, do a floppy-specific ioctl to
2269 		 * get the remainder of our info. If the ioctl fails, we have
2270 		 * a good idea that they aren't really on a floppy.  In that
2271 		 * case, they should have given us a partition specifier.
2272 		 */
2273 		if (DontUseFdisk) {
2274 			if (!(seek_nofdisk(fd, wbpb, seekto))) {
2275 				(void) close(fd);
2276 				exit(2);
2277 			}
2278 			find_fixed_details(fd, wbpb);
2279 		} else if (ioctl(fd, FDIOGCHAR, &fdchar) == -1) {
2280 			/*
2281 			 * It is possible that we are trying to use floppy
2282 			 * specific FDIOGCHAR ioctl on USB floppy. Since sd
2283 			 * driver, by which USB floppy is handled, doesn't
2284 			 * support it, we can try to use disk DKIOCGGEOM ioctl
2285 			 * to retrieve data we need. sd driver itself
2286 			 * determines floppy disk by number of blocks
2287 			 * (<=0x1000), then it sets geometry to 80 cylinders,
2288 			 * 2 heads.
2289 			 *
2290 			 * Note that DKIOCGGEOM cannot supply us with type
2291 			 * of media (e.g. 3.5" or 5.25"). We will set it to
2292 			 * 3 (3.5") which is most probable value.
2293 			 */
2294 			if (errno == ENOTTY) {
2295 				if (ioctl(fd, DKIOCGGEOM, &dg) != -1 &&
2296 				    dg.dkg_ncyl == 80 && dg.dkg_nhead == 2) {
2297 					fdchar.fdc_ncyl = dg.dkg_ncyl;
2298 					fdchar.fdc_medium = 3;
2299 					fdchar.fdc_secptrack = dg.dkg_nsect;
2300 					fdchar.fdc_nhead = dg.dkg_nhead;
2301 					lookup_floppy(&fdchar, wbpb);
2302 				} else {
2303 					partn_lecture(actualdisk);
2304 					(void) close(fd);
2305 					exit(2);
2306 				}
2307 			}
2308 		} else {
2309 #ifdef sparc
2310 			fdchar.fdc_medium = 3;
2311 #endif
2312 			lookup_floppy(&fdchar, wbpb);
2313 		}
2314 	} else {
2315 		find_fixed_details(fd, wbpb);
2316 	}
2317 
2318 	return (fd);
2319 }
2320 
2321 /*
2322  * The following is a copy of MS-DOS 4.0 boot block.
2323  * It consists of the BIOS parameter block, and a disk
2324  * bootstrap program.
2325  *
2326  * The BIOS parameter block contains the right values
2327  * for the 3.5" high-density 1.44MB floppy format.
2328  *
2329  * This will be our default boot sector, if the user
2330  * didn't point us at a different one.
2331  *
2332  */
2333 static
2334 uchar_t DefBootSec[512] = {
2335 	0xeb, 0x3c, 0x90, 	/* 8086 short jump + displacement + NOP */
2336 	'M', 'S', 'D', 'O', 'S', '4', '.', '0',	/* OEM name & version */
2337 	0x00, 0x02, 0x01, 0x01, 0x00,
2338 	0x02, 0xe0, 0x00, 0x40, 0x0b,
2339 	0xf0, 0x09, 0x00, 0x12, 0x00,
2340 	0x02, 0x00,
2341 	0x00, 0x00, 0x00, 0x00,
2342 	0x00, 0x00, 0x00, 0x00,
2343 	0x00, 0x00,
2344 	0x29, 0x00, 0x00, 0x00, 0x00,
2345 	'N', 'O', 'N', 'A', 'M', 'E', ' ', ' ', ' ', ' ', ' ',
2346 	'F', 'A', 'T', '1', '2', ' ', ' ', ' ',
2347 	0xfa, 0x33,
2348 	0xc0, 0x8e, 0xd0, 0xbc, 0x00, 0x7c, 0x16, 0x07,
2349 	0xbb, 0x78, 0x00, 0x36, 0xc5, 0x37, 0x1e, 0x56,
2350 	0x16, 0x53, 0xbf, 0x3e, 0x7c, 0xb9, 0x0b, 0x00,
2351 	0xfc, 0xf3, 0xa4, 0x06, 0x1f, 0xc6, 0x45, 0xfe,
2352 	0x0f, 0x8b, 0x0e, 0x18, 0x7c, 0x88, 0x4d, 0xf9,
2353 	0x89, 0x47, 0x02, 0xc7, 0x07, 0x3e, 0x7c, 0xfb,
2354 	0xcd, 0x13, 0x72, 0x7c, 0x33, 0xc0, 0x39, 0x06,
2355 	0x13, 0x7c, 0x74, 0x08, 0x8b, 0x0e, 0x13, 0x7c,
2356 	0x89, 0x0e, 0x20, 0x7c, 0xa0, 0x10, 0x7c, 0xf7,
2357 	0x26, 0x16, 0x7c, 0x03, 0x06, 0x1c, 0x7c, 0x13,
2358 	0x16, 0x1e, 0x7c, 0x03, 0x06, 0x0e, 0x7c, 0x83,
2359 	0xd2, 0x00, 0xa3, 0x50, 0x7c, 0x89, 0x16, 0x52,
2360 	0x7c, 0xa3, 0x49, 0x7c, 0x89, 0x16, 0x4b, 0x7c,
2361 	0xb8, 0x20, 0x00, 0xf7, 0x26, 0x11, 0x7c, 0x8b,
2362 	0x1e, 0x0b, 0x7c, 0x03, 0xc3, 0x48, 0xf7, 0xf3,
2363 	0x01, 0x06, 0x49, 0x7c, 0x83, 0x16, 0x4b, 0x7c,
2364 	0x00, 0xbb, 0x00, 0x05, 0x8b, 0x16, 0x52, 0x7c,
2365 	0xa1, 0x50, 0x7c, 0xe8, 0x87, 0x00, 0x72, 0x20,
2366 	0xb0, 0x01, 0xe8, 0xa1, 0x00, 0x72, 0x19, 0x8b,
2367 	0xfb, 0xb9, 0x0b, 0x00, 0xbe, 0xdb, 0x7d, 0xf3,
2368 	0xa6, 0x75, 0x0d, 0x8d, 0x7f, 0x20, 0xbe, 0xe6,
2369 	0x7d, 0xb9, 0x0b, 0x00, 0xf3, 0xa6, 0x74, 0x18,
2370 	0xbe, 0x93, 0x7d, 0xe8, 0x51, 0x00, 0x32, 0xe4,
2371 	0xcd, 0x16, 0x5e, 0x1f, 0x8f, 0x04, 0x8f, 0x44,
2372 	0x02, 0xcd, 0x19, 0x58, 0x58, 0x58, 0xeb, 0xe8,
2373 	0xbb, 0x00, 0x07, 0xb9, 0x03, 0x00, 0xa1, 0x49,
2374 	0x7c, 0x8b, 0x16, 0x4b, 0x7c, 0x50, 0x52, 0x51,
2375 	0xe8, 0x3a, 0x00, 0x72, 0xe6, 0xb0, 0x01, 0xe8,
2376 	0x54, 0x00, 0x59, 0x5a, 0x58, 0x72, 0xc9, 0x05,
2377 	0x01, 0x00, 0x83, 0xd2, 0x00, 0x03, 0x1e, 0x0b,
2378 	0x7c, 0xe2, 0xe2, 0x8a, 0x2e, 0x15, 0x7c, 0x8a,
2379 	0x16, 0x24, 0x7c, 0x8b, 0x1e, 0x49, 0x7c, 0xa1,
2380 	0x4b, 0x7c, 0xea, 0x00, 0x00, 0x70, 0x00, 0xac,
2381 	0x0a, 0xc0, 0x74, 0x29, 0xb4, 0x0e, 0xbb, 0x07,
2382 	0x00, 0xcd, 0x10, 0xeb, 0xf2, 0x3b, 0x16, 0x18,
2383 	0x7c, 0x73, 0x19, 0xf7, 0x36, 0x18, 0x7c, 0xfe,
2384 	0xc2, 0x88, 0x16, 0x4f, 0x7c, 0x33, 0xd2, 0xf7,
2385 	0x36, 0x1a, 0x7c, 0x88, 0x16, 0x25, 0x7c, 0xa3,
2386 	0x4d, 0x7c, 0xf8, 0xc3, 0xf9, 0xc3, 0xb4, 0x02,
2387 	0x8b, 0x16, 0x4d, 0x7c, 0xb1, 0x06, 0xd2, 0xe6,
2388 	0x0a, 0x36, 0x4f, 0x7c, 0x8b, 0xca, 0x86, 0xe9,
2389 	0x8a, 0x16, 0x24, 0x7c, 0x8a, 0x36, 0x25, 0x7c,
2390 	0xcd, 0x13, 0xc3, 0x0d, 0x0a, 0x4e, 0x6f, 0x6e,
2391 	0x2d, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20,
2392 	0x64, 0x69, 0x73, 0x6b, 0x20, 0x6f, 0x72, 0x20,
2393 	0x64, 0x69, 0x73, 0x6b, 0x20, 0x65, 0x72, 0x72,
2394 	0x6f, 0x72, 0x0d, 0x0a, 0x52, 0x65, 0x70, 0x6c,
2395 	0x61, 0x63, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20,
2396 	0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e,
2397 	0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x77, 0x68,
2398 	0x65, 0x6e, 0x20, 0x72, 0x65, 0x61, 0x64, 0x79,
2399 	0x0d, 0x0a, 0x00, 0x49, 0x4f, 0x20, 0x20, 0x20,
2400 	0x20, 0x20, 0x20, 0x53, 0x59, 0x53, 0x4d, 0x53,
2401 	0x44, 0x4f, 0x53, 0x20, 0x20, 0x20, 0x53, 0x59,
2402 	0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2403 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
2404 };
2405 
2406 /*
2407  *  verify_bootblkfile
2408  *
2409  *	We were provided with the name of a file containing the bootblk
2410  *	to install.  Verify it has a valid boot sector as best we can. Any
2411  *	errors and we return a bad file descriptor.  Otherwise we fill up the
2412  *	provided buffer with the boot sector, return the file
2413  *	descriptor for later use and leave the file pointer just
2414  *	past the boot sector part of the boot block file.
2415  */
2416 static
2417 int
2418 verify_bootblkfile(char *fn, boot_sector_t *bs, ulong_t *blkfilesize)
2419 {
2420 	struct stat fi;
2421 	int bsfd = -1;
2422 
2423 	if (stat(fn, &fi)) {
2424 		perror(fn);
2425 	} else if (fi.st_size < BPSEC) {
2426 		(void) fprintf(stderr,
2427 		    gettext("%s: Too short to be a boot sector.\n"), fn);
2428 	} else if ((bsfd = open(fn, O_RDONLY)) < 0) {
2429 		perror(fn);
2430 	} else if (read(bsfd, bs->buf, BPSEC) < BPSEC) {
2431 		(void) close(bsfd);
2432 		bsfd = -1;
2433 		perror(gettext("Boot block read"));
2434 	} else {
2435 #ifdef i386
2436 		if ((bs->bs.bs_front.bs_jump_code[0] != OPCODE1 &&
2437 		    bs->bs.bs_front.bs_jump_code[0] != OPCODE2) ||
2438 #else
2439 		if ((bs->bs.bs_jump_code[0] != OPCODE1 &&
2440 		    bs->bs.bs_jump_code[0] != OPCODE2) ||
2441 #endif
2442 		    (bs->bs.bs_signature[0] != (BOOTSECSIG & 0xFF) &&
2443 		    bs->bs.bs_signature[1] != ((BOOTSECSIG >> 8) & 0xFF))) {
2444 			(void) close(bsfd);
2445 			bsfd = -1;
2446 			(void) fprintf(stderr,
2447 			    gettext("Boot block (%s) bogus.\n"), fn);
2448 		}
2449 		*blkfilesize = fi.st_size;
2450 	}
2451 	return (bsfd);
2452 }
2453 
2454 /*
2455  *  verify_firstfile
2456  *
2457  *	We were provided with the name of a file to be the first file
2458  *	installed on the disk.  We just need to verify it exists and
2459  *	find out how big it is.  If it doesn't exist, we print a warning
2460  *	message about how the file wasn't found.  We don't exit fatally,
2461  *	though, rather we return a size of 0 and the FAT will be built
2462  *	without installing any first file.  They can then presumably
2463  *	install the correct first file by hand.
2464  */
2465 static
2466 int
2467 verify_firstfile(char *fn, ulong_t *filesize)
2468 {
2469 	struct stat fi;
2470 	int fd = -1;
2471 
2472 	*filesize = 0;
2473 	if (stat(fn, &fi) || (fd = open(fn, O_RDONLY)) < 0) {
2474 		perror(fn);
2475 		(void) fprintf(stderr,
2476 		    gettext("Could not access requested file.  It will not\n"
2477 			    "be installed in the new file system.\n"));
2478 	} else {
2479 		*filesize = fi.st_size;
2480 	}
2481 
2482 	return (fd);
2483 }
2484 
2485 /*
2486  *  label_volume
2487  *
2488  *	Fill in BPB with volume label.
2489  */
2490 static
2491 void
2492 label_volume(char *lbl, bpb_t *wbpb)
2493 {
2494 	int ll, i;
2495 
2496 	/* Put a volume label into our BPB. */
2497 	if (!lbl)
2498 		lbl = DEFAULT_LABEL;
2499 
2500 	ll = min(11, (int)strlen(lbl));
2501 	for (i = 0; i < ll; i++) {
2502 		wbpb->ebpb.volume_label[i] = toupper(lbl[i]);
2503 	}
2504 	for (; i < 11; i++) {
2505 		wbpb->ebpb.volume_label[i] = ' ';
2506 	}
2507 }
2508 
2509 static
2510 int
2511 copy_bootblk(char *fn, boot_sector_t *bootsect, ulong_t *bootblksize)
2512 {
2513 	int bsfd = -1;
2514 
2515 	if (Verbose && fn)
2516 		(void) printf(gettext("Request to install boot "
2517 		    "block file %s.\n"), fn);
2518 	else if (Verbose)
2519 		(void) printf(gettext("Request to install DOS boot block.\n"));
2520 
2521 	/*
2522 	 *  If they want to install their own boot block, sanity check
2523 	 *  that block.
2524 	 */
2525 	if (fn) {
2526 		bsfd = verify_bootblkfile(fn, bootsect, bootblksize);
2527 		if (bsfd < 0) {
2528 			exit(3);
2529 		}
2530 		*bootblksize = roundup(*bootblksize, BPSEC);
2531 	} else {
2532 		(void) memcpy(bootsect, DefBootSec, BPSEC);
2533 		*bootblksize = BPSEC;
2534 	}
2535 
2536 	return (bsfd);
2537 }
2538 
2539 /*
2540  *  mark_cluster
2541  *
2542  *	This routine fills a FAT entry with the value supplied to it as an
2543  *	argument.  The fatp argument is assumed to be a pointer to the FAT's
2544  *	0th entry.  The clustnum is the cluster entry that should be updated.
2545  *	The value is the new value for the entry.
2546  */
2547 static
2548 void
2549 mark_cluster(uchar_t *fatp, pc_cluster32_t clustnum, uint32_t value)
2550 {
2551 	uchar_t *ep;
2552 	ulong_t idx;
2553 
2554 	idx = (Fatentsize == 32) ? clustnum * 4 :
2555 		(Fatentsize == 16) ? clustnum * 2 : clustnum + clustnum/2;
2556 	ep = fatp + idx;
2557 
2558 	if (Fatentsize == 32) {
2559 		store_32_bits(&ep, value);
2560 	} else if (Fatentsize == 16) {
2561 		store_16_bits(&ep, value);
2562 	} else {
2563 		if (clustnum & 1) {
2564 			*ep = (*ep & 0x0f) | ((value << 4) & 0xf0);
2565 			ep++;
2566 			*ep = (value >> 4) & 0xff;
2567 		} else {
2568 			*ep++ = value & 0xff;
2569 			*ep = (*ep & 0xf0) | ((value >> 8) & 0x0f);
2570 		}
2571 	}
2572 }
2573 
2574 static
2575 uchar_t *
2576 build_fat(bpb_t *wbpb, struct fat32_boot_fsinfo *fsinfop, ulong_t bootblksize,
2577     ulong_t *fatsize, char *ffn, int *fffd, ulong_t *ffsize,
2578     pc_cluster32_t *ffstartclust)
2579 {
2580 	pc_cluster32_t nextfree, ci;
2581 	uchar_t *fatp;
2582 	ushort_t numclust, numsect;
2583 	int  remclust;
2584 
2585 	/* Alloc space for a FAT and then null it out. */
2586 	if (Verbose) {
2587 		(void) printf(gettext("BUILD FAT.\n%d sectors per fat.\n"),
2588 		    wbpb->bpb.sectors_per_fat ? wbpb->bpb.sectors_per_fat :
2589 			wbpb->bpb32.big_sectors_per_fat);
2590 	}
2591 
2592 	if (MakeFAT32) {
2593 		*fatsize = BPSEC * wbpb->bpb32.big_sectors_per_fat;
2594 	} else {
2595 		*fatsize = BPSEC * wbpb->bpb.sectors_per_fat;
2596 	}
2597 	if (!(fatp = (uchar_t *)malloc(*fatsize))) {
2598 		perror(gettext("FAT table alloc"));
2599 		exit(4);
2600 	} else {
2601 		(void) memset(fatp, 0, *fatsize);
2602 	}
2603 
2604 	/* Build in-memory FAT */
2605 	*fatp = wbpb->bpb.media;
2606 	*(fatp + 1) = 0xFF;
2607 	*(fatp + 2) = 0xFF;
2608 
2609 	if (Fatentsize == 16) {
2610 		*(fatp + 3) = 0xFF;
2611 	} else if (Fatentsize == 32) {
2612 		*(fatp + 3) = 0x0F;
2613 		*(fatp + 4) = 0xFF;
2614 		*(fatp + 5) = 0xFF;
2615 		*(fatp + 6) = 0xFF;
2616 		*(fatp + 7) = 0x0F;
2617 	}
2618 
2619 	/*
2620 	 * Keep track of clusters used.
2621 	 */
2622 	remclust = TotalClusters;
2623 	nextfree = 2;
2624 
2625 	/*
2626 	 * Get info on first file to install, if any.
2627 	 */
2628 	if (ffn)
2629 		*fffd = verify_firstfile(ffn, ffsize);
2630 
2631 	/*
2632 	 * Compute number of clusters to preserve for bootblk overage.
2633 	 * Remember that we already wrote the first sector of the boot block.
2634 	 * These clusters are marked BAD to prevent them from being deleted
2635 	 * or used.  The first available cluster is 2, so we always offset
2636 	 * the clusters.
2637 	 */
2638 	numsect = idivceil((bootblksize - BPSEC), BPSEC);
2639 	numclust = idivceil(numsect, wbpb->bpb.sectors_per_cluster);
2640 
2641 	if (Verbose && numclust)
2642 		(void) printf(gettext("Hiding %d excess bootblk cluster(s).\n"),
2643 		    numclust);
2644 	for (ci = 0; ci < numclust; ci++)
2645 		mark_cluster(fatp, nextfree++,
2646 			MakeFAT32 ? PCF_BADCLUSTER32 : PCF_BADCLUSTER);
2647 	remclust -= numclust;
2648 
2649 	/*
2650 	 * Reserve a cluster for the root directory on a FAT32.
2651 	 */
2652 	if (MakeFAT32) {
2653 		mark_cluster(fatp, nextfree, PCF_LASTCLUSTER32);
2654 		wbpb->bpb32.root_dir_clust = nextfree++;
2655 		remclust--;
2656 	}
2657 
2658 	/*
2659 	 * Compute and preserve number of clusters for first file.
2660 	 */
2661 	if (*fffd >= 0) {
2662 		*ffstartclust = nextfree;
2663 		numsect = idivceil(*ffsize, BPSEC);
2664 		numclust = idivceil(numsect, wbpb->bpb.sectors_per_cluster);
2665 
2666 		if (numclust > remclust) {
2667 			(void) fprintf(stderr,
2668 				gettext("Requested first file too large to be\n"
2669 					"installed in the new file system.\n"));
2670 			(void) close(*fffd);
2671 			*fffd = -1;
2672 			goto finish;
2673 		}
2674 
2675 		if (Verbose)
2676 			(void) printf(gettext("Reserving %d first file "
2677 			    "cluster(s).\n"), numclust);
2678 		for (ci = 0; (int)ci < (int)(numclust-1); ci++, nextfree++)
2679 			mark_cluster(fatp, nextfree, nextfree + 1);
2680 		mark_cluster(fatp, nextfree++,
2681 			MakeFAT32 ? PCF_LASTCLUSTER32 : PCF_LASTCLUSTER);
2682 		remclust -= numclust;
2683 	}
2684 
2685 finish:
2686 	if (Verbose) {
2687 		(void) printf(gettext("First sector of FAT"));
2688 		header_for_dump();
2689 		dump_bytes(fatp, BPSEC);
2690 	}
2691 
2692 	fsinfop->fs_signature = FAT32_FS_SIGN;
2693 	fsinfop->fs_free_clusters = remclust;
2694 	fsinfop->fs_next_cluster = nextfree;
2695 	return (fatp);
2696 }
2697 
2698 static
2699 void
2700 dirent_time_fill(struct pcdir *dep)
2701 {
2702 	struct  timeval tv;
2703 	struct	tm	*tp;
2704 	ushort_t	dostime;
2705 	ushort_t	dosday;
2706 
2707 	(void) gettimeofday(&tv, (struct timezone *)0);
2708 	tp = localtime(&tv.tv_sec);
2709 	/* get the time & day into DOS format */
2710 	dostime = tp->tm_sec / 2;
2711 	dostime |= tp->tm_min << 5;
2712 	dostime |= tp->tm_hour << 11;
2713 	dosday = tp->tm_mday;
2714 	dosday |= (tp->tm_mon + 1) << 5;
2715 	dosday |= (tp->tm_year - 80) << 9;
2716 	dep->pcd_mtime.pct_time = htols(dostime);
2717 	dep->pcd_mtime.pct_date = htols(dosday);
2718 }
2719 
2720 static
2721 void
2722 dirent_label_fill(struct pcdir *dep, char *fn)
2723 {
2724 	int nl, i;
2725 
2726 	/*
2727 	 * We spread the volume label across both the NAME and EXT fields
2728 	 */
2729 	nl = min(PCFNAMESIZE, strlen(fn));
2730 	for (i = 0; i < nl; i++) {
2731 		dep->pcd_filename[i] = toupper(fn[i]);
2732 	}
2733 	if (i < PCFNAMESIZE) {
2734 		for (; i < PCFNAMESIZE; i++)
2735 			dep->pcd_filename[i] = ' ';
2736 		for (i = 0; i < PCFEXTSIZE; i++)
2737 			dep->pcd_ext[i] = ' ';
2738 		return;
2739 	}
2740 	nl = min(PCFEXTSIZE, strlen(fn) - PCFNAMESIZE);
2741 	for (i = 0; i < nl; i++)
2742 		dep->pcd_ext[i] = toupper(fn[i + PCFNAMESIZE]);
2743 	if (i < PCFEXTSIZE) {
2744 		for (; i < PCFEXTSIZE; i++)
2745 			dep->pcd_ext[i] = ' ';
2746 	}
2747 }
2748 
2749 static
2750 void
2751 dirent_fname_fill(struct pcdir *dep, char *fn)
2752 {
2753 	char *fname, *fext;
2754 	int nl, i;
2755 
2756 	if (fname = strrchr(fn, '/')) {
2757 		fname++;
2758 	} else {
2759 		fname = fn;
2760 	}
2761 
2762 	if (fext = strrchr(fname, '.')) {
2763 		fext++;
2764 	} else {
2765 		fext = "";
2766 	}
2767 
2768 	fname = strtok(fname, ".");
2769 
2770 	nl = min(PCFNAMESIZE, (int)strlen(fname));
2771 	for (i = 0; i < nl; i++) {
2772 		dep->pcd_filename[i] = toupper(fname[i]);
2773 	}
2774 	for (; i < PCFNAMESIZE; i++) {
2775 		dep->pcd_filename[i] = ' ';
2776 	}
2777 
2778 	nl = min(PCFEXTSIZE, (int)strlen(fext));
2779 	for (i = 0; i < nl; i++) {
2780 		dep->pcd_ext[i] = toupper(fext[i]);
2781 	}
2782 	for (; i < PCFEXTSIZE; i++) {
2783 		dep->pcd_ext[i] = ' ';
2784 	}
2785 }
2786 
2787 static
2788 uchar_t *
2789 build_rootdir(bpb_t *wbpb, char *ffn, int fffd,
2790     ulong_t ffsize, pc_cluster32_t ffstart, ulong_t *rdirsize)
2791 {
2792 	struct pcdir *rootdirp;
2793 	struct pcdir *entry;
2794 
2795 	/*
2796 	 * Build a root directory.  It will have at least one entry,
2797 	 * the volume label and a second if the first file was defined.
2798 	 */
2799 	if (MakeFAT32) {
2800 		/*
2801 		 * We devote an entire cluster to the root
2802 		 * directory on FAT32.
2803 		 */
2804 		*rdirsize = wbpb->bpb.sectors_per_cluster * BPSEC;
2805 	} else {
2806 		*rdirsize = wbpb->bpb.num_root_entries * sizeof (struct pcdir);
2807 	}
2808 	if ((rootdirp = (struct pcdir *)malloc(*rdirsize)) == NULL) {
2809 		perror(gettext("Root directory allocation"));
2810 		exit(4);
2811 	} else {
2812 		entry = rootdirp;
2813 		(void) memset((char *)rootdirp, 0, *rdirsize);
2814 	}
2815 
2816 	/* Create directory entry for first file, if there is one */
2817 	if (fffd >= 0) {
2818 		dirent_fname_fill(entry, ffn);
2819 		entry->pcd_attr = Firstfileattr;
2820 		dirent_time_fill(entry);
2821 		entry->pcd_scluster_lo = htols(ffstart);
2822 		if (MakeFAT32) {
2823 			ffstart = ffstart >> 16;
2824 			entry->un.pcd_scluster_hi = htols(ffstart);
2825 		}
2826 		entry->pcd_size = htoli(ffsize);
2827 		entry++;
2828 	}
2829 
2830 	/* Create directory entry for volume label, if there is one */
2831 	if (Label != NULL) {
2832 		dirent_label_fill(entry, Label);
2833 		entry->pcd_attr = PCA_ARCH | PCA_LABEL;
2834 		dirent_time_fill(entry);
2835 		entry->pcd_scluster_lo = 0;
2836 		if (MakeFAT32) {
2837 			entry->un.pcd_scluster_hi = 0;
2838 		}
2839 		entry->pcd_size = 0;
2840 		entry++;
2841 	}
2842 
2843 	if (Verbose) {
2844 		(void) printf(gettext("First two directory entries"));
2845 		header_for_dump();
2846 		dump_bytes((uchar_t *)rootdirp, 2 * sizeof (struct pcdir));
2847 	}
2848 
2849 	return ((uchar_t *)rootdirp);
2850 }
2851 
2852 /*
2853  * write_rest
2854  *
2855  *	Write all the bytes from the current file pointer to end of file
2856  *	in the source file out to the destination file.  The writes should
2857  *	be padded to whole clusters with 0's if necessary.
2858  */
2859 static
2860 void
2861 write_rest(bpb_t *wbpb, char *efn, int dfd, int sfd, int remaining)
2862 {
2863 	char buf[BPSEC];
2864 	ushort_t numsect, numclust;
2865 	ushort_t wnumsect, s;
2866 	int doneread = 0;
2867 	int rstat;
2868 
2869 	/*
2870 	 * Compute number of clusters required to contain remaining bytes.
2871 	 */
2872 	numsect = idivceil(remaining, BPSEC);
2873 	numclust = idivceil(numsect, wbpb->bpb.sectors_per_cluster);
2874 
2875 	wnumsect = numclust * wbpb->bpb.sectors_per_cluster;
2876 	for (s = 0; s < wnumsect; s++) {
2877 		if (!doneread) {
2878 			if ((rstat = read(sfd, buf, BPSEC)) < 0) {
2879 				perror(efn);
2880 				doneread = 1;
2881 				rstat = 0;
2882 			} else if (rstat == 0) {
2883 				doneread = 1;
2884 			}
2885 			(void) memset(&(buf[rstat]), 0, BPSEC - rstat);
2886 		}
2887 		if (write(dfd, buf, BPSEC) != BPSEC) {
2888 			(void) fprintf(stderr, gettext("Copying "));
2889 			perror(efn);
2890 		}
2891 	}
2892 }
2893 
2894 static
2895 void
2896 write_fat32_bootstuff(int fd, boot_sector_t *bsp,
2897 	struct fat32_boot_fsinfo *fsinfop, off64_t seekto)
2898 {
2899 	uchar_t fsinfobuf[BPSEC];
2900 	uchar_t *fp;
2901 
2902 	/*
2903 	 * Construct the fsinfo buf
2904 	 */
2905 	(void) memset(fsinfobuf, 0, BPSEC);
2906 	/*
2907 	 * Not sure if this magic signature at the beginning of the
2908 	 * sector is necessary for us to create or not.  For now, I'm
2909 	 * going to assume it is, since I haven't seen any Windows FAT32s
2910 	 * without it.
2911 	 */
2912 	fsinfobuf[0] = 'R';
2913 	fsinfobuf[1] = 'R';
2914 	fsinfobuf[2] = 'a';
2915 	fsinfobuf[3] = 'A';
2916 	/*
2917 	 * We also appear to want the magic Boot sector signature at the
2918 	 * end of the sector.
2919 	 */
2920 	fsinfobuf[BPSEC - 2] = BOOTSECSIG & 0xFF;
2921 	fsinfobuf[BPSEC - 1] = (BOOTSECSIG >> 8) & 0xFF;
2922 
2923 	fp = &(fsinfobuf[FAT32_BOOT_FSINFO_OFF]);
2924 	fp += 4;	/* skip first reserved field */
2925 	store_32_bits(&fp, fsinfop->fs_signature);
2926 	store_32_bits(&fp, fsinfop->fs_free_clusters);
2927 	store_32_bits(&fp, fsinfop->fs_next_cluster);
2928 
2929 	if (Verbose) {
2930 		(void) printf(gettext("Dump of the fs info sector"));
2931 		header_for_dump();
2932 		dump_bytes(fsinfobuf, sizeof (fsinfobuf));
2933 	}
2934 
2935 	if (!Notreally) {
2936 		/*
2937 		 * FAT32's have an FS info sector, then a backup of the boot
2938 		 * sector, and a modified backup of the FS Info sector.
2939 		 */
2940 		if (write(fd, fsinfobuf, sizeof (fsinfobuf)) != BPSEC) {
2941 			perror(gettext("FS info sector write"));
2942 			exit(4);
2943 		}
2944 		if (lseek64(fd,	seekto + BKUP_BOOTSECT_OFFSET, SEEK_SET) < 0) {
2945 			(void) close(fd);
2946 			perror(gettext("Boot sector backup seek"));
2947 			exit(4);
2948 		}
2949 		if (write(fd, bsp->buf, sizeof (bsp->buf)) != BPSEC) {
2950 			perror(gettext("Boot sector backup write"));
2951 			exit(4);
2952 		}
2953 	}
2954 
2955 	/*
2956 	 * Second copy of fs info sector is modified to have "don't know"
2957 	 * as the number of free clusters
2958 	 */
2959 	fp = &(fsinfobuf[FAT32_BOOT_FSINFO_OFF]);
2960 	fp += 8;	/* skip first reserved field and signature */
2961 	store_32_bits(&fp, -1);
2962 
2963 	if (Verbose) {
2964 		(void) printf(gettext("Dump of the backup fs info sector"));
2965 		header_for_dump();
2966 		dump_bytes(fsinfobuf, sizeof (fsinfobuf));
2967 	}
2968 
2969 	if (!Notreally) {
2970 		if (write(fd, fsinfobuf, sizeof (fsinfobuf)) != BPSEC) {
2971 			perror(gettext("FS info sector backup write"));
2972 			exit(4);
2973 		}
2974 	}
2975 }
2976 
2977 static
2978 void
2979 write_bootsects(int fd, boot_sector_t *bsp, bpb_t *wbpb,
2980 	struct fat32_boot_fsinfo *fsinfop, off64_t seekto)
2981 {
2982 	if (MakeFAT32) {
2983 		/* Copy our BPB into bootsec structure */
2984 #ifdef i386
2985 		(void) memcpy(&(bsp->bs32.bs_front.bs_bpb), &(wbpb->bpb),
2986 			    sizeof (wbpb->bpb));
2987 		(void) memcpy(&(bsp->bs32.bs_bpb32), &(wbpb->bpb32),
2988 			    sizeof (wbpb->bpb32));
2989 		(void) memcpy(&(bsp->bs32.bs_ebpb), &(wbpb->ebpb),
2990 			    sizeof (wbpb->ebpb));
2991 #else
2992 		swap_pack_bpb32cpy(&(bsp->bs32), wbpb);
2993 #endif
2994 	} else {
2995 		/* Copy our BPB into bootsec structure */
2996 #ifdef i386
2997 		(void) memcpy(&(bsp->bs.bs_front.bs_bpb), &(wbpb->bpb),
2998 			    sizeof (wbpb->bpb));
2999 		(void) memcpy(&(bsp->bs.bs_ebpb), &(wbpb->ebpb),
3000 			    sizeof (wbpb->ebpb));
3001 #else
3002 		swap_pack_bpbcpy(&(bsp->bs), wbpb);
3003 #endif
3004 
3005 		/* Copy SUN BPB extensions into bootsec structure */
3006 		if (SunBPBfields) {
3007 #ifdef i386
3008 			(void) memcpy(&(bsp->bs.bs_sebpb), &(wbpb->sunbpb),
3009 				sizeof (wbpb->sunbpb));
3010 #else
3011 			swap_pack_sebpbcpy(&(bsp->bs), wbpb);
3012 #endif
3013 		}
3014 	}
3015 
3016 	/* Write boot sector */
3017 	if (!Notreally && write(fd, bsp->buf, sizeof (bsp->buf)) != BPSEC) {
3018 		perror(gettext("Boot sector write"));
3019 		exit(4);
3020 	}
3021 
3022 	if (Verbose) {
3023 		(void) printf(gettext("Dump of the boot sector"));
3024 		header_for_dump();
3025 		dump_bytes(bsp->buf, sizeof (bsp->buf));
3026 	}
3027 
3028 	if (MakeFAT32)
3029 		write_fat32_bootstuff(fd, bsp, fsinfop, seekto);
3030 }
3031 
3032 static
3033 void
3034 write_fat(int fd, off64_t seekto, char *fn, char *lbl, char *ffn, bpb_t *wbpb)
3035 {
3036 	struct fat32_boot_fsinfo fsinfo;
3037 	pc_cluster32_t ffsc;
3038 	boot_sector_t bootsect;
3039 	uchar_t *fatp, *rdirp;
3040 	ulong_t bootblksize, fatsize, rdirsize, ffsize;
3041 	int bsfd = -1;
3042 	int fffd = -1;
3043 
3044 	compute_file_area_size(wbpb);
3045 
3046 	bsfd = copy_bootblk(fn, &bootsect, &bootblksize);
3047 	label_volume(lbl, wbpb);
3048 
3049 	if (Verbose)
3050 		(void) printf(gettext("Building FAT.\n"));
3051 	fatp = build_fat(wbpb, &fsinfo, bootblksize, &fatsize,
3052 	    ffn, &fffd, &ffsize, &ffsc);
3053 
3054 	write_bootsects(fd, &bootsect, wbpb, &fsinfo, seekto);
3055 
3056 	if (lseek64(fd,
3057 	    seekto + (BPSEC * wbpb->bpb.resv_sectors), SEEK_SET) < 0) {
3058 		(void) close(fd);
3059 		perror(gettext("Seek to end of reserved sectors"));
3060 		exit(4);
3061 	}
3062 
3063 	/* Write FAT */
3064 	if (Verbose)
3065 		(void) printf(gettext("Writing FAT(s). %d bytes times %d.\n"),
3066 		    fatsize, wbpb->bpb.num_fats);
3067 	if (!Notreally) {
3068 		int nf, wb;
3069 		for (nf = 0; nf < (int)wbpb->bpb.num_fats; nf++)
3070 			if ((wb = write(fd, fatp, fatsize)) != fatsize) {
3071 				perror(gettext("FAT write"));
3072 				exit(4);
3073 			} else {
3074 				if (Verbose)
3075 					(void) printf(
3076 					    gettext("Wrote %d bytes\n"), wb);
3077 			}
3078 	}
3079 	free(fatp);
3080 
3081 	if (Verbose)
3082 		(void) printf(gettext("Building root directory.\n"));
3083 	rdirp = build_rootdir(wbpb, ffn, fffd, ffsize, ffsc, &rdirsize);
3084 
3085 	/*
3086 	 *  In non FAT32, root directory exists outside of the file area
3087 	 */
3088 	if (!MakeFAT32) {
3089 		if (Verbose)
3090 		    (void) printf(
3091 			gettext("Writing root directory. "
3092 				"%d bytes.\n"), rdirsize);
3093 		if (!Notreally) {
3094 			if (write(fd, rdirp, rdirsize) != rdirsize) {
3095 				perror(gettext("Root directory write"));
3096 				exit(4);
3097 			}
3098 		}
3099 		free(rdirp);
3100 	}
3101 
3102 	/*
3103 	 * Now write anything that needs to be in the file space.
3104 	 */
3105 	if (bootblksize > BPSEC) {
3106 		if (Verbose)
3107 			(void) printf(gettext("Writing remainder of "
3108 				"boot block.\n"));
3109 		if (!Notreally)
3110 			write_rest(wbpb, fn, fd, bsfd, bootblksize - BPSEC);
3111 	}
3112 
3113 	if (MakeFAT32) {
3114 		if (Verbose)
3115 		    (void) printf(
3116 			gettext("Writing root directory. "
3117 				"%d bytes.\n"), rdirsize);
3118 		if (!Notreally) {
3119 			if (write(fd, rdirp, rdirsize) != rdirsize) {
3120 				perror(gettext("Root directory write"));
3121 				exit(4);
3122 			}
3123 		}
3124 		free(rdirp);
3125 	}
3126 
3127 	if (fffd >= 0) {
3128 		if (Verbose)
3129 			(void) printf(gettext("Writing first file.\n"));
3130 		if (!Notreally)
3131 			write_rest(wbpb, ffn, fd, fffd, ffsize);
3132 	}
3133 }
3134 
3135 static
3136 char *LegalOpts[] = {
3137 #define	NFLAG 0
3138 	"N",
3139 #define	VFLAG 1
3140 	"v",
3141 #define	RFLAG 2
3142 	"r",
3143 #define	HFLAG 3
3144 	"h",
3145 #define	SFLAG 4
3146 	"s",
3147 #define	SUNFLAG 5
3148 	"S",
3149 #define	LABFLAG 6
3150 	"b",
3151 #define	BTRFLAG 7
3152 	"B",
3153 #define	INITFLAG 8
3154 	"i",
3155 #define	SZFLAG 9
3156 	"size",
3157 #define	SECTFLAG 10
3158 	"nsect",
3159 #define	TRKFLAG 11
3160 	"ntrack",
3161 #define	SPCFLAG 12
3162 	"spc",
3163 #define	BPFFLAG 13
3164 	"fat",
3165 #define	FFLAG 14
3166 	"f",
3167 #define	DFLAG 15
3168 	"d",
3169 #define	NOFDISKFLAG 16
3170 	"nofdisk",
3171 #define	RESRVFLAG 17
3172 	"reserve",
3173 #define	HIDDENFLAG 18
3174 	"hidden",
3175 	NULL
3176 };
3177 
3178 static
3179 void
3180 bad_arg(char *option)
3181 {
3182 	(void) fprintf(stderr,
3183 		gettext("Unrecognized option %s.\n"), option);
3184 	usage();
3185 	exit(2);
3186 }
3187 
3188 static
3189 void
3190 missing_arg(char *option)
3191 {
3192 	(void) fprintf(stderr,
3193 		gettext("Option %s requires a value.\n"), option);
3194 	usage();
3195 	exit(3);
3196 }
3197 
3198 static
3199 void
3200 parse_suboptions(char *optsstr)
3201 {
3202 	char *value;
3203 	int c;
3204 
3205 	while (*optsstr != '\0') {
3206 		switch (c = getsubopt(&optsstr, LegalOpts, &value)) {
3207 		case NFLAG:
3208 			Notreally++;
3209 			break;
3210 		case VFLAG:
3211 			Verbose++;
3212 			break;
3213 		case RFLAG:
3214 			Firstfileattr |= 0x01;
3215 			break;
3216 		case HFLAG:
3217 			Firstfileattr |= 0x02;
3218 			break;
3219 		case SFLAG:
3220 			Firstfileattr |= 0x04;
3221 			break;
3222 		case SUNFLAG:
3223 			SunBPBfields = 1;
3224 			break;
3225 		case LABFLAG:
3226 			if (value == NULL) {
3227 				missing_arg(LegalOpts[c]);
3228 			} else {
3229 				Label = value;
3230 			}
3231 			break;
3232 		case BTRFLAG:
3233 			if (value == NULL) {
3234 				missing_arg(LegalOpts[c]);
3235 			} else {
3236 				BootBlkFn = value;
3237 			}
3238 			break;
3239 		case INITFLAG:
3240 			if (value == NULL) {
3241 				missing_arg(LegalOpts[c]);
3242 			} else {
3243 				FirstFn = value;
3244 			}
3245 			break;
3246 		case SZFLAG:
3247 			if (value == NULL) {
3248 				missing_arg(LegalOpts[c]);
3249 			} else {
3250 				TotSize = atoi(value);
3251 				GetSize = 0;
3252 			}
3253 			break;
3254 		case SECTFLAG:
3255 			if (value == NULL) {
3256 				missing_arg(LegalOpts[c]);
3257 			} else {
3258 				SecPerTrk = atoi(value);
3259 				GetSPT = 0;
3260 			}
3261 			break;
3262 		case TRKFLAG:
3263 			if (value == NULL) {
3264 				missing_arg(LegalOpts[c]);
3265 			} else {
3266 				TrkPerCyl = atoi(value);
3267 				GetTPC = 0;
3268 			}
3269 			break;
3270 		case SPCFLAG:
3271 			if (value == NULL) {
3272 				missing_arg(LegalOpts[c]);
3273 			} else {
3274 				SecPerClust = atoi(value);
3275 				GetSPC = 0;
3276 			}
3277 			break;
3278 		case BPFFLAG:
3279 			if (value == NULL) {
3280 				missing_arg(LegalOpts[c]);
3281 			} else {
3282 				BitsPerFAT = atoi(value);
3283 				GetBPF = 0;
3284 			}
3285 			break;
3286 		case NOFDISKFLAG:
3287 			DontUseFdisk = 1;
3288 			break;
3289 		case RESRVFLAG:
3290 			if (value == NULL) {
3291 				missing_arg(LegalOpts[c]);
3292 			} else {
3293 				Resrvd = atoi(value);
3294 				GetResrvd = 0;
3295 			}
3296 			break;
3297 		case HIDDENFLAG:
3298 			if (value == NULL) {
3299 				missing_arg(LegalOpts[c]);
3300 			} else {
3301 				RelOffset = atoi(value);
3302 				GetOffset = 0;
3303 			}
3304 			break;
3305 		case FFLAG:
3306 			if (value == NULL) {
3307 				missing_arg(LegalOpts[c]);
3308 			} else {
3309 				DiskName = value;
3310 				Outputtofile = 1;
3311 			}
3312 			break;
3313 		case DFLAG:
3314 			if (value == NULL) {
3315 				missing_arg(LegalOpts[c]);
3316 			} else {
3317 				Imagesize = atoi(value);
3318 			}
3319 			break;
3320 		default:
3321 			bad_arg(value);
3322 			break;
3323 		}
3324 	}
3325 }
3326 
3327 static
3328 void
3329 sanity_check_options(int argc, int optind)
3330 {
3331 	if (GetFsParams) {
3332 		if (argc - optind != 1)
3333 			usage();
3334 		return;
3335 	}
3336 
3337 	if (DontUseFdisk && GetOffset) {
3338 		/* Set default relative offset of zero */
3339 		RelOffset = 0;
3340 	}
3341 
3342 	if (BitsPerFAT == 32)
3343 		MakeFAT32 = 1;
3344 
3345 	if (Outputtofile && (argc - optind)) {
3346 		usage();
3347 	} else if (Outputtofile && !DiskName) {
3348 		usage();
3349 	} else if (!Outputtofile && (argc - optind != 1)) {
3350 		usage();
3351 	} else if (SunBPBfields && !BootBlkFn) {
3352 		(void) fprintf(stderr,
3353 		    gettext("Use of the 'S' option requires that\n"
3354 			    "the 'B=' option also be used.\n\n"));
3355 		usage();
3356 	} else if (Firstfileattr != 0x20 && !FirstFn) {
3357 		(void) fprintf(stderr,
3358 		    gettext("Use of the 'r', 'h', or 's' options requires\n"
3359 			    "that the 'i=' option also be used.\n\n"));
3360 		usage();
3361 	} else if (!GetOffset && !DontUseFdisk) {
3362 		(void) fprintf(stderr,
3363 		    gettext("Use of the 'hidden' option requires that\n"
3364 			    "the 'nofdisk' option also be used.\n\n"));
3365 		usage();
3366 	} else if (DontUseFdisk && GetSize) {
3367 		(void) fprintf(stderr,
3368 		    gettext("Use of the 'nofdisk' option requires that\n"
3369 			    "the 'size=' option also be used.\n\n"));
3370 		usage();
3371 	} else if (!GetBPF &&
3372 		    BitsPerFAT != 12 && BitsPerFAT != 16 && BitsPerFAT != 32) {
3373 		(void) fprintf(stderr,
3374 		    gettext("Invalid Bits/Fat value."
3375 			    "  Must be 12, 16 or 32.\n"));
3376 		exit(2);
3377 	} else if (!GetSPC && !powerofx_le_y(2, 128, SecPerClust)) {
3378 		(void) fprintf(stderr,
3379 		    gettext("Invalid Sectors/Cluster value.  Must be a "
3380 			    "power of 2 between 1 and 128.\n"));
3381 		exit(2);
3382 	} else if (!GetResrvd && (Resrvd < 1 || Resrvd > 0xffff)) {
3383 		(void) fprintf(stderr,
3384 		    gettext("Invalid number of reserved sectors.  "
3385 			"Must be at least 1 but\nno larger than 65535."));
3386 		exit(2);
3387 	} else if (!GetResrvd && MakeFAT32 &&
3388 		    (Resrvd < 32 || Resrvd > 0xffff)) {
3389 		(void) fprintf(stderr,
3390 		    gettext("Invalid number of reserved sectors.  "
3391 			"Must be at least 32 but\nno larger than 65535."));
3392 		exit(2);
3393 	} else if (Imagesize != 3 && Imagesize != 5) {
3394 		usage();
3395 	}
3396 }
3397 
3398 int
3399 main(int argc, char **argv)
3400 {
3401 	off64_t AbsBootSect = 0;
3402 	bpb_t dskparamblk;
3403 	char *string;
3404 	int  fd;
3405 	int  c;
3406 
3407 	(void) setlocale(LC_ALL, "");
3408 
3409 #if !defined(TEXT_DOMAIN)
3410 #define	TEXT_DOMAIN "SYS_TEST"
3411 #endif
3412 	(void) textdomain(TEXT_DOMAIN);
3413 
3414 	while ((c = getopt(argc, argv, "F:Vmo:")) != EOF) {
3415 		switch (c) {
3416 		case 'F':
3417 			string = optarg;
3418 			if (strcmp(string, "pcfs") != 0)
3419 				usage();
3420 			break;
3421 		case 'V':
3422 			{
3423 				char	*opt_text;
3424 				int	opt_count;
3425 
3426 				(void) fprintf(stdout,
3427 				    gettext("mkfs -F pcfs "));
3428 				for (opt_count = 1; opt_count < argc;
3429 								opt_count++) {
3430 					opt_text = argv[opt_count];
3431 					if (opt_text)
3432 					    (void) fprintf(stdout, " %s ",
3433 								opt_text);
3434 				}
3435 				(void) fprintf(stdout, "\n");
3436 			}
3437 			break;
3438 		case 'm':
3439 			GetFsParams++;
3440 			break;
3441 		case 'o':
3442 			string = optarg;
3443 			parse_suboptions(string);
3444 			break;
3445 		}
3446 	}
3447 
3448 	sanity_check_options(argc, optind);
3449 
3450 	if (!Outputtofile)
3451 		DiskName = argv[optind];
3452 
3453 	(void) memset(&dskparamblk, 0, sizeof (dskparamblk));
3454 
3455 	if (GetFsParams) {
3456 		fd = open_and_examine(DiskName, &dskparamblk);
3457 	} else {
3458 		fd = open_and_seek(DiskName, &dskparamblk, &AbsBootSect);
3459 		if (ask_nicely(DiskName))
3460 			write_fat(fd, AbsBootSect, BootBlkFn, Label,
3461 			    FirstFn, &dskparamblk);
3462 	}
3463 	(void) close(fd);
3464 	return (0);
3465 }
3466