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