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