newfs_msdos.c revision 289368
11638Srgrimes/*
21638Srgrimes * Copyright (c) 1998 Robert Nordier
31638Srgrimes * All rights reserved.
41638Srgrimes *
51638Srgrimes * Redistribution and use in source and binary forms, with or without
61638Srgrimes * modification, are permitted provided that the following conditions
71638Srgrimes * are met:
81638Srgrimes * 1. Redistributions of source code must retain the above copyright
91638Srgrimes *    notice, this list of conditions and the following disclaimer.
101638Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111638Srgrimes *    notice, this list of conditions and the following disclaimer in
121638Srgrimes *    the documentation and/or other materials provided with the
131638Srgrimes *    distribution.
141638Srgrimes *
151638Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
161638Srgrimes * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
171638Srgrimes * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
181638Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
191638Srgrimes * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
201638Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
211638Srgrimes * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
221638Srgrimes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
231638Srgrimes * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
241638Srgrimes * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
251638Srgrimes * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
261638Srgrimes */
271638Srgrimes
281638Srgrimes#ifndef lint
291638Srgrimesstatic const char rcsid[] =
301638Srgrimes  "$FreeBSD: stable/10/sbin/newfs_msdos/newfs_msdos.c 289368 2015-10-15 16:07:15Z emaste $";
311638Srgrimes#endif /* not lint */
321638Srgrimes
3321410Sache#include <sys/param.h>
341638Srgrimes#include <sys/fdcio.h>
351638Srgrimes#include <sys/disk.h>
361638Srgrimes#include <sys/disklabel.h>
371638Srgrimes#include <sys/mount.h>
381638Srgrimes#include <sys/stat.h>
391638Srgrimes#include <sys/time.h>
401638Srgrimes
411638Srgrimes#include <ctype.h>
421638Srgrimes#include <err.h>
431638Srgrimes#include <errno.h>
441638Srgrimes#include <fcntl.h>
451638Srgrimes#include <inttypes.h>
461638Srgrimes#include <paths.h>
471638Srgrimes#include <signal.h>
481638Srgrimes#include <stdio.h>
491638Srgrimes#include <stdlib.h>
501638Srgrimes#include <string.h>
511638Srgrimes#include <time.h>
521638Srgrimes#include <unistd.h>
531638Srgrimes
541638Srgrimes#define	MAXU16	  0xffff	/* maximum unsigned 16-bit quantity */
551638Srgrimes#define	BPN	  4		/* bits per nibble */
561638Srgrimes#define	NPB	  2		/* nibbles per byte */
571638Srgrimes
581638Srgrimes#define	DOSMAGIC  0xaa55	/* DOS magic number */
591638Srgrimes#define	MINBPS	  512		/* minimum bytes per sector */
601638Srgrimes#define	MAXSPC	  128		/* maximum sectors per cluster */
611638Srgrimes#define	MAXNFT	  16		/* maximum number of FATs */
621638Srgrimes#define	DEFBLK	  4096		/* default block size */
631638Srgrimes#define	DEFBLK16  2048		/* default block size FAT16 */
641638Srgrimes#define	DEFRDE	  512		/* default root directory entries */
651638Srgrimes#define	RESFTE	  2		/* reserved FAT entries */
661638Srgrimes#define	MINCLS12  1U		/* minimum FAT12 clusters */
671638Srgrimes#define	MINCLS16  0xff5U	/* minimum FAT16 clusters */
681638Srgrimes#define	MINCLS32  0xfff5U	/* minimum FAT32 clusters */
691638Srgrimes#define	MAXCLS12  0xff4U	/* maximum FAT12 clusters */
701638Srgrimes#define	MAXCLS16  0xfff4U	/* maximum FAT16 clusters */
711638Srgrimes#define	MAXCLS32  0xffffff4U	/* maximum FAT32 clusters */
721638Srgrimes
731638Srgrimes#define	mincls(fat)  ((fat) == 12 ? MINCLS12 :	\
741638Srgrimes		      (fat) == 16 ? MINCLS16 :	\
751638Srgrimes				    MINCLS32)
761638Srgrimes
771638Srgrimes#define	maxcls(fat)  ((fat) == 12 ? MAXCLS12 :	\
781638Srgrimes		      (fat) == 16 ? MAXCLS16 :	\
791638Srgrimes				    MAXCLS32)
801638Srgrimes
811638Srgrimes#define	mk1(p, x)				\
821638Srgrimes    (p) = (u_int8_t)(x)
831638Srgrimes
841638Srgrimes#define	mk2(p, x)				\
851638Srgrimes    (p)[0] = (u_int8_t)(x),			\
861638Srgrimes    (p)[1] = (u_int8_t)((x) >> 010)
871638Srgrimes
881638Srgrimes#define	mk4(p, x)				\
891638Srgrimes    (p)[0] = (u_int8_t)(x),			\
901638Srgrimes    (p)[1] = (u_int8_t)((x) >> 010),		\
911638Srgrimes    (p)[2] = (u_int8_t)((x) >> 020),		\
921638Srgrimes    (p)[3] = (u_int8_t)((x) >> 030)
931638Srgrimes
941638Srgrimes#define	argto1(arg, lo, msg)  argtou(arg, lo, 0xff, msg)
951638Srgrimes#define	argto2(arg, lo, msg)  argtou(arg, lo, 0xffff, msg)
961638Srgrimes#define	argto4(arg, lo, msg)  argtou(arg, lo, 0xffffffff, msg)
971638Srgrimes#define	argtox(arg, lo, msg)  argtou(arg, lo, UINT_MAX, msg)
981638Srgrimes
991638Srgrimesstruct bs {
1001638Srgrimes    u_int8_t bsJump[3];			/* bootstrap entry point */
1011638Srgrimes    u_int8_t bsOemName[8];		/* OEM name and version */
1021638Srgrimes} __packed;
1031638Srgrimes
1041638Srgrimesstruct bsbpb {
1051638Srgrimes    u_int8_t bpbBytesPerSec[2];		/* bytes per sector */
1061638Srgrimes    u_int8_t bpbSecPerClust;		/* sectors per cluster */
1071638Srgrimes    u_int8_t bpbResSectors[2];		/* reserved sectors */
1081638Srgrimes    u_int8_t bpbFATs;			/* number of FATs */
1091638Srgrimes    u_int8_t bpbRootDirEnts[2];		/* root directory entries */
1101638Srgrimes    u_int8_t bpbSectors[2];		/* total sectors */
1111638Srgrimes    u_int8_t bpbMedia;			/* media descriptor */
1121638Srgrimes    u_int8_t bpbFATsecs[2];		/* sectors per FAT */
1131638Srgrimes    u_int8_t bpbSecPerTrack[2];		/* sectors per track */
1141638Srgrimes    u_int8_t bpbHeads[2];		/* drive heads */
1151638Srgrimes    u_int8_t bpbHiddenSecs[4];		/* hidden sectors */
1161638Srgrimes    u_int8_t bpbHugeSectors[4];		/* big total sectors */
1171638Srgrimes} __packed;
1181638Srgrimes
1191638Srgrimesstruct bsxbpb {
1201638Srgrimes    u_int8_t bpbBigFATsecs[4];		/* big sectors per FAT */
1211638Srgrimes    u_int8_t bpbExtFlags[2];		/* FAT control flags */
1221638Srgrimes    u_int8_t bpbFSVers[2];		/* file system version */
1231638Srgrimes    u_int8_t bpbRootClust[4];		/* root directory start cluster */
1241638Srgrimes    u_int8_t bpbFSInfo[2];		/* file system info sector */
1251638Srgrimes    u_int8_t bpbBackup[2];		/* backup boot sector */
1261638Srgrimes    u_int8_t bpbReserved[12];		/* reserved */
1271638Srgrimes} __packed;
1281638Srgrimes
1291638Srgrimesstruct bsx {
1301638Srgrimes    u_int8_t exDriveNumber;		/* drive number */
1311638Srgrimes    u_int8_t exReserved1;		/* reserved */
1321638Srgrimes    u_int8_t exBootSignature;		/* extended boot signature */
1331638Srgrimes    u_int8_t exVolumeID[4];		/* volume ID number */
1341638Srgrimes    u_int8_t exVolumeLabel[11];		/* volume label */
1351638Srgrimes    u_int8_t exFileSysType[8];		/* file system type */
1361638Srgrimes} __packed;
1371638Srgrimes
1381638Srgrimesstruct de {
1391638Srgrimes    u_int8_t deName[11];		/* name and extension */
1401638Srgrimes    u_int8_t deAttributes;		/* attributes */
1411638Srgrimes    u_int8_t rsvd[10];			/* reserved */
1421638Srgrimes    u_int8_t deMTime[2];		/* last-modified time */
1431638Srgrimes    u_int8_t deMDate[2];		/* last-modified date */
1441638Srgrimes    u_int8_t deStartCluster[2];		/* starting cluster */
1451638Srgrimes    u_int8_t deFileSize[4];		/* size */
1461638Srgrimes} __packed;
1471638Srgrimes
1481638Srgrimesstruct bpb {
1491638Srgrimes    u_int bpbBytesPerSec;		/* bytes per sector */
1501638Srgrimes    u_int bpbSecPerClust;		/* sectors per cluster */
1511638Srgrimes    u_int bpbResSectors;		/* reserved sectors */
1521638Srgrimes    u_int bpbFATs;			/* number of FATs */
1531638Srgrimes    u_int bpbRootDirEnts;		/* root directory entries */
1541638Srgrimes    u_int bpbSectors;			/* total sectors */
1551638Srgrimes    u_int bpbMedia;			/* media descriptor */
1561638Srgrimes    u_int bpbFATsecs;			/* sectors per FAT */
1571638Srgrimes    u_int bpbSecPerTrack;		/* sectors per track */
1581638Srgrimes    u_int bpbHeads;			/* drive heads */
1591638Srgrimes    u_int bpbHiddenSecs;		/* hidden sectors */
1601638Srgrimes    u_int bpbHugeSectors; 		/* big total sectors */
1611638Srgrimes    u_int bpbBigFATsecs; 		/* big sectors per FAT */
1621638Srgrimes    u_int bpbRootClust; 		/* root directory start cluster */
1631638Srgrimes    u_int bpbFSInfo; 			/* file system info sector */
1641638Srgrimes    u_int bpbBackup; 			/* backup boot sector */
1651638Srgrimes};
1661638Srgrimes
1671638Srgrimes#define	BPBGAP 0, 0, 0, 0, 0, 0
1681638Srgrimes
1691638Srgrimesstatic struct {
1701638Srgrimes    const char *name;
1711638Srgrimes    struct bpb bpb;
1721638Srgrimes} const stdfmt[] = {
1731638Srgrimes    {"160",  {512, 1, 1, 2,  64,  320, 0xfe, 1,  8, 1, BPBGAP}},
1741638Srgrimes    {"180",  {512, 1, 1, 2,  64,  360, 0xfc, 2,  9, 1, BPBGAP}},
1751638Srgrimes    {"320",  {512, 2, 1, 2, 112,  640, 0xff, 1,  8, 2, BPBGAP}},
1761638Srgrimes    {"360",  {512, 2, 1, 2, 112,  720, 0xfd, 2,  9, 2, BPBGAP}},
1771638Srgrimes    {"640",  {512, 2, 1, 2, 112, 1280, 0xfb, 2,  8, 2, BPBGAP}},
1781638Srgrimes    {"720",  {512, 2, 1, 2, 112, 1440, 0xf9, 3,  9, 2, BPBGAP}},
1791638Srgrimes    {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}},
1801638Srgrimes    {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2,  8, 2, BPBGAP}},
1811638Srgrimes    {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}},
1821638Srgrimes    {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}}
1831638Srgrimes};
1841638Srgrimes
1851638Srgrimesstatic const u_int8_t bootcode[] = {
1861638Srgrimes    0xfa,			/* cli		    */
1871638Srgrimes    0x31, 0xc0, 		/* xor	   ax,ax    */
1881638Srgrimes    0x8e, 0xd0, 		/* mov	   ss,ax    */
1891638Srgrimes    0xbc, 0x00, 0x7c,		/* mov	   sp,7c00h */
1901638Srgrimes    0xfb,			/* sti		    */
1911638Srgrimes    0x8e, 0xd8, 		/* mov	   ds,ax    */
1921638Srgrimes    0xe8, 0x00, 0x00,		/* call    $ + 3    */
1931638Srgrimes    0x5e,			/* pop	   si	    */
1941638Srgrimes    0x83, 0xc6, 0x19,		/* add	   si,+19h  */
1951638Srgrimes    0xbb, 0x07, 0x00,		/* mov	   bx,0007h */
1961638Srgrimes    0xfc,			/* cld		    */
1971638Srgrimes    0xac,			/* lodsb	    */
1981638Srgrimes    0x84, 0xc0, 		/* test    al,al    */
1991638Srgrimes    0x74, 0x06, 		/* jz	   $ + 8    */
2001638Srgrimes    0xb4, 0x0e, 		/* mov	   ah,0eh   */
2011638Srgrimes    0xcd, 0x10, 		/* int	   10h	    */
2021638Srgrimes    0xeb, 0xf5, 		/* jmp	   $ - 9    */
2031638Srgrimes    0x30, 0xe4, 		/* xor	   ah,ah    */
2041638Srgrimes    0xcd, 0x16, 		/* int	   16h	    */
2051638Srgrimes    0xcd, 0x19, 		/* int	   19h	    */
2061638Srgrimes    0x0d, 0x0a,
2071638Srgrimes    'N', 'o', 'n', '-', 's', 'y', 's', 't',
2081638Srgrimes    'e', 'm', ' ', 'd', 'i', 's', 'k',
2091638Srgrimes    0x0d, 0x0a,
2101638Srgrimes    'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
2111638Srgrimes    'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
2121638Srgrimes    ' ', 'r', 'e', 'b', 'o', 'o', 't',
2131638Srgrimes    0x0d, 0x0a,
2141638Srgrimes    0
2151638Srgrimes};
2161638Srgrimes
2171638Srgrimesstatic volatile sig_atomic_t got_siginfo;
2181638Srgrimesstatic void infohandler(int);
2191638Srgrimes
2201638Srgrimesstatic void check_mounted(const char *, mode_t);
2211638Srgrimesstatic void getstdfmt(const char *, struct bpb *);
2221638Srgrimesstatic void getdiskinfo(int, const char *, const char *, int,
2231638Srgrimes			struct bpb *);
2241638Srgrimesstatic void print_bpb(struct bpb *);
2251638Srgrimesstatic u_int ckgeom(const char *, u_int, const char *);
2261638Srgrimesstatic u_int argtou(const char *, u_int, u_int, const char *);
2271638Srgrimesstatic off_t argtooff(const char *, const char *);
2281638Srgrimesstatic int oklabel(const char *);
2291638Srgrimesstatic void mklabel(u_int8_t *, const char *);
2301638Srgrimesstatic void setstr(u_int8_t *, const char *, size_t);
2311638Srgrimesstatic void usage(void);
2321638Srgrimes
2331638Srgrimes/*
2341638Srgrimes * Construct a FAT12, FAT16, or FAT32 file system.
2351638Srgrimes */
2361638Srgrimesint
2371638Srgrimesmain(int argc, char *argv[])
2381638Srgrimes{
2391638Srgrimes    static const char opts[] = "@:NB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:u:";
2401638Srgrimes    const char *opt_B = NULL, *opt_L = NULL, *opt_O = NULL, *opt_f = NULL;
2411638Srgrimes    u_int opt_F = 0, opt_I = 0, opt_S = 0, opt_a = 0, opt_b = 0, opt_c = 0;
2421638Srgrimes    u_int opt_e = 0, opt_h = 0, opt_i = 0, opt_k = 0, opt_m = 0, opt_n = 0;
2431638Srgrimes    u_int opt_o = 0, opt_r = 0, opt_s = 0, opt_u = 0;
2441638Srgrimes    int opt_N = 0;
2451638Srgrimes    int Iflag = 0, mflag = 0, oflag = 0;
2461638Srgrimes    char buf[MAXPATHLEN];
2471638Srgrimes    struct sigaction si_sa;
2481638Srgrimes    struct stat sb;
2491638Srgrimes    struct timeval tv;
2501638Srgrimes    struct bpb bpb;
2511638Srgrimes    struct tm *tm;
2521638Srgrimes    struct bs *bs;
2531638Srgrimes    struct bsbpb *bsbpb;
2541638Srgrimes    struct bsxbpb *bsxbpb;
2551638Srgrimes    struct bsx *bsx;
2561638Srgrimes    struct de *de;
2571638Srgrimes    u_int8_t *img;
2581638Srgrimes    const char *fname, *dtype, *bname;
2591638Srgrimes    ssize_t n;
2601638Srgrimes    time_t now;
2611638Srgrimes    u_int fat, bss, rds, cls, dir, lsn, x, x1, x2;
2621638Srgrimes    int ch, fd, fd1;
2631638Srgrimes    off_t opt_create = 0, opt_ofs = 0;
2641638Srgrimes
2651638Srgrimes    while ((ch = getopt(argc, argv, opts)) != -1)
2661638Srgrimes	switch (ch) {
2671638Srgrimes	case '@':
2681638Srgrimes	    opt_ofs = argtooff(optarg, "offset");
2691638Srgrimes	    break;
2701638Srgrimes	case 'N':
2711638Srgrimes	    opt_N = 1;
2721638Srgrimes	    break;
2731638Srgrimes	case 'B':
2741638Srgrimes	    opt_B = optarg;
2751638Srgrimes	    break;
2761638Srgrimes	case 'C':
2771638Srgrimes	    opt_create = argtooff(optarg, "create size");
2781638Srgrimes	    break;
2791638Srgrimes	case 'F':
2801638Srgrimes	    if (strcmp(optarg, "12") &&
2811638Srgrimes		strcmp(optarg, "16") &&
2821638Srgrimes		strcmp(optarg, "32"))
2831638Srgrimes		errx(1, "%s: bad FAT type", optarg);
2841638Srgrimes	    opt_F = atoi(optarg);
2851638Srgrimes	    break;
2861638Srgrimes	case 'I':
28710073Speter	    opt_I = argto4(optarg, 0, "volume ID");
2881638Srgrimes	    Iflag = 1;
2891638Srgrimes	    break;
2901638Srgrimes	case 'L':
2911638Srgrimes	    if (!oklabel(optarg))
2921638Srgrimes		errx(1, "%s: bad volume label", optarg);
2931638Srgrimes	    opt_L = optarg;
2941638Srgrimes	    break;
2951638Srgrimes	case 'O':
2961638Srgrimes	    if (strlen(optarg) > 8)
2971638Srgrimes		errx(1, "%s: bad OEM string", optarg);
2981638Srgrimes	    opt_O = optarg;
2991638Srgrimes	    break;
3001638Srgrimes	case 'S':
3011638Srgrimes	    opt_S = argto2(optarg, 1, "bytes/sector");
3021638Srgrimes	    break;
3031638Srgrimes	case 'a':
3041638Srgrimes	    opt_a = argto4(optarg, 1, "sectors/FAT");
3051638Srgrimes	    break;
3061638Srgrimes	case 'b':
3071638Srgrimes	    opt_b = argtox(optarg, 1, "block size");
3081638Srgrimes	    opt_c = 0;
3091638Srgrimes	    break;
3101638Srgrimes	case 'c':
3111638Srgrimes	    opt_c = argto1(optarg, 1, "sectors/cluster");
3121638Srgrimes	    opt_b = 0;
3131638Srgrimes	    break;
3141638Srgrimes	case 'e':
3151638Srgrimes	    opt_e = argto2(optarg, 1, "directory entries");
3161638Srgrimes	    break;
3171638Srgrimes	case 'f':
3181638Srgrimes	    opt_f = optarg;
3191638Srgrimes	    break;
3201638Srgrimes	case 'h':
3211638Srgrimes	    opt_h = argto2(optarg, 1, "drive heads");
3221638Srgrimes	    break;
3231638Srgrimes	case 'i':
3241638Srgrimes	    opt_i = argto2(optarg, 1, "info sector");
3251638Srgrimes	    break;
3261638Srgrimes	case 'k':
3271638Srgrimes	    opt_k = argto2(optarg, 1, "backup sector");
3281638Srgrimes	    break;
3291638Srgrimes	case 'm':
3301638Srgrimes	    opt_m = argto1(optarg, 0, "media descriptor");
3311638Srgrimes	    mflag = 1;
3321638Srgrimes	    break;
3331638Srgrimes	case 'n':
3341638Srgrimes	    opt_n = argto1(optarg, 1, "number of FATs");
3351638Srgrimes	    break;
3361638Srgrimes	case 'o':
3371638Srgrimes	    opt_o = argto4(optarg, 0, "hidden sectors");
3381638Srgrimes	    oflag = 1;
3391638Srgrimes	    break;
3401638Srgrimes	case 'r':
3411638Srgrimes	    opt_r = argto2(optarg, 1, "reserved sectors");
3421638Srgrimes	    break;
3431638Srgrimes	case 's':
3441638Srgrimes	    opt_s = argto4(optarg, 1, "file system size");
3451638Srgrimes	    break;
3461638Srgrimes	case 'u':
3471638Srgrimes	    opt_u = argto2(optarg, 1, "sectors/track");
3481638Srgrimes	    break;
3491638Srgrimes	default:
3501638Srgrimes	    usage();
3511638Srgrimes	}
3521638Srgrimes    argc -= optind;
3531638Srgrimes    argv += optind;
3541638Srgrimes    if (argc < 1 || argc > 2)
3551638Srgrimes	usage();
3561638Srgrimes    fname = *argv++;
3571638Srgrimes    if (!opt_create && !strchr(fname, '/')) {
3581638Srgrimes	snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
3591638Srgrimes	if (!(fname = strdup(buf)))
3601638Srgrimes	    err(1, NULL);
3611638Srgrimes    }
3621638Srgrimes    dtype = *argv;
3631638Srgrimes    if (opt_create) {
3641638Srgrimes	if (opt_N)
3651638Srgrimes	    errx(1, "create (-C) is incompatible with -N");
3661638Srgrimes	fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
3671638Srgrimes	if (fd == -1)
3681638Srgrimes	    errx(1, "failed to create %s", fname);
3691638Srgrimes	if (ftruncate(fd, opt_create))
3701638Srgrimes	    errx(1, "failed to initialize %jd bytes", (intmax_t)opt_create);
3711638Srgrimes    } else if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1)
3721638Srgrimes	err(1, "%s", fname);
3731638Srgrimes    if (fstat(fd, &sb))
3741638Srgrimes	err(1, "%s", fname);
3751638Srgrimes    if (opt_create) {
3761638Srgrimes	if (!S_ISREG(sb.st_mode))
3771638Srgrimes	    warnx("warning, %s is not a regular file", fname);
3781638Srgrimes    } else {
3791638Srgrimes	if (!S_ISCHR(sb.st_mode))
3801638Srgrimes	    warnx("warning, %s is not a character device", fname);
3811638Srgrimes    }
3821638Srgrimes    if (!opt_N)
3831638Srgrimes	check_mounted(fname, sb.st_mode);
3841638Srgrimes    if (opt_ofs && opt_ofs != lseek(fd, opt_ofs, SEEK_SET))
3851638Srgrimes	errx(1, "cannot seek to %jd", (intmax_t)opt_ofs);
3861638Srgrimes    memset(&bpb, 0, sizeof(bpb));
3871638Srgrimes    if (opt_f) {
3881638Srgrimes	getstdfmt(opt_f, &bpb);
3891638Srgrimes	bpb.bpbHugeSectors = bpb.bpbSectors;
3901638Srgrimes	bpb.bpbSectors = 0;
3911638Srgrimes	bpb.bpbBigFATsecs = bpb.bpbFATsecs;
3921638Srgrimes	bpb.bpbFATsecs = 0;
3931638Srgrimes    }
3941638Srgrimes    if (opt_h)
3951638Srgrimes	bpb.bpbHeads = opt_h;
3961638Srgrimes    if (opt_u)
3971638Srgrimes	bpb.bpbSecPerTrack = opt_u;
3981638Srgrimes    if (opt_S)
3991638Srgrimes	bpb.bpbBytesPerSec = opt_S;
4001638Srgrimes    if (opt_s)
4011638Srgrimes	bpb.bpbHugeSectors = opt_s;
4021638Srgrimes    if (oflag)
4031638Srgrimes	bpb.bpbHiddenSecs = opt_o;
4041638Srgrimes    if (!(opt_f || (opt_h && opt_u && opt_S && opt_s && oflag))) {
4051638Srgrimes	off_t delta;
4061638Srgrimes	getdiskinfo(fd, fname, dtype, oflag, &bpb);
4071638Srgrimes	bpb.bpbHugeSectors -= (opt_ofs / bpb.bpbBytesPerSec);
4081638Srgrimes	delta = bpb.bpbHugeSectors % bpb.bpbSecPerTrack;
4091638Srgrimes	if (delta != 0) {
4101638Srgrimes	    warnx("trim %d sectors to adjust to a multiple of %d",
4111638Srgrimes		(int)delta, bpb.bpbSecPerTrack);
4121638Srgrimes	    bpb.bpbHugeSectors -= delta;
4131638Srgrimes	}
4141638Srgrimes	if (bpb.bpbSecPerClust == 0) {	/* set defaults */
4151638Srgrimes	    if (bpb.bpbHugeSectors <= 6000)	/* about 3MB -> 512 bytes */
4161638Srgrimes		bpb.bpbSecPerClust = 1;
4171638Srgrimes	    else if (bpb.bpbHugeSectors <= (1<<17)) /* 64M -> 4k */
4181638Srgrimes		bpb.bpbSecPerClust = 8;
4191638Srgrimes	    else if (bpb.bpbHugeSectors <= (1<<19)) /* 256M -> 8k */
4201638Srgrimes		bpb.bpbSecPerClust = 16;
4211638Srgrimes	    else if (bpb.bpbHugeSectors <= (1<<21)) /* 1G -> 16k */
4221638Srgrimes		bpb.bpbSecPerClust = 32;
4231638Srgrimes	    else
4241638Srgrimes		bpb.bpbSecPerClust = 64;		/* otherwise 32k */
4251638Srgrimes	}
4261638Srgrimes    }
4271638Srgrimes    if (!powerof2(bpb.bpbBytesPerSec))
4281638Srgrimes	errx(1, "bytes/sector (%u) is not a power of 2", bpb.bpbBytesPerSec);
4291638Srgrimes    if (bpb.bpbBytesPerSec < MINBPS)
4301638Srgrimes	errx(1, "bytes/sector (%u) is too small; minimum is %u",
4311638Srgrimes	     bpb.bpbBytesPerSec, MINBPS);
4321638Srgrimes    if (!(fat = opt_F)) {
4331638Srgrimes	if (opt_f)
4341638Srgrimes	    fat = 12;
4351638Srgrimes	else if (!opt_e && (opt_i || opt_k))
4361638Srgrimes	    fat = 32;
4371638Srgrimes    }
4381638Srgrimes    if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k)))
4391638Srgrimes	errx(1, "-%c is not a legal FAT%s option",
4401638Srgrimes	     fat == 32 ? 'e' : opt_i ? 'i' : 'k',
4411638Srgrimes	     fat == 32 ? "32" : "12/16");
4421638Srgrimes    if (opt_f && fat == 32)
4431638Srgrimes	bpb.bpbRootDirEnts = 0;
4441638Srgrimes    if (opt_b) {
4451638Srgrimes	if (!powerof2(opt_b))
4461638Srgrimes	    errx(1, "block size (%u) is not a power of 2", opt_b);
4471638Srgrimes	if (opt_b < bpb.bpbBytesPerSec)
4481638Srgrimes	    errx(1, "block size (%u) is too small; minimum is %u",
4491638Srgrimes		 opt_b, bpb.bpbBytesPerSec);
4501638Srgrimes	if (opt_b > bpb.bpbBytesPerSec * MAXSPC)
4511638Srgrimes	    errx(1, "block size (%u) is too large; maximum is %u",
4521638Srgrimes		 opt_b, bpb.bpbBytesPerSec * MAXSPC);
4531638Srgrimes	bpb.bpbSecPerClust = opt_b / bpb.bpbBytesPerSec;
4541638Srgrimes    }
4551638Srgrimes    if (opt_c) {
4561638Srgrimes	if (!powerof2(opt_c))
4571638Srgrimes	    errx(1, "sectors/cluster (%u) is not a power of 2", opt_c);
4581638Srgrimes	bpb.bpbSecPerClust = opt_c;
4591638Srgrimes    }
4601638Srgrimes    if (opt_r)
4611638Srgrimes	bpb.bpbResSectors = opt_r;
4621638Srgrimes    if (opt_n) {
4631638Srgrimes	if (opt_n > MAXNFT)
4641638Srgrimes	    errx(1, "number of FATs (%u) is too large; maximum is %u",
4651638Srgrimes		 opt_n, MAXNFT);
4661638Srgrimes	bpb.bpbFATs = opt_n;
4671638Srgrimes    }
4681638Srgrimes    if (opt_e)
4691638Srgrimes	bpb.bpbRootDirEnts = opt_e;
4701638Srgrimes    if (mflag) {
4711638Srgrimes	if (opt_m < 0xf0)
4721638Srgrimes	    errx(1, "illegal media descriptor (%#x)", opt_m);
4731638Srgrimes	bpb.bpbMedia = opt_m;
4741638Srgrimes    }
4751638Srgrimes    if (opt_a)
4761638Srgrimes	bpb.bpbBigFATsecs = opt_a;
4771638Srgrimes    if (opt_i)
4781638Srgrimes	bpb.bpbFSInfo = opt_i;
4791638Srgrimes    if (opt_k)
4801638Srgrimes	bpb.bpbBackup = opt_k;
4811638Srgrimes    bss = 1;
4821638Srgrimes    bname = NULL;
4831638Srgrimes    fd1 = -1;
4841638Srgrimes    if (opt_B) {
4851638Srgrimes	bname = opt_B;
4861638Srgrimes	if (!strchr(bname, '/')) {
4871638Srgrimes	    snprintf(buf, sizeof(buf), "/boot/%s", bname);
4881638Srgrimes	    if (!(bname = strdup(buf)))
4891638Srgrimes		err(1, NULL);
4901638Srgrimes	}
4911638Srgrimes	if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb))
4921638Srgrimes	    err(1, "%s", bname);
4931638Srgrimes	if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bpbBytesPerSec ||
49414291Sjkh	    sb.st_size < bpb.bpbBytesPerSec ||
4951638Srgrimes	    sb.st_size > bpb.bpbBytesPerSec * MAXU16)
4961638Srgrimes	    errx(1, "%s: inappropriate file type or format", bname);
4971638Srgrimes	bss = sb.st_size / bpb.bpbBytesPerSec;
4981638Srgrimes    }
4991638Srgrimes    if (!bpb.bpbFATs)
5001638Srgrimes	bpb.bpbFATs = 2;
5011638Srgrimes    if (!fat) {
5021638Srgrimes	if (bpb.bpbHugeSectors < (bpb.bpbResSectors ? bpb.bpbResSectors : bss) +
5031638Srgrimes	    howmany((RESFTE + (bpb.bpbSecPerClust ? MINCLS16 : MAXCLS12 + 1)) *
5041638Srgrimes		(bpb.bpbSecPerClust ? 16 : 12) / BPN,
5051638Srgrimes		bpb.bpbBytesPerSec * NPB) *
5061638Srgrimes	    bpb.bpbFATs +
5071638Srgrimes	    howmany(bpb.bpbRootDirEnts ? bpb.bpbRootDirEnts : DEFRDE,
5081638Srgrimes		    bpb.bpbBytesPerSec / sizeof(struct de)) +
5091638Srgrimes	    (bpb.bpbSecPerClust ? MINCLS16 : MAXCLS12 + 1) *
5101638Srgrimes	    (bpb.bpbSecPerClust ? bpb.bpbSecPerClust :
5111638Srgrimes	     howmany(DEFBLK, bpb.bpbBytesPerSec)))
5121638Srgrimes	    fat = 12;
5131638Srgrimes	else if (bpb.bpbRootDirEnts || bpb.bpbHugeSectors <
5141638Srgrimes		 (bpb.bpbResSectors ? bpb.bpbResSectors : bss) +
5151638Srgrimes		 howmany((RESFTE + MAXCLS16) * 2, bpb.bpbBytesPerSec) *
5161638Srgrimes		 bpb.bpbFATs +
5171638Srgrimes		 howmany(DEFRDE, bpb.bpbBytesPerSec / sizeof(struct de)) +
5181638Srgrimes		 (MAXCLS16 + 1) *
5191638Srgrimes		 (bpb.bpbSecPerClust ? bpb.bpbSecPerClust :
5201638Srgrimes		  howmany(8192, bpb.bpbBytesPerSec)))
5211638Srgrimes	    fat = 16;
5221638Srgrimes	else
5231638Srgrimes	    fat = 32;
5241638Srgrimes    }
5251638Srgrimes    x = bss;
5261638Srgrimes    if (fat == 32) {
5271638Srgrimes	if (!bpb.bpbFSInfo) {
5281638Srgrimes	    if (x == MAXU16 || x == bpb.bpbBackup)
5291638Srgrimes		errx(1, "no room for info sector");
5301638Srgrimes	    bpb.bpbFSInfo = x;
5311638Srgrimes	}
5321638Srgrimes	if (bpb.bpbFSInfo != MAXU16 && x <= bpb.bpbFSInfo)
5331638Srgrimes	    x = bpb.bpbFSInfo + 1;
5341638Srgrimes	if (!bpb.bpbBackup) {
5351638Srgrimes	    if (x == MAXU16)
5361638Srgrimes		errx(1, "no room for backup sector");
5371638Srgrimes	    bpb.bpbBackup = x;
5381638Srgrimes	} else if (bpb.bpbBackup != MAXU16 && bpb.bpbBackup == bpb.bpbFSInfo)
5391638Srgrimes	    errx(1, "backup sector would overwrite info sector");
5401638Srgrimes	if (bpb.bpbBackup != MAXU16 && x <= bpb.bpbBackup)
5411638Srgrimes	    x = bpb.bpbBackup + 1;
5421638Srgrimes    }
5431638Srgrimes    if (!bpb.bpbResSectors)
5441638Srgrimes	bpb.bpbResSectors = fat == 32 ?
5451638Srgrimes	    MAX(x, MAX(16384 / bpb.bpbBytesPerSec, 4)) : x;
5461638Srgrimes    else if (bpb.bpbResSectors < x)
5471638Srgrimes	errx(1, "too few reserved sectors (need %d have %d)", x,
5481638Srgrimes	     bpb.bpbResSectors);
5491638Srgrimes    if (fat != 32 && !bpb.bpbRootDirEnts)
5501638Srgrimes	bpb.bpbRootDirEnts = DEFRDE;
5511638Srgrimes    rds = howmany(bpb.bpbRootDirEnts, bpb.bpbBytesPerSec / sizeof(struct de));
5521638Srgrimes    if (!bpb.bpbSecPerClust)
5531638Srgrimes	for (bpb.bpbSecPerClust = howmany(fat == 16 ? DEFBLK16 :
5541638Srgrimes					  DEFBLK, bpb.bpbBytesPerSec);
5551638Srgrimes	     bpb.bpbSecPerClust < MAXSPC &&
5561638Srgrimes	     bpb.bpbResSectors +
5571638Srgrimes	     howmany((RESFTE + maxcls(fat)) * (fat / BPN),
5581638Srgrimes		     bpb.bpbBytesPerSec * NPB) *
5591638Srgrimes	     bpb.bpbFATs +
5601638Srgrimes	     rds +
5611638Srgrimes	     (u_int64_t) (maxcls(fat) + 1) *
5621638Srgrimes	     bpb.bpbSecPerClust <= bpb.bpbHugeSectors;
5631638Srgrimes	     bpb.bpbSecPerClust <<= 1)
5641638Srgrimes	    continue;
5651638Srgrimes    if (fat != 32 && bpb.bpbBigFATsecs > MAXU16)
5661638Srgrimes	errx(1, "too many sectors/FAT for FAT12/16");
5671638Srgrimes    x1 = bpb.bpbResSectors + rds;
5681638Srgrimes    x = bpb.bpbBigFATsecs ? bpb.bpbBigFATsecs : 1;
5691638Srgrimes    if (x1 + (u_int64_t)x * bpb.bpbFATs > bpb.bpbHugeSectors)
5701638Srgrimes	errx(1, "meta data exceeds file system size");
5711638Srgrimes    x1 += x * bpb.bpbFATs;
5721638Srgrimes    x = (u_int64_t)(bpb.bpbHugeSectors - x1) * bpb.bpbBytesPerSec * NPB /
5731638Srgrimes	(bpb.bpbSecPerClust * bpb.bpbBytesPerSec * NPB + fat /
5741638Srgrimes	 BPN * bpb.bpbFATs);
5751638Srgrimes    x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN),
5761638Srgrimes		 bpb.bpbBytesPerSec * NPB);
5771638Srgrimes    if (!bpb.bpbBigFATsecs) {
5781638Srgrimes	bpb.bpbBigFATsecs = x2;
5791638Srgrimes	x1 += (bpb.bpbBigFATsecs - 1) * bpb.bpbFATs;
5801638Srgrimes    }
5811638Srgrimes    cls = (bpb.bpbHugeSectors - x1) / bpb.bpbSecPerClust;
5821638Srgrimes    x = (u_int64_t)bpb.bpbBigFATsecs * bpb.bpbBytesPerSec * NPB / (fat / BPN) -
5831638Srgrimes	RESFTE;
5841638Srgrimes    if (cls > x)
5851638Srgrimes	cls = x;
5861638Srgrimes    if (bpb.bpbBigFATsecs < x2)
5871638Srgrimes	warnx("warning: sectors/FAT limits file system to %u clusters",
5881638Srgrimes	      cls);
5891638Srgrimes    if (cls < mincls(fat))
5901638Srgrimes	errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat,
5911638Srgrimes	    mincls(fat));
5921638Srgrimes    if (cls > maxcls(fat)) {
5931638Srgrimes	cls = maxcls(fat);
5941638Srgrimes	bpb.bpbHugeSectors = x1 + (cls + 1) * bpb.bpbSecPerClust - 1;
5951638Srgrimes	warnx("warning: FAT type limits file system to %u sectors",
5961638Srgrimes	      bpb.bpbHugeSectors);
5971638Srgrimes    }
5981638Srgrimes    printf("%s: %u sector%s in %u FAT%u cluster%s "
5991638Srgrimes	   "(%u bytes/cluster)\n", fname, cls * bpb.bpbSecPerClust,
6001638Srgrimes	   cls * bpb.bpbSecPerClust == 1 ? "" : "s", cls, fat,
6011638Srgrimes	   cls == 1 ? "" : "s", bpb.bpbBytesPerSec * bpb.bpbSecPerClust);
6021638Srgrimes    if (!bpb.bpbMedia)
6031638Srgrimes	bpb.bpbMedia = !bpb.bpbHiddenSecs ? 0xf0 : 0xf8;
6041638Srgrimes    if (fat == 32)
6051638Srgrimes	bpb.bpbRootClust = RESFTE;
6061638Srgrimes    if (bpb.bpbHiddenSecs + bpb.bpbHugeSectors <= MAXU16) {
6071800Sphk	bpb.bpbSectors = bpb.bpbHugeSectors;
6081638Srgrimes	bpb.bpbHugeSectors = 0;
6091638Srgrimes    }
6101638Srgrimes    if (fat != 32) {
6111638Srgrimes	bpb.bpbFATsecs = bpb.bpbBigFATsecs;
6121638Srgrimes	bpb.bpbBigFATsecs = 0;
6131638Srgrimes    }
6141638Srgrimes    print_bpb(&bpb);
6151638Srgrimes    if (!opt_N) {
6161638Srgrimes	gettimeofday(&tv, NULL);
6171638Srgrimes	now = tv.tv_sec;
6181638Srgrimes	tm = localtime(&now);
6191638Srgrimes	if (!(img = malloc(bpb.bpbBytesPerSec)))
6201638Srgrimes	    err(1, NULL);
6211638Srgrimes	dir = bpb.bpbResSectors + (bpb.bpbFATsecs ? bpb.bpbFATsecs :
6221638Srgrimes				   bpb.bpbBigFATsecs) * bpb.bpbFATs;
6231638Srgrimes	memset(&si_sa, 0, sizeof(si_sa));
6241638Srgrimes	si_sa.sa_handler = infohandler;
6251638Srgrimes	if (sigaction(SIGINFO, &si_sa, NULL) == -1)
6261638Srgrimes		err(1, "sigaction SIGINFO");
6271638Srgrimes	for (lsn = 0; lsn < dir + (fat == 32 ? bpb.bpbSecPerClust : rds); lsn++) {
6281638Srgrimes	    if (got_siginfo) {
6291638Srgrimes		    fprintf(stderr,"%s: writing sector %u of %u (%u%%)\n",
6301638Srgrimes			fname, lsn,
6311638Srgrimes			(dir + (fat == 32 ? bpb.bpbSecPerClust: rds)),
6321638Srgrimes			(lsn * 100) / (dir +
6331638Srgrimes			    (fat == 32 ? bpb.bpbSecPerClust: rds)));
6341638Srgrimes		    got_siginfo = 0;
6351638Srgrimes	    }
6361638Srgrimes	    x = lsn;
6371638Srgrimes	    if (opt_B &&
6381638Srgrimes		fat == 32 && bpb.bpbBackup != MAXU16 &&
6391638Srgrimes		bss <= bpb.bpbBackup && x >= bpb.bpbBackup) {
6401638Srgrimes		x -= bpb.bpbBackup;
6411638Srgrimes		if (!x && lseek(fd1, opt_ofs, SEEK_SET))
6421638Srgrimes		    err(1, "%s", bname);
6431638Srgrimes	    }
6441638Srgrimes	    if (opt_B && x < bss) {
6451638Srgrimes		if ((n = read(fd1, img, bpb.bpbBytesPerSec)) == -1)
6461638Srgrimes		    err(1, "%s", bname);
6471638Srgrimes		if ((unsigned)n != bpb.bpbBytesPerSec)
6481638Srgrimes		    errx(1, "%s: can't read sector %u", bname, x);
6491638Srgrimes	    } else
6501638Srgrimes		memset(img, 0, bpb.bpbBytesPerSec);
6511638Srgrimes	    if (!lsn ||
6521638Srgrimes		(fat == 32 && bpb.bpbBackup != MAXU16 &&
6531638Srgrimes		 lsn == bpb.bpbBackup)) {
6541638Srgrimes		x1 = sizeof(struct bs);
6551638Srgrimes		bsbpb = (struct bsbpb *)(img + x1);
6561638Srgrimes		mk2(bsbpb->bpbBytesPerSec, bpb.bpbBytesPerSec);
6571638Srgrimes		mk1(bsbpb->bpbSecPerClust, bpb.bpbSecPerClust);
6581638Srgrimes		mk2(bsbpb->bpbResSectors, bpb.bpbResSectors);
6591638Srgrimes		mk1(bsbpb->bpbFATs, bpb.bpbFATs);
6601638Srgrimes		mk2(bsbpb->bpbRootDirEnts, bpb.bpbRootDirEnts);
6611638Srgrimes		mk2(bsbpb->bpbSectors, bpb.bpbSectors);
6621638Srgrimes		mk1(bsbpb->bpbMedia, bpb.bpbMedia);
6631638Srgrimes		mk2(bsbpb->bpbFATsecs, bpb.bpbFATsecs);
6641638Srgrimes		mk2(bsbpb->bpbSecPerTrack, bpb.bpbSecPerTrack);
6651638Srgrimes		mk2(bsbpb->bpbHeads, bpb.bpbHeads);
6661638Srgrimes		mk4(bsbpb->bpbHiddenSecs, bpb.bpbHiddenSecs);
6671638Srgrimes		mk4(bsbpb->bpbHugeSectors, bpb.bpbHugeSectors);
6681638Srgrimes		x1 += sizeof(struct bsbpb);
6691638Srgrimes		if (fat == 32) {
6701638Srgrimes		    bsxbpb = (struct bsxbpb *)(img + x1);
6711638Srgrimes		    mk4(bsxbpb->bpbBigFATsecs, bpb.bpbBigFATsecs);
6721638Srgrimes		    mk2(bsxbpb->bpbExtFlags, 0);
6731638Srgrimes		    mk2(bsxbpb->bpbFSVers, 0);
6741638Srgrimes		    mk4(bsxbpb->bpbRootClust, bpb.bpbRootClust);
6751638Srgrimes		    mk2(bsxbpb->bpbFSInfo, bpb.bpbFSInfo);
6761638Srgrimes		    mk2(bsxbpb->bpbBackup, bpb.bpbBackup);
6771638Srgrimes		    x1 += sizeof(struct bsxbpb);
6781638Srgrimes		}
6791638Srgrimes		bsx = (struct bsx *)(img + x1);
6801638Srgrimes		mk1(bsx->exBootSignature, 0x29);
6811638Srgrimes		if (Iflag)
6821638Srgrimes		    x = opt_I;
6831638Srgrimes		else
6841638Srgrimes		    x = (((u_int)(1 + tm->tm_mon) << 8 |
6851638Srgrimes			  (u_int)tm->tm_mday) +
6861638Srgrimes			 ((u_int)tm->tm_sec << 8 |
6871638Srgrimes			  (u_int)(tv.tv_usec / 10))) << 16 |
6881638Srgrimes			((u_int)(1900 + tm->tm_year) +
6891638Srgrimes			 ((u_int)tm->tm_hour << 8 |
6901638Srgrimes			  (u_int)tm->tm_min));
6911638Srgrimes		mk4(bsx->exVolumeID, x);
6921638Srgrimes		mklabel(bsx->exVolumeLabel, opt_L ? opt_L : "NO NAME");
6931638Srgrimes		sprintf(buf, "FAT%u", fat);
6941638Srgrimes		setstr(bsx->exFileSysType, buf, sizeof(bsx->exFileSysType));
6951638Srgrimes		if (!opt_B) {
6961638Srgrimes		    x1 += sizeof(struct bsx);
6971638Srgrimes		    bs = (struct bs *)img;
6981638Srgrimes		    mk1(bs->bsJump[0], 0xeb);
6991638Srgrimes		    mk1(bs->bsJump[1], x1 - 2);
7001638Srgrimes		    mk1(bs->bsJump[2], 0x90);
7011638Srgrimes		    setstr(bs->bsOemName, opt_O ? opt_O : "BSD4.4  ",
7021638Srgrimes			   sizeof(bs->bsOemName));
7031638Srgrimes		    memcpy(img + x1, bootcode, sizeof(bootcode));
7041638Srgrimes		    mk2(img + MINBPS - 2, DOSMAGIC);
7051638Srgrimes		}
7061638Srgrimes	    } else if (fat == 32 && bpb.bpbFSInfo != MAXU16 &&
7071638Srgrimes		       (lsn == bpb.bpbFSInfo ||
7081638Srgrimes			(bpb.bpbBackup != MAXU16 &&
7091638Srgrimes			 lsn == bpb.bpbBackup + bpb.bpbFSInfo))) {
7101638Srgrimes		mk4(img, 0x41615252);
7111638Srgrimes		mk4(img + MINBPS - 28, 0x61417272);
7121638Srgrimes		mk4(img + MINBPS - 24, 0xffffffff);
7131638Srgrimes		mk4(img + MINBPS - 20, bpb.bpbRootClust);
7141638Srgrimes		mk2(img + MINBPS - 2, DOSMAGIC);
7151638Srgrimes	    } else if (lsn >= bpb.bpbResSectors && lsn < dir &&
7161638Srgrimes		       !((lsn - bpb.bpbResSectors) %
7171638Srgrimes			 (bpb.bpbFATsecs ? bpb.bpbFATsecs :
7181638Srgrimes			  bpb.bpbBigFATsecs))) {
7191638Srgrimes		mk1(img[0], bpb.bpbMedia);
7201638Srgrimes		for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++)
7211638Srgrimes		    mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff);
7221638Srgrimes	    } else if (lsn == dir && opt_L) {
7231638Srgrimes		de = (struct de *)img;
7241638Srgrimes		mklabel(de->deName, opt_L);
7251638Srgrimes		mk1(de->deAttributes, 050);
7261638Srgrimes		x = (u_int)tm->tm_hour << 11 |
7271638Srgrimes		    (u_int)tm->tm_min << 5 |
7281638Srgrimes		    (u_int)tm->tm_sec >> 1;
7291638Srgrimes		mk2(de->deMTime, x);
7301638Srgrimes		x = (u_int)(tm->tm_year - 80) << 9 |
7311638Srgrimes		    (u_int)(tm->tm_mon + 1) << 5 |
7321638Srgrimes		    (u_int)tm->tm_mday;
7331638Srgrimes		mk2(de->deMDate, x);
7341638Srgrimes	    }
7351638Srgrimes	    if ((n = write(fd, img, bpb.bpbBytesPerSec)) == -1)
7361638Srgrimes		err(1, "%s", fname);
7371638Srgrimes	    if ((unsigned)n != bpb.bpbBytesPerSec)
7381638Srgrimes		errx(1, "%s: can't write sector %u", fname, lsn);
7391638Srgrimes	}
7401638Srgrimes    }
7411638Srgrimes    return 0;
7421638Srgrimes}
7431638Srgrimes
7441638Srgrimes/*
7451638Srgrimes * Exit with error if file system is mounted.
7461638Srgrimes */
7471638Srgrimesstatic void
7481638Srgrimescheck_mounted(const char *fname, mode_t mode)
7491638Srgrimes{
7501638Srgrimes    struct statfs *mp;
7511638Srgrimes    const char *s1, *s2;
7521638Srgrimes    size_t len;
7531638Srgrimes    int n, r;
7541638Srgrimes
7551638Srgrimes    if (!(n = getmntinfo(&mp, MNT_NOWAIT)))
7561638Srgrimes	err(1, "getmntinfo");
7571638Srgrimes    len = strlen(_PATH_DEV);
7581638Srgrimes    s1 = fname;
7591638Srgrimes    if (!strncmp(s1, _PATH_DEV, len))
7601638Srgrimes	s1 += len;
7611638Srgrimes    r = S_ISCHR(mode) && s1 != fname && *s1 == 'r';
7621638Srgrimes    for (; n--; mp++) {
7631638Srgrimes	s2 = mp->f_mntfromname;
7641638Srgrimes	if (!strncmp(s2, _PATH_DEV, len))
7651638Srgrimes	    s2 += len;
7661638Srgrimes	if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) ||
7671638Srgrimes	    !strcmp(s1, s2))
7681638Srgrimes	    errx(1, "%s is mounted on %s", fname, mp->f_mntonname);
7691638Srgrimes    }
7701638Srgrimes}
7711638Srgrimes
7721638Srgrimes/*
7731638Srgrimes * Get a standard format.
7741638Srgrimes */
7751638Srgrimesstatic void
7761638Srgrimesgetstdfmt(const char *fmt, struct bpb *bpb)
7771638Srgrimes{
7781638Srgrimes    u_int x, i;
7791638Srgrimes
7801638Srgrimes    x = sizeof(stdfmt) / sizeof(stdfmt[0]);
7811638Srgrimes    for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++);
7821638Srgrimes    if (i == x)
7831638Srgrimes	errx(1, "%s: unknown standard format", fmt);
7841638Srgrimes    *bpb = stdfmt[i].bpb;
7851638Srgrimes}
7861638Srgrimes
7871638Srgrimes/*
7881638Srgrimes * Get disk slice, partition, and geometry information.
7891638Srgrimes */
7901638Srgrimesstatic void
7911638Srgrimesgetdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag,
7921638Srgrimes	    struct bpb *bpb)
7931638Srgrimes{
7941638Srgrimes    struct disklabel *lp, dlp;
7951638Srgrimes    struct fd_type type;
7961638Srgrimes    off_t ms, hs = 0;
7971638Srgrimes
7981638Srgrimes    lp = NULL;
7991638Srgrimes
8001638Srgrimes    /* If the user specified a disk type, try to use that */
8011638Srgrimes    if (dtype != NULL) {
8021638Srgrimes	lp = getdiskbyname(dtype);
8031638Srgrimes    }
8041638Srgrimes
8051638Srgrimes    /* Maybe it's a floppy drive */
80610070Sjoerg    if (lp == NULL) {
8071638Srgrimes	if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) {
8081638Srgrimes	    struct stat st;
8091638Srgrimes
8101638Srgrimes	    if (fstat(fd, &st))
8111638Srgrimes		err(1, "cannot get disk size");
8121638Srgrimes	    /* create a fake geometry for a file image */
8131638Srgrimes	    ms = st.st_size;
8141638Srgrimes	    dlp.d_secsize = 512;
8151638Srgrimes	    dlp.d_nsectors = 63;
8161638Srgrimes	    dlp.d_ntracks = 255;
8171638Srgrimes	    dlp.d_secperunit = ms / dlp.d_secsize;
8181638Srgrimes	    lp = &dlp;
8191638Srgrimes	} else if (ioctl(fd, FD_GTYPE, &type) != -1) {
8201638Srgrimes	    dlp.d_secsize = 128 << type.secsize;
8211638Srgrimes	    dlp.d_nsectors = type.sectrac;
8221638Srgrimes	    dlp.d_ntracks = type.heads;
8231638Srgrimes	    dlp.d_secperunit = ms / dlp.d_secsize;
8241638Srgrimes	    lp = &dlp;
8251638Srgrimes	}
8261638Srgrimes    }
8271638Srgrimes
8281638Srgrimes    /* Maybe it's a fixed drive */
8291638Srgrimes    if (lp == NULL) {
8301638Srgrimes	if (bpb->bpbBytesPerSec)
8311638Srgrimes	    dlp.d_secsize = bpb->bpbBytesPerSec;
8321638Srgrimes	if (ioctl(fd, DIOCGDINFO, &dlp) == -1) {
8331638Srgrimes	    if (bpb->bpbBytesPerSec == 0 && ioctl(fd, DIOCGSECTORSIZE,
8341638Srgrimes						  &dlp.d_secsize) == -1)
8351638Srgrimes		err(1, "cannot get sector size");
8361638Srgrimes
8371638Srgrimes	    dlp.d_secperunit = ms / dlp.d_secsize;
8381638Srgrimes
8391638Srgrimes	    if (bpb->bpbSecPerTrack == 0 && ioctl(fd, DIOCGFWSECTORS,
8401638Srgrimes						  &dlp.d_nsectors) == -1) {
8411638Srgrimes		warn("cannot get number of sectors per track");
8421638Srgrimes		dlp.d_nsectors = 63;
8431638Srgrimes	    }
8441638Srgrimes	    if (bpb->bpbHeads == 0 &&
8451638Srgrimes	        ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) {
8461638Srgrimes		warn("cannot get number of heads");
8471638Srgrimes		if (dlp.d_secperunit <= 63*1*1024)
8481638Srgrimes		    dlp.d_ntracks = 1;
8491638Srgrimes		else if (dlp.d_secperunit <= 63*16*1024)
8501638Srgrimes		    dlp.d_ntracks = 16;
8511638Srgrimes		else
8521638Srgrimes		    dlp.d_ntracks = 255;
8531638Srgrimes	    }
8541638Srgrimes	}
8551638Srgrimes
8561638Srgrimes	hs = (ms / dlp.d_secsize) - dlp.d_secperunit;
8571638Srgrimes	lp = &dlp;
8581638Srgrimes    }
8591638Srgrimes
8601638Srgrimes    if (bpb->bpbBytesPerSec == 0)
8611638Srgrimes	bpb->bpbBytesPerSec = ckgeom(fname, lp->d_secsize, "bytes/sector");
8621638Srgrimes    if (bpb->bpbSecPerTrack == 0)
8631638Srgrimes	bpb->bpbSecPerTrack = ckgeom(fname, lp->d_nsectors, "sectors/track");
8641638Srgrimes    if (bpb->bpbHeads == 0)
8651638Srgrimes	bpb->bpbHeads = ckgeom(fname, lp->d_ntracks, "drive heads");
8661638Srgrimes    if (bpb->bpbHugeSectors == 0)
8671638Srgrimes	bpb->bpbHugeSectors = lp->d_secperunit;
8681638Srgrimes    if (bpb->bpbHiddenSecs == 0)
8691638Srgrimes	bpb->bpbHiddenSecs = hs;
8701638Srgrimes}
8711638Srgrimes
8721638Srgrimes/*
8731638Srgrimes * Print out BPB values.
8741638Srgrimes */
8751638Srgrimesstatic void
8761638Srgrimesprint_bpb(struct bpb *bpb)
8771638Srgrimes{
8781638Srgrimes    printf("BytesPerSec=%u SecPerClust=%u ResSectors=%u FATs=%u",
8791638Srgrimes	   bpb->bpbBytesPerSec, bpb->bpbSecPerClust, bpb->bpbResSectors,
8801638Srgrimes	   bpb->bpbFATs);
8811638Srgrimes    if (bpb->bpbRootDirEnts)
8821638Srgrimes	printf(" RootDirEnts=%u", bpb->bpbRootDirEnts);
8831638Srgrimes    if (bpb->bpbSectors)
8841638Srgrimes	printf(" Sectors=%u", bpb->bpbSectors);
8851638Srgrimes    printf(" Media=%#x", bpb->bpbMedia);
8861638Srgrimes    if (bpb->bpbFATsecs)
8871638Srgrimes	printf(" FATsecs=%u", bpb->bpbFATsecs);
8881638Srgrimes    printf(" SecPerTrack=%u Heads=%u HiddenSecs=%u", bpb->bpbSecPerTrack,
8891638Srgrimes	   bpb->bpbHeads, bpb->bpbHiddenSecs);
8901638Srgrimes    if (bpb->bpbHugeSectors)
8911638Srgrimes	printf(" HugeSectors=%u", bpb->bpbHugeSectors);
8921638Srgrimes    if (!bpb->bpbFATsecs) {
8931638Srgrimes	printf(" FATsecs=%u RootCluster=%u", bpb->bpbBigFATsecs,
8941638Srgrimes	       bpb->bpbRootClust);
8951638Srgrimes	printf(" FSInfo=");
8961638Srgrimes	printf(bpb->bpbFSInfo == MAXU16 ? "%#x" : "%u", bpb->bpbFSInfo);
8971638Srgrimes	printf(" Backup=");
8981638Srgrimes	printf(bpb->bpbBackup == MAXU16 ? "%#x" : "%u", bpb->bpbBackup);
8991638Srgrimes    }
9001638Srgrimes    printf("\n");
9011638Srgrimes}
9021638Srgrimes
9031638Srgrimes/*
9041638Srgrimes * Check a disk geometry value.
9051638Srgrimes */
9061638Srgrimesstatic u_int
9071638Srgrimesckgeom(const char *fname, u_int val, const char *msg)
9081638Srgrimes{
9091638Srgrimes    if (!val)
9101638Srgrimes	errx(1, "%s: no default %s", fname, msg);
9111638Srgrimes    if (val > MAXU16)
9121638Srgrimes	errx(1, "%s: illegal %s %d", fname, msg, val);
9131638Srgrimes    return val;
9141638Srgrimes}
9151638Srgrimes
9161638Srgrimes/*
9171638Srgrimes * Convert and check a numeric option argument.
9181638Srgrimes */
9191638Srgrimesstatic u_int
9201638Srgrimesargtou(const char *arg, u_int lo, u_int hi, const char *msg)
9211638Srgrimes{
9221638Srgrimes    char *s;
9231638Srgrimes    u_long x;
9241638Srgrimes
9251638Srgrimes    errno = 0;
9261638Srgrimes    x = strtoul(arg, &s, 0);
9271638Srgrimes    if (errno || !*arg || *s || x < lo || x > hi)
9281638Srgrimes	errx(1, "%s: bad %s", arg, msg);
9291638Srgrimes    return x;
9301638Srgrimes}
9311638Srgrimes
9321638Srgrimes/*
9331638Srgrimes * Same for off_t, with optional skmgpP suffix
9341638Srgrimes */
9351638Srgrimesstatic off_t
9361638Srgrimesargtooff(const char *arg, const char *msg)
9371638Srgrimes{
9381638Srgrimes    char *s;
9391638Srgrimes    off_t x;
9401638Srgrimes
9411638Srgrimes    errno = 0;
9421638Srgrimes    x = strtoll(arg, &s, 0);
9431638Srgrimes    /* allow at most one extra char */
9441638Srgrimes    if (errno || x < 0 || (s[0] && s[1]) )
9451638Srgrimes	errx(1, "%s: bad %s", arg, msg);
9461638Srgrimes    if (*s) {	/* the extra char is the multiplier */
9471638Srgrimes	switch (*s) {
9481638Srgrimes	default:
9491638Srgrimes	    errx(1, "%s: bad %s", arg, msg);
9501638Srgrimes	    /* notreached */
9511638Srgrimes
9521638Srgrimes	case 's':		/* sector */
9531638Srgrimes	case 'S':
9541638Srgrimes	    x <<= 9;		/* times 512 */
9551638Srgrimes	    break;
9561638Srgrimes
9571638Srgrimes	case 'k':		/* kilobyte */
9581638Srgrimes	case 'K':
9591638Srgrimes	    x <<= 10;		/* times 1024 */
9601638Srgrimes	    break;
9611638Srgrimes
9621638Srgrimes	case 'm':		/* megabyte */
9631638Srgrimes	case 'M':
9641638Srgrimes	    x <<= 20;		/* times 1024*1024 */
9651638Srgrimes	    break;
9661638Srgrimes
9671638Srgrimes	case 'g':		/* gigabyte */
9681638Srgrimes	case 'G':
9691638Srgrimes	    x <<= 30;		/* times 1024*1024*1024 */
9701638Srgrimes	    break;
9711638Srgrimes
9721638Srgrimes	case 'p':		/* partition start */
9731638Srgrimes	case 'P':
9741638Srgrimes	case 'l':		/* partition length */
9751638Srgrimes	case 'L':
9761638Srgrimes	    errx(1, "%s: not supported yet %s", arg, msg);
9771638Srgrimes	    /* notreached */
9781638Srgrimes	}
9791638Srgrimes    }
9801638Srgrimes    return x;
9811638Srgrimes}
9821638Srgrimes
9831638Srgrimes/*
9841638Srgrimes * Check a volume label.
9851638Srgrimes */
9861638Srgrimesstatic int
9871638Srgrimesoklabel(const char *src)
9881638Srgrimes{
9891638Srgrimes    int c, i;
9901638Srgrimes
9911638Srgrimes    for (i = 0; i <= 11; i++) {
9921638Srgrimes	c = (u_char)*src++;
9931638Srgrimes	if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
9941638Srgrimes	    break;
9951638Srgrimes    }
9961638Srgrimes    return i && !c;
9971638Srgrimes}
9981638Srgrimes
9991638Srgrimes/*
10001638Srgrimes * Make a volume label.
10011638Srgrimes */
10021638Srgrimesstatic void
10031638Srgrimesmklabel(u_int8_t *dest, const char *src)
10041638Srgrimes{
10051638Srgrimes    int c, i;
10061638Srgrimes
10071638Srgrimes    for (i = 0; i < 11; i++) {
10081638Srgrimes	c = *src ? toupper(*src++) : ' ';
10091638Srgrimes	*dest++ = !i && c == '\xe5' ? 5 : c;
10101638Srgrimes    }
10111638Srgrimes}
10121638Srgrimes
10131638Srgrimes/*
10141638Srgrimes * Copy string, padding with spaces.
10151638Srgrimes */
10161638Srgrimesstatic void
10171638Srgrimessetstr(u_int8_t *dest, const char *src, size_t len)
10181638Srgrimes{
10191638Srgrimes    while (len--)
10201638Srgrimes	*dest++ = *src ? *src++ : ' ';
10211638Srgrimes}
10221638Srgrimes
10231638Srgrimes/*
10241638Srgrimes * Print usage message.
10251638Srgrimes */
10261638Srgrimesstatic void
10271638Srgrimesusage(void)
10281638Srgrimes{
10291638Srgrimes	fprintf(stderr,
10301638Srgrimes	    "usage: newfs_msdos [ -options ] special [disktype]\n"
10311638Srgrimes	    "where the options are:\n"
10321638Srgrimes	    "\t-@ create file system at specified offset\n"
10331638Srgrimes	    "\t-B get bootstrap from file\n"
10341638Srgrimes	    "\t-C create image file with specified size\n"
10351638Srgrimes	    "\t-F FAT type (12, 16, or 32)\n"
10361638Srgrimes	    "\t-I volume ID\n"
10371638Srgrimes	    "\t-L volume label\n"
10381638Srgrimes	    "\t-N don't create file system: just print out parameters\n"
10391638Srgrimes	    "\t-O OEM string\n"
10401638Srgrimes	    "\t-S bytes/sector\n"
10411638Srgrimes	    "\t-a sectors/FAT\n"
10421638Srgrimes	    "\t-b block size\n"
10431638Srgrimes	    "\t-c sectors/cluster\n"
10441638Srgrimes	    "\t-e root directory entries\n"
10451638Srgrimes	    "\t-f standard format\n"
10461638Srgrimes	    "\t-h drive heads\n"
10471638Srgrimes	    "\t-i file system info sector\n"
10481638Srgrimes	    "\t-k backup boot sector\n"
10491638Srgrimes	    "\t-m media descriptor\n"
10501638Srgrimes	    "\t-n number of FATs\n"
10511638Srgrimes	    "\t-o hidden sectors\n"
10521638Srgrimes	    "\t-r reserved sectors\n"
10531638Srgrimes	    "\t-s file system size (sectors)\n"
10541638Srgrimes	    "\t-u sectors/track\n");
10551638Srgrimes	exit(1);
10561638Srgrimes}
10571638Srgrimes
10581638Srgrimesstatic void
10591638Srgrimesinfohandler(int sig __unused)
10601638Srgrimes{
10611638Srgrimes
10621638Srgrimes	got_siginfo = 1;
10631638Srgrimes}
10641638Srgrimes