mkfs_msdos.c revision 363317
1/*
2 * Copyright (c) 1998 Robert Nordier
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in
12 *    the documentation and/or other materials provided with the
13 *    distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
21 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
25 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#ifndef lint
29static const char rcsid[] =
30  "$FreeBSD: stable/11/sbin/newfs_msdos/mkfs_msdos.c 363317 2020-07-19 03:02:46Z delphij $";
31#endif /* not lint */
32
33#include <sys/param.h>
34#include <sys/fdcio.h>
35#include <sys/disk.h>
36#include <sys/disklabel.h>
37#include <sys/mount.h>
38#include <sys/stat.h>
39#include <sys/sysctl.h>
40#include <sys/time.h>
41
42#include <assert.h>
43#include <ctype.h>
44#include <err.h>
45#include <errno.h>
46#include <fcntl.h>
47#include <inttypes.h>
48#include <paths.h>
49#include <signal.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <time.h>
54#include <unistd.h>
55
56#include "mkfs_msdos.h"
57
58#define	MAXU16	  0xffff	/* maximum unsigned 16-bit quantity */
59#define	BPN	  4		/* bits per nibble */
60#define	NPB	  2		/* nibbles per byte */
61
62#define	DOSMAGIC  0xaa55	/* DOS magic number */
63#define	MINBPS	  512		/* minimum bytes per sector */
64#define	MAXBPS    4096		/* maximum bytes per sector */
65#define	MAXSPC	  128		/* maximum sectors per cluster */
66#define	MAXNFT	  16		/* maximum number of FATs */
67#define	DEFBLK	  4096		/* default block size */
68#define	DEFBLK16  2048		/* default block size FAT16 */
69#define	DEFRDE	  512		/* default root directory entries */
70#define	RESFTE	  2		/* reserved FAT entries */
71#define	MINCLS12  1U		/* minimum FAT12 clusters */
72#define	MINCLS16  0xff5U	/* minimum FAT16 clusters */
73#define	MINCLS32  0xfff5U	/* minimum FAT32 clusters */
74#define	MAXCLS12  0xff4U	/* maximum FAT12 clusters */
75#define	MAXCLS16  0xfff4U	/* maximum FAT16 clusters */
76#define	MAXCLS32  0xffffff4U	/* maximum FAT32 clusters */
77
78#define	mincls(fat)  ((fat) == 12 ? MINCLS12 :	\
79		      (fat) == 16 ? MINCLS16 :	\
80				    MINCLS32)
81
82#define	maxcls(fat)  ((fat) == 12 ? MAXCLS12 :	\
83		      (fat) == 16 ? MAXCLS16 :	\
84				    MAXCLS32)
85
86#define	mk1(p, x)				\
87    (p) = (u_int8_t)(x)
88
89#define	mk2(p, x)				\
90    (p)[0] = (u_int8_t)(x),			\
91    (p)[1] = (u_int8_t)((x) >> 010)
92
93#define	mk4(p, x)				\
94    (p)[0] = (u_int8_t)(x),			\
95    (p)[1] = (u_int8_t)((x) >> 010),		\
96    (p)[2] = (u_int8_t)((x) >> 020),		\
97    (p)[3] = (u_int8_t)((x) >> 030)
98
99struct bs {
100    u_int8_t bsJump[3];			/* bootstrap entry point */
101    u_int8_t bsOemName[8];		/* OEM name and version */
102} __packed;
103
104struct bsbpb {
105    u_int8_t bpbBytesPerSec[2];		/* bytes per sector */
106    u_int8_t bpbSecPerClust;		/* sectors per cluster */
107    u_int8_t bpbResSectors[2];		/* reserved sectors */
108    u_int8_t bpbFATs;			/* number of FATs */
109    u_int8_t bpbRootDirEnts[2];		/* root directory entries */
110    u_int8_t bpbSectors[2];		/* total sectors */
111    u_int8_t bpbMedia;			/* media descriptor */
112    u_int8_t bpbFATsecs[2];		/* sectors per FAT */
113    u_int8_t bpbSecPerTrack[2];		/* sectors per track */
114    u_int8_t bpbHeads[2];		/* drive heads */
115    u_int8_t bpbHiddenSecs[4];		/* hidden sectors */
116    u_int8_t bpbHugeSectors[4];		/* big total sectors */
117} __packed;
118
119struct bsxbpb {
120    u_int8_t bpbBigFATsecs[4];		/* big sectors per FAT */
121    u_int8_t bpbExtFlags[2];		/* FAT control flags */
122    u_int8_t bpbFSVers[2];		/* file system version */
123    u_int8_t bpbRootClust[4];		/* root directory start cluster */
124    u_int8_t bpbFSInfo[2];		/* file system info sector */
125    u_int8_t bpbBackup[2];		/* backup boot sector */
126    u_int8_t bpbReserved[12];		/* reserved */
127} __packed;
128
129struct bsx {
130    u_int8_t exDriveNumber;		/* drive number */
131    u_int8_t exReserved1;		/* reserved */
132    u_int8_t exBootSignature;		/* extended boot signature */
133    u_int8_t exVolumeID[4];		/* volume ID number */
134    u_int8_t exVolumeLabel[11];		/* volume label */
135    u_int8_t exFileSysType[8];		/* file system type */
136} __packed;
137
138struct de {
139    u_int8_t deName[11];		/* name and extension */
140    u_int8_t deAttributes;		/* attributes */
141    u_int8_t rsvd[10];			/* reserved */
142    u_int8_t deMTime[2];		/* last-modified time */
143    u_int8_t deMDate[2];		/* last-modified date */
144    u_int8_t deStartCluster[2];		/* starting cluster */
145    u_int8_t deFileSize[4];		/* size */
146} __packed;
147
148struct bpb {
149    u_int bpbBytesPerSec;		/* bytes per sector */
150    u_int bpbSecPerClust;		/* sectors per cluster */
151    u_int bpbResSectors;		/* reserved sectors */
152    u_int bpbFATs;			/* number of FATs */
153    u_int bpbRootDirEnts;		/* root directory entries */
154    u_int bpbSectors;			/* total sectors */
155    u_int bpbMedia;			/* media descriptor */
156    u_int bpbFATsecs;			/* sectors per FAT */
157    u_int bpbSecPerTrack;		/* sectors per track */
158    u_int bpbHeads;			/* drive heads */
159    u_int bpbHiddenSecs;		/* hidden sectors */
160    u_int bpbHugeSectors; 		/* big total sectors */
161    u_int bpbBigFATsecs; 		/* big sectors per FAT */
162    u_int bpbRootClust; 		/* root directory start cluster */
163    u_int bpbFSInfo; 			/* file system info sector */
164    u_int bpbBackup; 			/* backup boot sector */
165};
166
167#define	BPBGAP 0, 0, 0, 0, 0, 0
168
169static struct {
170    const char *name;
171    struct bpb bpb;
172} const stdfmt[] = {
173    {"160",  {512, 1, 1, 2,  64,  320, 0xfe, 1,  8, 1, BPBGAP}},
174    {"180",  {512, 1, 1, 2,  64,  360, 0xfc, 2,  9, 1, BPBGAP}},
175    {"320",  {512, 2, 1, 2, 112,  640, 0xff, 1,  8, 2, BPBGAP}},
176    {"360",  {512, 2, 1, 2, 112,  720, 0xfd, 2,  9, 2, BPBGAP}},
177    {"640",  {512, 2, 1, 2, 112, 1280, 0xfb, 2,  8, 2, BPBGAP}},
178    {"720",  {512, 2, 1, 2, 112, 1440, 0xf9, 3,  9, 2, BPBGAP}},
179    {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}},
180    {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2,  8, 2, BPBGAP}},
181    {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}},
182    {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}}
183};
184
185static const u_int8_t bootcode[] = {
186    0xfa,			/* cli		    */
187    0x31, 0xc0, 		/* xor	   ax,ax    */
188    0x8e, 0xd0, 		/* mov	   ss,ax    */
189    0xbc, 0x00, 0x7c,		/* mov	   sp,7c00h */
190    0xfb,			/* sti		    */
191    0x8e, 0xd8, 		/* mov	   ds,ax    */
192    0xe8, 0x00, 0x00,		/* call    $ + 3    */
193    0x5e,			/* pop	   si	    */
194    0x83, 0xc6, 0x19,		/* add	   si,+19h  */
195    0xbb, 0x07, 0x00,		/* mov	   bx,0007h */
196    0xfc,			/* cld		    */
197    0xac,			/* lodsb	    */
198    0x84, 0xc0, 		/* test    al,al    */
199    0x74, 0x06, 		/* jz	   $ + 8    */
200    0xb4, 0x0e, 		/* mov	   ah,0eh   */
201    0xcd, 0x10, 		/* int	   10h	    */
202    0xeb, 0xf5, 		/* jmp	   $ - 9    */
203    0x30, 0xe4, 		/* xor	   ah,ah    */
204    0xcd, 0x16, 		/* int	   16h	    */
205    0xcd, 0x19, 		/* int	   19h	    */
206    0x0d, 0x0a,
207    'N', 'o', 'n', '-', 's', 'y', 's', 't',
208    'e', 'm', ' ', 'd', 'i', 's', 'k',
209    0x0d, 0x0a,
210    'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
211    'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
212    ' ', 'r', 'e', 'b', 'o', 'o', 't',
213    0x0d, 0x0a,
214    0
215};
216
217static volatile sig_atomic_t got_siginfo;
218static void infohandler(int);
219
220static int check_mounted(const char *, mode_t);
221static ssize_t getchunksize(void);
222static int getstdfmt(const char *, struct bpb *);
223static int getdiskinfo(int, const char *, const char *, int, struct bpb *);
224static void print_bpb(struct bpb *);
225static int ckgeom(const char *, u_int, const char *);
226static void mklabel(u_int8_t *, const char *);
227static int oklabel(const char *);
228static void setstr(u_int8_t *, const char *, size_t);
229
230int
231mkfs_msdos(const char *fname, const char *dtype, const struct msdos_options *op)
232{
233    char buf[MAXPATHLEN];
234    struct sigaction si_sa;
235    struct stat sb;
236    struct timeval tv;
237    struct bpb bpb;
238    struct tm *tm;
239    struct bs *bs;
240    struct bsbpb *bsbpb;
241    struct bsxbpb *bsxbpb;
242    struct bsx *bsx;
243    struct de *de;
244    u_int8_t *img;
245    u_int8_t *physbuf, *physbuf_end;
246    const char *bname;
247    ssize_t n;
248    time_t now;
249    u_int fat, bss, rds, cls, dir, lsn, x, x1, x2;
250    u_int extra_res, alignment, saved_x, attempts=0;
251    bool set_res, set_spf, set_spc;
252    int fd, fd1, rv;
253    struct msdos_options o = *op;
254    ssize_t chunksize;
255
256    physbuf = NULL;
257    rv = -1;
258    fd = fd1 = -1;
259
260    if (o.block_size && o.sectors_per_cluster) {
261	warnx("Cannot specify both block size and sectors per cluster");
262	goto done;
263    }
264    if (o.OEM_string && strlen(o.OEM_string) > 8) {
265	warnx("%s: bad OEM string", o.OEM_string);
266	goto done;
267    }
268    if (o.create_size) {
269	if (o.no_create) {
270	    warnx("create (-C) is incompatible with -N");
271	    goto done;
272	}
273	fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
274	if (fd == -1) {
275	    warnx("failed to create %s", fname);
276	    goto done;
277	}
278	if (ftruncate(fd, o.create_size)) {
279	    warnx("failed to initialize %jd bytes", (intmax_t)o.create_size);
280	    goto done;
281	}
282    } else if ((fd = open(fname, o.no_create ? O_RDONLY : O_RDWR)) == -1) {
283	warn("%s", fname);
284	goto done;
285    }
286    if (fstat(fd, &sb)) {
287	warn("%s", fname);
288	goto done;
289    }
290    if (o.create_size) {
291	if (!S_ISREG(sb.st_mode))
292	    warnx("warning, %s is not a regular file", fname);
293    } else {
294	if (!S_ISCHR(sb.st_mode))
295	    warnx("warning, %s is not a character device", fname);
296    }
297    if (!o.no_create)
298	if (check_mounted(fname, sb.st_mode) == -1)
299	    goto done;
300    if (o.offset && o.offset != lseek(fd, o.offset, SEEK_SET)) {
301	warnx("cannot seek to %jd", (intmax_t)o.offset);
302	goto done;
303    }
304    memset(&bpb, 0, sizeof(bpb));
305    if (o.floppy) {
306	if (getstdfmt(o.floppy, &bpb) == -1)
307	    goto done;
308	bpb.bpbHugeSectors = bpb.bpbSectors;
309	bpb.bpbSectors = 0;
310	bpb.bpbBigFATsecs = bpb.bpbFATsecs;
311	bpb.bpbFATsecs = 0;
312    }
313    if (o.drive_heads)
314	bpb.bpbHeads = o.drive_heads;
315    if (o.sectors_per_track)
316	bpb.bpbSecPerTrack = o.sectors_per_track;
317    if (o.bytes_per_sector)
318	bpb.bpbBytesPerSec = o.bytes_per_sector;
319    if (o.size)
320	bpb.bpbHugeSectors = o.size;
321    if (o.hidden_sectors_set)
322	bpb.bpbHiddenSecs = o.hidden_sectors;
323    if (!(o.floppy || (o.drive_heads && o.sectors_per_track &&
324	o.bytes_per_sector && o.size && o.hidden_sectors_set))) {
325	if (getdiskinfo(fd, fname, dtype, o.hidden_sectors_set, &bpb) == -1)
326		goto done;
327	bpb.bpbHugeSectors -= (o.offset / bpb.bpbBytesPerSec);
328	if (bpb.bpbSecPerClust == 0) {	/* set defaults */
329	    if (bpb.bpbHugeSectors <= 6000)	/* about 3MB -> 512 bytes */
330		bpb.bpbSecPerClust = 1;
331	    else if (bpb.bpbHugeSectors <= (1<<17)) /* 64M -> 4k */
332		bpb.bpbSecPerClust = 8;
333	    else if (bpb.bpbHugeSectors <= (1<<19)) /* 256M -> 8k */
334		bpb.bpbSecPerClust = 16;
335	    else if (bpb.bpbHugeSectors <= (1<<21)) /* 1G -> 16k */
336		bpb.bpbSecPerClust = 32;
337	    else
338		bpb.bpbSecPerClust = 64;		/* otherwise 32k */
339	}
340    }
341    if (bpb.bpbBytesPerSec < MINBPS ||
342        bpb.bpbBytesPerSec > MAXBPS ||
343	!powerof2(bpb.bpbBytesPerSec)) {
344	warnx("Invalid bytes/sector (%u): must be 512, 1024, 2048 or 4096",
345	    bpb.bpbBytesPerSec);
346	goto done;
347    }
348
349    if (o.volume_label && !oklabel(o.volume_label)) {
350	warnx("%s: bad volume label", o.volume_label);
351	goto done;
352    }
353    if (!(fat = o.fat_type)) {
354	if (o.floppy)
355	    fat = 12;
356	else if (!o.directory_entries && (o.info_sector || o.backup_sector))
357	    fat = 32;
358    }
359    if ((fat == 32 && o.directory_entries) || (fat != 32 && (o.info_sector || o.backup_sector))) {
360	warnx("-%c is not a legal FAT%s option",
361	     fat == 32 ? 'e' : o.info_sector ? 'i' : 'k',
362	     fat == 32 ? "32" : "12/16");
363	goto done;
364    }
365    if (o.floppy && fat == 32)
366	bpb.bpbRootDirEnts = 0;
367    if (fat != 0 && fat != 12 && fat != 16 && fat != 32) {
368	warnx("%d: bad FAT type", fat);
369	goto done;
370    }
371
372    if (o.block_size) {
373	if (!powerof2(o.block_size)) {
374	    warnx("block size (%u) is not a power of 2", o.block_size);
375	    goto done;
376	}
377	if (o.block_size < bpb.bpbBytesPerSec) {
378	    warnx("block size (%u) is too small; minimum is %u",
379		 o.block_size, bpb.bpbBytesPerSec);
380	    goto done;
381	}
382	if (o.block_size > bpb.bpbBytesPerSec * MAXSPC) {
383	    warnx("block size (%u) is too large; maximum is %u",
384		 o.block_size, bpb.bpbBytesPerSec * MAXSPC);
385	    goto done;
386	}
387	bpb.bpbSecPerClust = o.block_size / bpb.bpbBytesPerSec;
388    }
389    if (o.sectors_per_cluster) {
390	if (!powerof2(o.sectors_per_cluster)) {
391	    warnx("sectors/cluster (%u) is not a power of 2",
392		o.sectors_per_cluster);
393	    goto done;
394	}
395	bpb.bpbSecPerClust = o.sectors_per_cluster;
396    }
397    if (o.reserved_sectors)
398	bpb.bpbResSectors = o.reserved_sectors;
399    if (o.num_FAT) {
400	if (o.num_FAT > MAXNFT) {
401	    warnx("number of FATs (%u) is too large; maximum is %u",
402		 o.num_FAT, MAXNFT);
403	    goto done;
404	}
405	bpb.bpbFATs = o.num_FAT;
406    }
407    if (o.directory_entries)
408	bpb.bpbRootDirEnts = o.directory_entries;
409    if (o.media_descriptor_set) {
410	if (o.media_descriptor < 0xf0) {
411	    warnx("illegal media descriptor (%#x)", o.media_descriptor);
412	    goto done;
413	}
414	bpb.bpbMedia = o.media_descriptor;
415    }
416    if (o.sectors_per_fat)
417	bpb.bpbBigFATsecs = o.sectors_per_fat;
418    if (o.info_sector)
419	bpb.bpbFSInfo = o.info_sector;
420    if (o.backup_sector)
421	bpb.bpbBackup = o.backup_sector;
422    bss = 1;
423    bname = NULL;
424    fd1 = -1;
425    if (o.bootstrap) {
426	bname = o.bootstrap;
427	if (!strchr(bname, '/')) {
428	    snprintf(buf, sizeof(buf), "/boot/%s", bname);
429	    bname = buf;
430	}
431	if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb)) {
432	    warn("%s", bname);
433	    goto done;
434	}
435	if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bpbBytesPerSec ||
436	    sb.st_size < bpb.bpbBytesPerSec ||
437	    sb.st_size > bpb.bpbBytesPerSec * MAXU16) {
438	    warnx("%s: inappropriate file type or format", bname);
439	    goto done;
440	}
441	bss = sb.st_size / bpb.bpbBytesPerSec;
442    }
443    if (!bpb.bpbFATs)
444	bpb.bpbFATs = 2;
445    if (!fat) {
446	if (bpb.bpbHugeSectors < (bpb.bpbResSectors ? bpb.bpbResSectors : bss) +
447	    howmany((RESFTE + (bpb.bpbSecPerClust ? MINCLS16 : MAXCLS12 + 1)) *
448		(bpb.bpbSecPerClust ? 16 : 12) / BPN,
449		bpb.bpbBytesPerSec * NPB) *
450	    bpb.bpbFATs +
451	    howmany(bpb.bpbRootDirEnts ? bpb.bpbRootDirEnts : DEFRDE,
452		    bpb.bpbBytesPerSec / sizeof(struct de)) +
453	    (bpb.bpbSecPerClust ? MINCLS16 : MAXCLS12 + 1) *
454	    (bpb.bpbSecPerClust ? bpb.bpbSecPerClust :
455	     howmany(DEFBLK, bpb.bpbBytesPerSec)))
456	    fat = 12;
457	else if (bpb.bpbRootDirEnts || bpb.bpbHugeSectors <
458		 (bpb.bpbResSectors ? bpb.bpbResSectors : bss) +
459		 howmany((RESFTE + MAXCLS16) * 2, bpb.bpbBytesPerSec) *
460		 bpb.bpbFATs +
461		 howmany(DEFRDE, bpb.bpbBytesPerSec / sizeof(struct de)) +
462		 (MAXCLS16 + 1) *
463		 (bpb.bpbSecPerClust ? bpb.bpbSecPerClust :
464		  howmany(8192, bpb.bpbBytesPerSec)))
465	    fat = 16;
466	else
467	    fat = 32;
468    }
469    x = bss;
470    if (fat == 32) {
471	if (!bpb.bpbFSInfo) {
472	    if (x == MAXU16 || x == bpb.bpbBackup) {
473		warnx("no room for info sector");
474		goto done;
475	    }
476	    bpb.bpbFSInfo = x;
477	}
478	if (bpb.bpbFSInfo != MAXU16 && x <= bpb.bpbFSInfo)
479	    x = bpb.bpbFSInfo + 1;
480	if (!bpb.bpbBackup) {
481	    if (x == MAXU16) {
482		warnx("no room for backup sector");
483		goto done;
484	    }
485	    bpb.bpbBackup = x;
486	} else if (bpb.bpbBackup != MAXU16 && bpb.bpbBackup == bpb.bpbFSInfo) {
487	    warnx("backup sector would overwrite info sector");
488	    goto done;
489	}
490	if (bpb.bpbBackup != MAXU16 && x <= bpb.bpbBackup)
491	    x = bpb.bpbBackup + 1;
492    }
493
494    extra_res = 0;
495    alignment = 0;
496    set_res = (bpb.bpbResSectors == 0);
497    set_spf = (bpb.bpbBigFATsecs == 0);
498    set_spc = (bpb.bpbSecPerClust == 0);
499    saved_x = x;
500
501    /*
502     * Attempt to align the root directory to cluster if o.align is set.
503     * This is done by padding with reserved blocks. Note that this can
504     * cause other factors to change, which can in turn change the alignment.
505     * This should take at most 2 iterations, as increasing the reserved
506     * amount may cause the FAT size to decrease by 1, requiring another
507     * bpbFATs reserved blocks. If bpbSecPerClust changes, it will
508     * be half of its previous size, and thus will not throw off alignment.
509     */
510    do {
511	x = saved_x;
512	if (set_res)
513	    bpb.bpbResSectors = ((fat == 32) ?
514		MAX(x, MAX(16384 / bpb.bpbBytesPerSec, 4)) : x) + extra_res;
515	else if (bpb.bpbResSectors < x) {
516	    warnx("too few reserved sectors (need %d have %d)", x,
517		bpb.bpbResSectors);
518	    goto done;
519	}
520	if (fat != 32 && !bpb.bpbRootDirEnts)
521	    bpb.bpbRootDirEnts = DEFRDE;
522	rds = howmany(bpb.bpbRootDirEnts,
523	    bpb.bpbBytesPerSec / sizeof(struct de));
524	if (set_spc) {
525	    for (bpb.bpbSecPerClust = howmany(fat == 16 ? DEFBLK16 :
526		    DEFBLK, bpb.bpbBytesPerSec);
527		bpb.bpbSecPerClust < MAXSPC && (bpb.bpbResSectors +
528		    howmany((RESFTE + maxcls(fat)) * (fat / BPN),
529			bpb.bpbBytesPerSec * NPB) * bpb.bpbFATs +
530		    rds +
531		    (u_int64_t) (maxcls(fat) + 1) * bpb.bpbSecPerClust) <=
532		    bpb.bpbHugeSectors;
533		bpb.bpbSecPerClust <<= 1)
534		    continue;
535
536	}
537	if (fat != 32 && bpb.bpbBigFATsecs > MAXU16) {
538	    warnx("too many sectors/FAT for FAT12/16");
539	    goto done;
540	}
541	x1 = bpb.bpbResSectors + rds;
542	x = bpb.bpbBigFATsecs ? bpb.bpbBigFATsecs : 1;
543	if (x1 + (u_int64_t)x * bpb.bpbFATs > bpb.bpbHugeSectors) {
544	    warnx("meta data exceeds file system size");
545	    goto done;
546	}
547	x1 += x * bpb.bpbFATs;
548	x = (u_int64_t)(bpb.bpbHugeSectors - x1) * bpb.bpbBytesPerSec * NPB /
549	    (bpb.bpbSecPerClust * bpb.bpbBytesPerSec * NPB +
550	    fat / BPN * bpb.bpbFATs);
551	x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN),
552	    bpb.bpbBytesPerSec * NPB);
553	if (set_spf) {
554	    if (bpb.bpbBigFATsecs == 0)
555		bpb.bpbBigFATsecs = x2;
556	    x1 += (bpb.bpbBigFATsecs - 1) * bpb.bpbFATs;
557	}
558	if (set_res) {
559	    /* attempt to align root directory */
560	    alignment = (bpb.bpbResSectors + bpb.bpbBigFATsecs * bpb.bpbFATs) %
561		bpb.bpbSecPerClust;
562	    if (o.align)
563		extra_res += bpb.bpbSecPerClust - alignment;
564	}
565	attempts++;
566    } while (o.align && alignment != 0 && attempts < 2);
567    if (o.align && alignment != 0)
568	warnx("warning: Alignment failed.");
569
570    cls = (bpb.bpbHugeSectors - x1) / bpb.bpbSecPerClust;
571    x = (u_int64_t)bpb.bpbBigFATsecs * bpb.bpbBytesPerSec * NPB / (fat / BPN) -
572	RESFTE;
573    if (cls > x)
574	cls = x;
575    if (bpb.bpbBigFATsecs < x2)
576	warnx("warning: sectors/FAT limits file system to %u clusters",
577	      cls);
578    if (cls < mincls(fat)) {
579	warnx("%u clusters too few clusters for FAT%u, need %u", cls, fat,
580	    mincls(fat));
581	goto done;
582    }
583    if (cls > maxcls(fat)) {
584	cls = maxcls(fat);
585	bpb.bpbHugeSectors = x1 + (cls + 1) * bpb.bpbSecPerClust - 1;
586	warnx("warning: FAT type limits file system to %u sectors",
587	      bpb.bpbHugeSectors);
588    }
589    printf("%s: %u sector%s in %u FAT%u cluster%s "
590	   "(%u bytes/cluster)\n", fname, cls * bpb.bpbSecPerClust,
591	   cls * bpb.bpbSecPerClust == 1 ? "" : "s", cls, fat,
592	   cls == 1 ? "" : "s", bpb.bpbBytesPerSec * bpb.bpbSecPerClust);
593    if (!bpb.bpbMedia)
594	bpb.bpbMedia = !bpb.bpbHiddenSecs ? 0xf0 : 0xf8;
595    if (fat == 32)
596	bpb.bpbRootClust = RESFTE;
597    if (bpb.bpbHugeSectors <= MAXU16) {
598	bpb.bpbSectors = bpb.bpbHugeSectors;
599	bpb.bpbHugeSectors = 0;
600    }
601    if (fat != 32) {
602	bpb.bpbFATsecs = bpb.bpbBigFATsecs;
603	bpb.bpbBigFATsecs = 0;
604    }
605    print_bpb(&bpb);
606    if (!o.no_create) {
607	if (o.timestamp_set) {
608	    tv.tv_sec = now = o.timestamp;
609	    tv.tv_usec = 0;
610	    tm = gmtime(&now);
611	} else {
612	    gettimeofday(&tv, NULL);
613	    now = tv.tv_sec;
614	    tm = localtime(&now);
615	}
616
617	chunksize = getchunksize();
618	physbuf = malloc(chunksize);
619	if (physbuf == NULL) {
620	    warn(NULL);
621	    goto done;
622	}
623	physbuf_end = physbuf + chunksize;
624	img = physbuf;
625
626	dir = bpb.bpbResSectors + (bpb.bpbFATsecs ? bpb.bpbFATsecs :
627				   bpb.bpbBigFATsecs) * bpb.bpbFATs;
628	memset(&si_sa, 0, sizeof(si_sa));
629	si_sa.sa_handler = infohandler;
630	if (sigaction(SIGINFO, &si_sa, NULL) == -1) {
631	    warn("sigaction SIGINFO");
632	    goto done;
633	}
634	for (lsn = 0; lsn < dir + (fat == 32 ? bpb.bpbSecPerClust : rds); lsn++) {
635	    if (got_siginfo) {
636		    fprintf(stderr,"%s: writing sector %u of %u (%u%%)\n",
637			fname, lsn,
638			(dir + (fat == 32 ? bpb.bpbSecPerClust: rds)),
639			(lsn * 100) / (dir +
640			    (fat == 32 ? bpb.bpbSecPerClust: rds)));
641		    got_siginfo = 0;
642	    }
643	    x = lsn;
644	    if (o.bootstrap &&
645		fat == 32 && bpb.bpbBackup != MAXU16 &&
646		bss <= bpb.bpbBackup && x >= bpb.bpbBackup) {
647		x -= bpb.bpbBackup;
648		if (!x && lseek(fd1, o.offset, SEEK_SET)) {
649		    warn("%s", bname);
650		    goto done;
651		}
652	    }
653	    if (o.bootstrap && x < bss) {
654		if ((n = read(fd1, img, bpb.bpbBytesPerSec)) == -1) {
655		    warn("%s", bname);
656		    goto done;
657		}
658		if ((unsigned)n != bpb.bpbBytesPerSec) {
659		    warnx("%s: can't read sector %u", bname, x);
660		    goto done;
661		}
662	    } else
663		memset(img, 0, bpb.bpbBytesPerSec);
664	    if (!lsn ||
665		(fat == 32 && bpb.bpbBackup != MAXU16 &&
666		 lsn == bpb.bpbBackup)) {
667		x1 = sizeof(struct bs);
668		bsbpb = (struct bsbpb *)(img + x1);
669		mk2(bsbpb->bpbBytesPerSec, bpb.bpbBytesPerSec);
670		mk1(bsbpb->bpbSecPerClust, bpb.bpbSecPerClust);
671		mk2(bsbpb->bpbResSectors, bpb.bpbResSectors);
672		mk1(bsbpb->bpbFATs, bpb.bpbFATs);
673		mk2(bsbpb->bpbRootDirEnts, bpb.bpbRootDirEnts);
674		mk2(bsbpb->bpbSectors, bpb.bpbSectors);
675		mk1(bsbpb->bpbMedia, bpb.bpbMedia);
676		mk2(bsbpb->bpbFATsecs, bpb.bpbFATsecs);
677		mk2(bsbpb->bpbSecPerTrack, bpb.bpbSecPerTrack);
678		mk2(bsbpb->bpbHeads, bpb.bpbHeads);
679		mk4(bsbpb->bpbHiddenSecs, bpb.bpbHiddenSecs);
680		mk4(bsbpb->bpbHugeSectors, bpb.bpbHugeSectors);
681		x1 += sizeof(struct bsbpb);
682		if (fat == 32) {
683		    bsxbpb = (struct bsxbpb *)(img + x1);
684		    mk4(bsxbpb->bpbBigFATsecs, bpb.bpbBigFATsecs);
685		    mk2(bsxbpb->bpbExtFlags, 0);
686		    mk2(bsxbpb->bpbFSVers, 0);
687		    mk4(bsxbpb->bpbRootClust, bpb.bpbRootClust);
688		    mk2(bsxbpb->bpbFSInfo, bpb.bpbFSInfo);
689		    mk2(bsxbpb->bpbBackup, bpb.bpbBackup);
690		    x1 += sizeof(struct bsxbpb);
691		}
692		bsx = (struct bsx *)(img + x1);
693		mk1(bsx->exBootSignature, 0x29);
694		if (o.volume_id_set)
695		    x = o.volume_id;
696		else
697		    x = (((u_int)(1 + tm->tm_mon) << 8 |
698			  (u_int)tm->tm_mday) +
699			 ((u_int)tm->tm_sec << 8 |
700			  (u_int)(tv.tv_usec / 10))) << 16 |
701			((u_int)(1900 + tm->tm_year) +
702			 ((u_int)tm->tm_hour << 8 |
703			  (u_int)tm->tm_min));
704		mk4(bsx->exVolumeID, x);
705		mklabel(bsx->exVolumeLabel, o.volume_label ? o.volume_label : "NO NAME");
706		snprintf(buf, sizeof(buf), "FAT%u", fat);
707		setstr(bsx->exFileSysType, buf, sizeof(bsx->exFileSysType));
708		if (!o.bootstrap) {
709		    x1 += sizeof(struct bsx);
710		    bs = (struct bs *)img;
711		    mk1(bs->bsJump[0], 0xeb);
712		    mk1(bs->bsJump[1], x1 - 2);
713		    mk1(bs->bsJump[2], 0x90);
714		    setstr(bs->bsOemName, o.OEM_string ? o.OEM_string : "BSD4.4  ",
715			   sizeof(bs->bsOemName));
716		    memcpy(img + x1, bootcode, sizeof(bootcode));
717		    mk2(img + MINBPS - 2, DOSMAGIC);
718		}
719	    } else if (fat == 32 && bpb.bpbFSInfo != MAXU16 &&
720		       (lsn == bpb.bpbFSInfo ||
721			(bpb.bpbBackup != MAXU16 &&
722			 lsn == bpb.bpbBackup + bpb.bpbFSInfo))) {
723		mk4(img, 0x41615252);
724		mk4(img + MINBPS - 28, 0x61417272);
725		mk4(img + MINBPS - 24, 0xffffffff);
726		mk4(img + MINBPS - 20, 0xffffffff);
727		mk2(img + MINBPS - 2, DOSMAGIC);
728	    } else if (lsn >= bpb.bpbResSectors && lsn < dir &&
729		       !((lsn - bpb.bpbResSectors) %
730			 (bpb.bpbFATsecs ? bpb.bpbFATsecs :
731			  bpb.bpbBigFATsecs))) {
732		mk1(img[0], bpb.bpbMedia);
733		for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++)
734		    mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff);
735	    } else if (lsn == dir && o.volume_label) {
736		de = (struct de *)img;
737		mklabel(de->deName, o.volume_label);
738		mk1(de->deAttributes, 050);
739		x = (u_int)tm->tm_hour << 11 |
740		    (u_int)tm->tm_min << 5 |
741		    (u_int)tm->tm_sec >> 1;
742		mk2(de->deMTime, x);
743		x = (u_int)(tm->tm_year - 80) << 9 |
744		    (u_int)(tm->tm_mon + 1) << 5 |
745		    (u_int)tm->tm_mday;
746		mk2(de->deMDate, x);
747	    }
748	    /*
749	     * Issue a write of chunksize once we have collected
750	     * enough sectors.
751	     */
752	    img += bpb.bpbBytesPerSec;
753	    if (img >= physbuf_end) {
754		n = write(fd, physbuf, chunksize);
755		if (n != chunksize) {
756		    warnx("%s: can't write sector %u", fname, lsn);
757		    goto done;
758		}
759		img = physbuf;
760	    }
761	}
762	/*
763	 * Write remaining sectors, if the last write didn't end
764	 * up filling a whole chunk.
765	 */
766	if (img != physbuf) {
767		ssize_t tailsize = img - physbuf;
768
769		n = write(fd, physbuf, tailsize);
770		if (n != tailsize) {
771		    warnx("%s: can't write sector %u", fname, lsn);
772		    goto done;
773		}
774	}
775    }
776    rv = 0;
777done:
778    free(physbuf);
779    if (fd != -1)
780	    close(fd);
781    if (fd1 != -1)
782	    close(fd1);
783
784    return rv;
785}
786
787/*
788 * return -1 with error if file system is mounted.
789 */
790static int
791check_mounted(const char *fname, mode_t mode)
792{
793    struct statfs *mp;
794    const char *s1, *s2;
795    size_t len;
796    int n, r;
797
798    if (!(n = getmntinfo(&mp, MNT_NOWAIT))) {
799	warn("getmntinfo");
800	return -1;
801    }
802    len = strlen(_PATH_DEV);
803    s1 = fname;
804    if (!strncmp(s1, _PATH_DEV, len))
805	s1 += len;
806    r = S_ISCHR(mode) && s1 != fname && *s1 == 'r';
807    for (; n--; mp++) {
808	s2 = mp->f_mntfromname;
809	if (!strncmp(s2, _PATH_DEV, len))
810	    s2 += len;
811	if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) ||
812	    !strcmp(s1, s2)) {
813	    warnx("%s is mounted on %s", fname, mp->f_mntonname);
814	    return -1;
815	}
816    }
817    return 0;
818}
819
820/*
821 * Get optimal I/O size
822 */
823static ssize_t
824getchunksize(void)
825{
826	static int chunksize;
827
828	if (chunksize != 0)
829		return ((ssize_t)chunksize);
830
831#ifdef	KERN_MAXPHYS
832	int mib[2];
833	size_t len;
834
835	mib[0] = CTL_KERN;
836	mib[1] = KERN_MAXPHYS;
837	len = sizeof(chunksize);
838
839	if (sysctl(mib, 2, &chunksize, &len, NULL, 0) == -1) {
840		warn("sysctl: KERN_MAXPHYS, using %zu", (size_t)MAXPHYS);
841		chunksize = 0;
842	}
843#endif
844	if (chunksize == 0)
845		chunksize = MAXPHYS;
846
847	/*
848	 * For better performance, we want to write larger chunks instead of
849	 * individual sectors (the size can only be 512, 1024, 2048 or 4096
850	 * bytes). Assert that chunksize can always hold an integer number of
851	 * sectors by asserting that both are power of two numbers and the
852	 * chunksize is greater than MAXBPS.
853	 */
854	static_assert(powerof2(MAXBPS), "MAXBPS is not power of 2");
855	assert(powerof2(chunksize));
856	assert(chunksize > MAXBPS);
857
858	return ((ssize_t)chunksize);
859}
860
861/*
862 * Get a standard format.
863 */
864static int
865getstdfmt(const char *fmt, struct bpb *bpb)
866{
867    u_int x, i;
868
869    x = nitems(stdfmt);
870    for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++);
871    if (i == x) {
872	warnx("%s: unknown standard format", fmt);
873	return -1;
874    }
875    *bpb = stdfmt[i].bpb;
876    return 0;
877}
878
879/*
880 * Get disk slice, partition, and geometry information.
881 */
882static int
883getdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag,
884	    struct bpb *bpb)
885{
886    struct disklabel *lp, dlp;
887    struct fd_type type;
888    off_t ms, hs = 0;
889
890    lp = NULL;
891
892    /* If the user specified a disk type, try to use that */
893    if (dtype != NULL) {
894	lp = getdiskbyname(dtype);
895    }
896
897    /* Maybe it's a floppy drive */
898    if (lp == NULL) {
899	if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) {
900	    struct stat st;
901
902	    if (fstat(fd, &st))
903		err(1, "cannot get disk size");
904	    /* create a fake geometry for a file image */
905	    ms = st.st_size;
906	    dlp.d_secsize = 512;
907	    dlp.d_nsectors = 63;
908	    dlp.d_ntracks = 255;
909	    dlp.d_secperunit = ms / dlp.d_secsize;
910	    lp = &dlp;
911	} else if (ioctl(fd, FD_GTYPE, &type) != -1) {
912	    dlp.d_secsize = 128 << type.secsize;
913	    dlp.d_nsectors = type.sectrac;
914	    dlp.d_ntracks = type.heads;
915	    dlp.d_secperunit = ms / dlp.d_secsize;
916	    lp = &dlp;
917	}
918    }
919
920    /* Maybe it's a fixed drive */
921    if (lp == NULL) {
922	if (bpb->bpbBytesPerSec)
923	    dlp.d_secsize = bpb->bpbBytesPerSec;
924	if (bpb->bpbBytesPerSec == 0 && ioctl(fd, DIOCGSECTORSIZE,
925					      &dlp.d_secsize) == -1)
926	    err(1, "cannot get sector size");
927
928	dlp.d_secperunit = ms / dlp.d_secsize;
929
930	if (bpb->bpbSecPerTrack == 0 && ioctl(fd, DIOCGFWSECTORS,
931					      &dlp.d_nsectors) == -1) {
932	    warn("cannot get number of sectors per track");
933	    dlp.d_nsectors = 63;
934	}
935	if (bpb->bpbHeads == 0 &&
936	    ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) {
937	    warn("cannot get number of heads");
938	    if (dlp.d_secperunit <= 63*1*1024)
939		dlp.d_ntracks = 1;
940	    else if (dlp.d_secperunit <= 63*16*1024)
941		dlp.d_ntracks = 16;
942	    else
943		dlp.d_ntracks = 255;
944	}
945
946	hs = (ms / dlp.d_secsize) - dlp.d_secperunit;
947	lp = &dlp;
948    }
949
950    if (bpb->bpbBytesPerSec == 0) {
951	if (ckgeom(fname, lp->d_secsize, "bytes/sector") == -1)
952	    return -1;
953	bpb->bpbBytesPerSec = lp->d_secsize;
954    }
955    if (bpb->bpbSecPerTrack == 0) {
956	if (ckgeom(fname, lp->d_nsectors, "sectors/track") == -1)
957	    return -1;
958	bpb->bpbSecPerTrack = lp->d_nsectors;
959    }
960    if (bpb->bpbHeads == 0) {
961	if (ckgeom(fname, lp->d_ntracks, "drive heads") == -1)
962	    return -1;
963	bpb->bpbHeads = lp->d_ntracks;
964    }
965    if (bpb->bpbHugeSectors == 0)
966	bpb->bpbHugeSectors = lp->d_secperunit;
967    if (bpb->bpbHiddenSecs == 0)
968	bpb->bpbHiddenSecs = hs;
969    return 0;
970}
971
972/*
973 * Print out BPB values.
974 */
975static void
976print_bpb(struct bpb *bpb)
977{
978    printf("BytesPerSec=%u SecPerClust=%u ResSectors=%u FATs=%u",
979	   bpb->bpbBytesPerSec, bpb->bpbSecPerClust, bpb->bpbResSectors,
980	   bpb->bpbFATs);
981    if (bpb->bpbRootDirEnts)
982	printf(" RootDirEnts=%u", bpb->bpbRootDirEnts);
983    if (bpb->bpbSectors)
984	printf(" Sectors=%u", bpb->bpbSectors);
985    printf(" Media=%#x", bpb->bpbMedia);
986    if (bpb->bpbFATsecs)
987	printf(" FATsecs=%u", bpb->bpbFATsecs);
988    printf(" SecPerTrack=%u Heads=%u HiddenSecs=%u", bpb->bpbSecPerTrack,
989	   bpb->bpbHeads, bpb->bpbHiddenSecs);
990    if (bpb->bpbHugeSectors)
991	printf(" HugeSectors=%u", bpb->bpbHugeSectors);
992    if (!bpb->bpbFATsecs) {
993	printf(" FATsecs=%u RootCluster=%u", bpb->bpbBigFATsecs,
994	       bpb->bpbRootClust);
995	printf(" FSInfo=");
996	printf(bpb->bpbFSInfo == MAXU16 ? "%#x" : "%u", bpb->bpbFSInfo);
997	printf(" Backup=");
998	printf(bpb->bpbBackup == MAXU16 ? "%#x" : "%u", bpb->bpbBackup);
999    }
1000    printf("\n");
1001}
1002
1003/*
1004 * Check a disk geometry value.
1005 */
1006static int
1007ckgeom(const char *fname, u_int val, const char *msg)
1008{
1009    if (!val) {
1010	warnx("%s: no default %s", fname, msg);
1011	return -1;
1012    }
1013    if (val > MAXU16) {
1014	warnx("%s: illegal %s %d", fname, msg, val);
1015	return -1;
1016    }
1017    return 0;
1018}
1019
1020/*
1021 * Check a volume label.
1022 */
1023static int
1024oklabel(const char *src)
1025{
1026    int c, i;
1027
1028    for (i = 0; i <= 11; i++) {
1029	c = (u_char)*src++;
1030	if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
1031	    break;
1032    }
1033    return i && !c;
1034}
1035
1036/*
1037 * Make a volume label.
1038 */
1039static void
1040mklabel(u_int8_t *dest, const char *src)
1041{
1042    int c, i;
1043
1044    for (i = 0; i < 11; i++) {
1045	c = *src ? toupper(*src++) : ' ';
1046	*dest++ = !i && c == '\xe5' ? 5 : c;
1047    }
1048}
1049
1050/*
1051 * Copy string, padding with spaces.
1052 */
1053static void
1054setstr(u_int8_t *dest, const char *src, size_t len)
1055{
1056    while (len--)
1057	*dest++ = *src ? *src++ : ' ';
1058}
1059
1060static void
1061infohandler(int sig __unused)
1062{
1063
1064	got_siginfo = 1;
1065}
1066