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