newfs_msdos.c revision 229550
1167974Sdelphij/*
278556Sobrien * Copyright (c) 1998 Robert Nordier
378556Sobrien * All rights reserved.
478556Sobrien *
578556Sobrien * Redistribution and use in source and binary forms, with or without
678556Sobrien * modification, are permitted provided that the following conditions
778556Sobrien * are met:
878556Sobrien * 1. Redistributions of source code must retain the above copyright
978556Sobrien *    notice, this list of conditions and the following disclaimer.
1078556Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1178556Sobrien *    notice, this list of conditions and the following disclaimer in
1278556Sobrien *    the documentation and/or other materials provided with the
1378556Sobrien *    distribution.
1478556Sobrien *
1578556Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
1678556Sobrien * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1778556Sobrien * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1878556Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
1978556Sobrien * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2078556Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
2178556Sobrien * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2278556Sobrien * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
2378556Sobrien * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
2478556Sobrien * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
2578556Sobrien * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2678556Sobrien */
2778556Sobrien
2878556Sobrien#ifndef lint
2978556Sobrienstatic const char rcsid[] =
3078556Sobrien  "$FreeBSD: stable/9/sbin/newfs_msdos/newfs_msdos.c 229550 2012-01-05 01:40:42Z pfg $";
3178556Sobrien#endif /* not lint */
3278556Sobrien
3378556Sobrien#include <sys/param.h>
3478556Sobrien#include <sys/fdcio.h>
3578556Sobrien#include <sys/disk.h>
3678556Sobrien#include <sys/disklabel.h>
3778556Sobrien#include <sys/mount.h>
3878556Sobrien#include <sys/stat.h>
3978556Sobrien#include <sys/time.h>
4078556Sobrien
4178556Sobrien#include <ctype.h>
4278556Sobrien#include <err.h>
4378556Sobrien#include <errno.h>
4478556Sobrien#include <fcntl.h>
4578556Sobrien#include <inttypes.h>
4678556Sobrien#include <paths.h>
4778556Sobrien#include <signal.h>
4878556Sobrien#include <stdio.h>
4978556Sobrien#include <stdlib.h>
5078556Sobrien#include <string.h>
5178556Sobrien#include <time.h>
5278556Sobrien#include <unistd.h>
5378556Sobrien
5478556Sobrien#define MAXU16	  0xffff	/* maximum unsigned 16-bit quantity */
5578556Sobrien#define BPN	  4		/* bits per nibble */
5678556Sobrien#define NPB	  2		/* nibbles per byte */
5778556Sobrien
5878556Sobrien#define DOSMAGIC  0xaa55	/* DOS magic number */
5978556Sobrien#define MINBPS	  512		/* minimum bytes per sector */
6078556Sobrien#define MAXSPC	  128		/* maximum sectors per cluster */
6178556Sobrien#define MAXNFT	  16		/* maximum number of FATs */
6278556Sobrien#define DEFBLK	  4096		/* default block size */
6378556Sobrien#define DEFBLK16  2048		/* default block size FAT16 */
6478556Sobrien#define DEFRDE	  512		/* default root directory entries */
6578556Sobrien#define RESFTE	  2		/* reserved FAT entries */
6678556Sobrien#define MINCLS12  1U		/* minimum FAT12 clusters */
6778556Sobrien#define MINCLS16  0x1000U	/* minimum FAT16 clusters */
6878556Sobrien#define MINCLS32  2U		/* minimum FAT32 clusters */
6978556Sobrien#define MAXCLS12  0xfedU	/* maximum FAT12 clusters */
7078556Sobrien#define MAXCLS16  0xfff5U	/* maximum FAT16 clusters */
7178556Sobrien#define MAXCLS32  0xffffff5U	/* maximum FAT32 clusters */
7278556Sobrien
7378556Sobrien#define mincls(fat)  ((fat) == 12 ? MINCLS12 :	\
7478556Sobrien		      (fat) == 16 ? MINCLS16 :	\
7578556Sobrien				    MINCLS32)
7678556Sobrien
7778556Sobrien#define maxcls(fat)  ((fat) == 12 ? MAXCLS12 :	\
7878556Sobrien		      (fat) == 16 ? MAXCLS16 :	\
7978556Sobrien				    MAXCLS32)
8078556Sobrien
8178556Sobrien#define mk1(p, x)				\
8278556Sobrien    (p) = (u_int8_t)(x)
8378556Sobrien
8478556Sobrien#define mk2(p, x)				\
8578556Sobrien    (p)[0] = (u_int8_t)(x),			\
8678556Sobrien    (p)[1] = (u_int8_t)((x) >> 010)
8778556Sobrien
8878556Sobrien#define mk4(p, x)				\
8978556Sobrien    (p)[0] = (u_int8_t)(x),			\
9078556Sobrien    (p)[1] = (u_int8_t)((x) >> 010),		\
9178556Sobrien    (p)[2] = (u_int8_t)((x) >> 020),		\
9278556Sobrien    (p)[3] = (u_int8_t)((x) >> 030)
9378556Sobrien
9478556Sobrien#define argto1(arg, lo, msg)  argtou(arg, lo, 0xff, msg)
9578556Sobrien#define argto2(arg, lo, msg)  argtou(arg, lo, 0xffff, msg)
9678556Sobrien#define argto4(arg, lo, msg)  argtou(arg, lo, 0xffffffff, msg)
9778556Sobrien#define argtox(arg, lo, msg)  argtou(arg, lo, UINT_MAX, msg)
9878556Sobrien
9978556Sobrienstruct bs {
10078556Sobrien    u_int8_t bsJump[3];			/* bootstrap entry point */
10178556Sobrien    u_int8_t bsOemName[8];		/* OEM name and version */
10278556Sobrien};
10378556Sobrien
10478556Sobrienstruct bsbpb {
10578556Sobrien    u_int8_t bpbBytesPerSec[2];		/* bytes per sector */
10678556Sobrien    u_int8_t bpbSecPerClust;		/* sectors per cluster */
10778556Sobrien    u_int8_t bpbResSectors[2];		/* reserved sectors */
10878556Sobrien    u_int8_t bpbFATs;			/* number of FATs */
10978556Sobrien    u_int8_t bpbRootDirEnts[2];		/* root directory entries */
11078556Sobrien    u_int8_t bpbSectors[2];		/* total sectors */
11178556Sobrien    u_int8_t bpbMedia;			/* media descriptor */
11278556Sobrien    u_int8_t bpbFATsecs[2];		/* sectors per FAT */
11378556Sobrien    u_int8_t bpbSecPerTrack[2];		/* sectors per track */
11478556Sobrien    u_int8_t bpbHeads[2];		/* drive heads */
11578556Sobrien    u_int8_t bpbHiddenSecs[4];		/* hidden sectors */
11678556Sobrien    u_int8_t bpbHugeSectors[4];		/* big total sectors */
11778556Sobrien};
11878556Sobrien
11978556Sobrienstruct bsxbpb {
12078556Sobrien    u_int8_t bpbBigFATsecs[4];		/* big sectors per FAT */
12178556Sobrien    u_int8_t bpbExtFlags[2];		/* FAT control flags */
12278556Sobrien    u_int8_t bpbFSVers[2];		/* file system version */
12378556Sobrien    u_int8_t bpbRootClust[4];		/* root directory start cluster */
12478556Sobrien    u_int8_t bpbFSInfo[2];		/* file system info sector */
12578556Sobrien    u_int8_t bpbBackup[2];		/* backup boot sector */
12678556Sobrien    u_int8_t bpbReserved[12];		/* reserved */
12778556Sobrien};
12878556Sobrien
12978556Sobrienstruct bsx {
13078556Sobrien    u_int8_t exDriveNumber;		/* drive number */
13178556Sobrien    u_int8_t exReserved1;		/* reserved */
13278556Sobrien    u_int8_t exBootSignature;		/* extended boot signature */
13378556Sobrien    u_int8_t exVolumeID[4];		/* volume ID number */
13478556Sobrien    u_int8_t exVolumeLabel[11]; 	/* volume label */
13578556Sobrien    u_int8_t exFileSysType[8];		/* file system type */
13678556Sobrien};
13778556Sobrien
13878556Sobrienstruct de {
13978556Sobrien    u_int8_t deName[11];		/* name and extension */
14078556Sobrien    u_int8_t deAttributes;		/* attributes */
14178556Sobrien    u_int8_t rsvd[10];			/* reserved */
14278556Sobrien    u_int8_t deMTime[2];		/* creation time */
14378556Sobrien    u_int8_t deMDate[2];		/* creation date */
14478556Sobrien    u_int8_t deStartCluster[2];		/* starting cluster */
14578556Sobrien    u_int8_t deFileSize[4];		/* size */
14678556Sobrien};
14778556Sobrien
14878556Sobrienstruct bpb {
14978556Sobrien    u_int bpbBytesPerSec;		/* bytes per sector */
15078556Sobrien    u_int bpbSecPerClust;		/* sectors per cluster */
15178556Sobrien    u_int bpbResSectors;		/* reserved sectors */
15278556Sobrien    u_int bpbFATs;			/* number of FATs */
15378556Sobrien    u_int bpbRootDirEnts;		/* root directory entries */
15478556Sobrien    u_int bpbSectors;			/* total sectors */
15578556Sobrien    u_int bpbMedia;			/* media descriptor */
15678556Sobrien    u_int bpbFATsecs;			/* sectors per FAT */
15778556Sobrien    u_int bpbSecPerTrack;		/* sectors per track */
15878556Sobrien    u_int bpbHeads;			/* drive heads */
15978556Sobrien    u_int bpbHiddenSecs;		/* hidden sectors */
16078556Sobrien    u_int bpbHugeSectors; 		/* big total sectors */
16178556Sobrien    u_int bpbBigFATsecs; 		/* big sectors per FAT */
16278556Sobrien    u_int bpbRootClust; 		/* root directory start cluster */
16378556Sobrien    u_int bpbFSInfo; 			/* file system info sector */
16478556Sobrien    u_int bpbBackup; 			/* backup boot sector */
16578556Sobrien};
16678556Sobrien
16778556Sobrien#define BPBGAP 0, 0, 0, 0, 0, 0
16878556Sobrien
16978556Sobrienstatic struct {
17078556Sobrien    const char *name;
17178556Sobrien    struct bpb bpb;
17278556Sobrien} const stdfmt[] = {
17378556Sobrien    {"160",  {512, 1, 1, 2,  64,  320, 0xfe, 1,  8, 1, BPBGAP}},
17478556Sobrien    {"180",  {512, 1, 1, 2,  64,  360, 0xfc, 2,  9, 1, BPBGAP}},
17578556Sobrien    {"320",  {512, 2, 1, 2, 112,  640, 0xff, 1,  8, 2, BPBGAP}},
17678556Sobrien    {"360",  {512, 2, 1, 2, 112,  720, 0xfd, 2,  9, 2, BPBGAP}},
17778556Sobrien    {"640",  {512, 2, 1, 2, 112, 1280, 0xfb, 2,  8, 2, BPBGAP}},
17878556Sobrien    {"720",  {512, 2, 1, 2, 112, 1440, 0xf9, 3,  9, 2, BPBGAP}},
17978556Sobrien    {"1200", {512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, BPBGAP}},
18078556Sobrien    {"1232", {1024,1, 1, 2, 192, 1232, 0xfe, 2,  8, 2, BPBGAP}},
18178556Sobrien    {"1440", {512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, BPBGAP}},
18278556Sobrien    {"2880", {512, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2, BPBGAP}}
18378556Sobrien};
18478556Sobrien
18578556Sobrienstatic const u_int8_t bootcode[] = {
18678556Sobrien    0xfa,			/* cli		    */
18778556Sobrien    0x31, 0xc0, 		/* xor	   ax,ax    */
18878556Sobrien    0x8e, 0xd0, 		/* mov	   ss,ax    */
18978556Sobrien    0xbc, 0x00, 0x7c,		/* mov	   sp,7c00h */
19078556Sobrien    0xfb,			/* sti		    */
19178556Sobrien    0x8e, 0xd8, 		/* mov	   ds,ax    */
19278556Sobrien    0xe8, 0x00, 0x00,		/* call    $ + 3    */
19378556Sobrien    0x5e,			/* pop	   si	    */
19478556Sobrien    0x83, 0xc6, 0x19,		/* add	   si,+19h  */
19578556Sobrien    0xbb, 0x07, 0x00,		/* mov	   bx,0007h */
19678556Sobrien    0xfc,			/* cld		    */
19778556Sobrien    0xac,			/* lodsb	    */
19878556Sobrien    0x84, 0xc0, 		/* test    al,al    */
19978556Sobrien    0x74, 0x06, 		/* jz	   $ + 8    */
20078556Sobrien    0xb4, 0x0e, 		/* mov	   ah,0eh   */
20178556Sobrien    0xcd, 0x10, 		/* int	   10h	    */
20278556Sobrien    0xeb, 0xf5, 		/* jmp	   $ - 9    */
20378556Sobrien    0x30, 0xe4, 		/* xor	   ah,ah    */
20478556Sobrien    0xcd, 0x16, 		/* int	   16h	    */
20578556Sobrien    0xcd, 0x19, 		/* int	   19h	    */
20678556Sobrien    0x0d, 0x0a,
20778556Sobrien    'N', 'o', 'n', '-', 's', 'y', 's', 't',
20878556Sobrien    'e', 'm', ' ', 'd', 'i', 's', 'k',
20978556Sobrien    0x0d, 0x0a,
21078556Sobrien    'P', 'r', 'e', 's', 's', ' ', 'a', 'n',
21178556Sobrien    'y', ' ', 'k', 'e', 'y', ' ', 't', 'o',
21278556Sobrien    ' ', 'r', 'e', 'b', 'o', 'o', 't',
21378556Sobrien    0x0d, 0x0a,
21478556Sobrien    0
21578556Sobrien};
21678556Sobrien
21778556Sobrienstatic volatile sig_atomic_t got_siginfo;
21878556Sobrienstatic void infohandler(int);
21978556Sobrien
22078556Sobrienstatic void check_mounted(const char *, mode_t);
22178556Sobrienstatic void getstdfmt(const char *, struct bpb *);
22278556Sobrienstatic void getdiskinfo(int, const char *, const char *, int,
22378556Sobrien			struct bpb *);
22478556Sobrienstatic void print_bpb(struct bpb *);
22578556Sobrienstatic u_int ckgeom(const char *, u_int, const char *);
22678556Sobrienstatic u_int argtou(const char *, u_int, u_int, const char *);
22778556Sobrienstatic off_t argtooff(const char *, const char *);
22878556Sobrienstatic int oklabel(const char *);
22978556Sobrienstatic void mklabel(u_int8_t *, const char *);
23078556Sobrienstatic void setstr(u_int8_t *, const char *, size_t);
23178556Sobrienstatic void usage(void);
23278556Sobrien
23378556Sobrien/*
23478556Sobrien * Construct a FAT12, FAT16, or FAT32 file system.
23578556Sobrien */
23678556Sobrienint
23778556Sobrienmain(int argc, char *argv[])
23878556Sobrien{
23978556Sobrien    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:";
24078556Sobrien    const char *opt_B = NULL, *opt_L = NULL, *opt_O = NULL, *opt_f = NULL;
24178556Sobrien    u_int opt_F = 0, opt_I = 0, opt_S = 0, opt_a = 0, opt_b = 0, opt_c = 0;
24278556Sobrien    u_int opt_e = 0, opt_h = 0, opt_i = 0, opt_k = 0, opt_m = 0, opt_n = 0;
24378556Sobrien    u_int opt_o = 0, opt_r = 0, opt_s = 0, opt_u = 0;
24478556Sobrien    int opt_N = 0;
24578556Sobrien    int Iflag = 0, mflag = 0, oflag = 0;
24678556Sobrien    char buf[MAXPATHLEN];
24778556Sobrien    struct sigaction si_sa;
24878556Sobrien    struct stat sb;
24978556Sobrien    struct timeval tv;
25078556Sobrien    struct bpb bpb;
25178556Sobrien    struct tm *tm;
25278556Sobrien    struct bs *bs;
25378556Sobrien    struct bsbpb *bsbpb;
25478556Sobrien    struct bsxbpb *bsxbpb;
25578556Sobrien    struct bsx *bsx;
25678556Sobrien    struct de *de;
25778556Sobrien    u_int8_t *img;
25878556Sobrien    const char *fname, *dtype, *bname;
25978556Sobrien    ssize_t n;
26078556Sobrien    time_t now;
26178556Sobrien    u_int fat, bss, rds, cls, dir, lsn, x, x1, x2;
26278556Sobrien    int ch, fd, fd1;
26378556Sobrien    off_t opt_create = 0, opt_ofs = 0;
26478556Sobrien
26578556Sobrien    while ((ch = getopt(argc, argv, opts)) != -1)
26678556Sobrien	switch (ch) {
26778556Sobrien	case '@':
26878556Sobrien	    opt_ofs = argtooff(optarg, "offset");
26978556Sobrien	    break;
27078556Sobrien	case 'N':
27178556Sobrien	    opt_N = 1;
27278556Sobrien	    break;
27378556Sobrien	case 'B':
27478556Sobrien	    opt_B = optarg;
27578556Sobrien	    break;
27678556Sobrien	case 'C':
27778556Sobrien	    opt_create = argtooff(optarg, "create size");
27878556Sobrien	    break;
27978556Sobrien	case 'F':
28078556Sobrien	    if (strcmp(optarg, "12") &&
28178556Sobrien		strcmp(optarg, "16") &&
28278556Sobrien		strcmp(optarg, "32"))
28378556Sobrien		errx(1, "%s: bad FAT type", optarg);
28478556Sobrien	    opt_F = atoi(optarg);
28578556Sobrien	    break;
28678556Sobrien	case 'I':
28778556Sobrien	    opt_I = argto4(optarg, 0, "volume ID");
28878556Sobrien	    Iflag = 1;
28978556Sobrien	    break;
29078556Sobrien	case 'L':
29178556Sobrien	    if (!oklabel(optarg))
29278556Sobrien		errx(1, "%s: bad volume label", optarg);
29378556Sobrien	    opt_L = optarg;
29478556Sobrien	    break;
29578556Sobrien	case 'O':
29678556Sobrien	    if (strlen(optarg) > 8)
29778556Sobrien		errx(1, "%s: bad OEM string", optarg);
29878556Sobrien	    opt_O = optarg;
29978556Sobrien	    break;
30078556Sobrien	case 'S':
30178556Sobrien	    opt_S = argto2(optarg, 1, "bytes/sector");
30278556Sobrien	    break;
30378556Sobrien	case 'a':
30478556Sobrien	    opt_a = argto4(optarg, 1, "sectors/FAT");
30578556Sobrien	    break;
30678556Sobrien	case 'b':
30778556Sobrien	    opt_b = argtox(optarg, 1, "block size");
30878556Sobrien	    opt_c = 0;
30978556Sobrien	    break;
31078556Sobrien	case 'c':
31178556Sobrien	    opt_c = argto1(optarg, 1, "sectors/cluster");
31278556Sobrien	    opt_b = 0;
31378556Sobrien	    break;
31478556Sobrien	case 'e':
31578556Sobrien	    opt_e = argto2(optarg, 1, "directory entries");
31678556Sobrien	    break;
31778556Sobrien	case 'f':
31878556Sobrien	    opt_f = optarg;
31978556Sobrien	    break;
32078556Sobrien	case 'h':
32178556Sobrien	    opt_h = argto2(optarg, 1, "drive heads");
32278556Sobrien	    break;
32378556Sobrien	case 'i':
32478556Sobrien	    opt_i = argto2(optarg, 1, "info sector");
32578556Sobrien	    break;
32678556Sobrien	case 'k':
32778556Sobrien	    opt_k = argto2(optarg, 1, "backup sector");
32878556Sobrien	    break;
32978556Sobrien	case 'm':
33078556Sobrien	    opt_m = argto1(optarg, 0, "media descriptor");
33178556Sobrien	    mflag = 1;
33278556Sobrien	    break;
33378556Sobrien	case 'n':
33478556Sobrien	    opt_n = argto1(optarg, 1, "number of FATs");
33578556Sobrien	    break;
33678556Sobrien	case 'o':
33778556Sobrien	    opt_o = argto4(optarg, 0, "hidden sectors");
33878556Sobrien	    oflag = 1;
33978556Sobrien	    break;
34078556Sobrien	case 'r':
34178556Sobrien	    opt_r = argto2(optarg, 1, "reserved sectors");
34278556Sobrien	    break;
34378556Sobrien	case 's':
34478556Sobrien	    opt_s = argto4(optarg, 1, "file system size");
34578556Sobrien	    break;
34678556Sobrien	case 'u':
34778556Sobrien	    opt_u = argto2(optarg, 1, "sectors/track");
34878556Sobrien	    break;
34978556Sobrien	default:
35078556Sobrien	    usage();
35178556Sobrien	}
35278556Sobrien    argc -= optind;
35378556Sobrien    argv += optind;
35478556Sobrien    if (argc < 1 || argc > 2)
35578556Sobrien	usage();
35678556Sobrien    fname = *argv++;
35778556Sobrien    if (!opt_create && !strchr(fname, '/')) {
35878556Sobrien	snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
35978556Sobrien	if (!(fname = strdup(buf)))
36078556Sobrien	    err(1, NULL);
36178556Sobrien    }
36278556Sobrien    dtype = *argv;
36378556Sobrien    if (opt_create) {
36478556Sobrien	if (opt_N)
36578556Sobrien	    errx(1, "create (-C) is incompatible with -N");
36678556Sobrien	fd = open(fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
36778556Sobrien	if (fd == -1)
36878556Sobrien	    errx(1, "failed to create %s", fname);
36978556Sobrien	if (ftruncate(fd, opt_create))
37078556Sobrien	    errx(1, "failed to initialize %jd bytes", (intmax_t)opt_create);
37178556Sobrien    } else if ((fd = open(fname, opt_N ? O_RDONLY : O_RDWR)) == -1)
37278556Sobrien	err(1, "%s", fname);
37378556Sobrien    if (fstat(fd, &sb))
37478556Sobrien	err(1, "%s", fname);
37578556Sobrien    if (opt_create) {
37678556Sobrien	if (!S_ISREG(sb.st_mode))
37778556Sobrien	    warnx("warning, %s is not a regular file", fname);
37878556Sobrien    } else {
37978556Sobrien	if (!S_ISCHR(sb.st_mode))
38078556Sobrien	    warnx("warning, %s is not a character device", fname);
38178556Sobrien    }
38278556Sobrien    if (!opt_N)
38378556Sobrien	check_mounted(fname, sb.st_mode);
38478556Sobrien    if (opt_ofs && opt_ofs != lseek(fd, opt_ofs, SEEK_SET))
38578556Sobrien	errx(1, "cannot seek to %jd", (intmax_t)opt_ofs);
38678556Sobrien    memset(&bpb, 0, sizeof(bpb));
38778556Sobrien    if (opt_f) {
38878556Sobrien	getstdfmt(opt_f, &bpb);
38978556Sobrien	bpb.bpbHugeSectors = bpb.bpbSectors;
39078556Sobrien	bpb.bpbSectors = 0;
39178556Sobrien	bpb.bpbBigFATsecs = bpb.bpbFATsecs;
39278556Sobrien	bpb.bpbFATsecs = 0;
39378556Sobrien    }
39478556Sobrien    if (opt_h)
39578556Sobrien	bpb.bpbHeads = opt_h;
39678556Sobrien    if (opt_u)
39778556Sobrien	bpb.bpbSecPerTrack = opt_u;
39878556Sobrien    if (opt_S)
39978556Sobrien	bpb.bpbBytesPerSec = opt_S;
40078556Sobrien    if (opt_s)
40178556Sobrien	bpb.bpbHugeSectors = opt_s;
40278556Sobrien    if (oflag)
40378556Sobrien	bpb.bpbHiddenSecs = opt_o;
40478556Sobrien    if (!(opt_f || (opt_h && opt_u && opt_S && opt_s && oflag))) {
40578556Sobrien	off_t delta;
40678556Sobrien	getdiskinfo(fd, fname, dtype, oflag, &bpb);
40778556Sobrien	bpb.bpbHugeSectors -= (opt_ofs / bpb.bpbBytesPerSec);
40878556Sobrien	delta = bpb.bpbHugeSectors % bpb.bpbSecPerTrack;
40978556Sobrien	if (delta != 0) {
41078556Sobrien	    warnx("trim %d sectors to adjust to a multiple of %d",
41178556Sobrien		(int)delta, bpb.bpbSecPerTrack);
41278556Sobrien	    bpb.bpbHugeSectors -= delta;
41378556Sobrien	}
41478556Sobrien	if (bpb.bpbSecPerClust == 0) {	/* set defaults */
41578556Sobrien	    if (bpb.bpbHugeSectors <= 6000)	/* about 3MB -> 512 bytes */
41678556Sobrien		bpb.bpbSecPerClust = 1;
41778556Sobrien	    else if (bpb.bpbHugeSectors <= (1<<17)) /* 64M -> 4k */
41878556Sobrien		bpb.bpbSecPerClust = 8;
41978556Sobrien	    else if (bpb.bpbHugeSectors <= (1<<19)) /* 256M -> 8k */
42078556Sobrien		bpb.bpbSecPerClust = 16;
42178556Sobrien	    else if (bpb.bpbHugeSectors <= (1<<21)) /* 1G -> 16k */
42278556Sobrien		bpb.bpbSecPerClust = 32;
42378556Sobrien	    else
42478556Sobrien		bpb.bpbSecPerClust = 64;		/* otherwise 32k */
42578556Sobrien	}
42678556Sobrien    }
42778556Sobrien    if (!powerof2(bpb.bpbBytesPerSec))
42878556Sobrien	errx(1, "bytes/sector (%u) is not a power of 2", bpb.bpbBytesPerSec);
42978556Sobrien    if (bpb.bpbBytesPerSec < MINBPS)
43078556Sobrien	errx(1, "bytes/sector (%u) is too small; minimum is %u",
43178556Sobrien	     bpb.bpbBytesPerSec, MINBPS);
43278556Sobrien    if (!(fat = opt_F)) {
43378556Sobrien	if (opt_f)
43478556Sobrien	    fat = 12;
43578556Sobrien	else if (!opt_e && (opt_i || opt_k))
43678556Sobrien	    fat = 32;
43778556Sobrien    }
43878556Sobrien    if ((fat == 32 && opt_e) || (fat != 32 && (opt_i || opt_k)))
43978556Sobrien	errx(1, "-%c is not a legal FAT%s option",
44078556Sobrien	     fat == 32 ? 'e' : opt_i ? 'i' : 'k',
44178556Sobrien	     fat == 32 ? "32" : "12/16");
44278556Sobrien    if (opt_f && fat == 32)
44378556Sobrien	bpb.bpbRootDirEnts = 0;
44478556Sobrien    if (opt_b) {
44578556Sobrien	if (!powerof2(opt_b))
44678556Sobrien	    errx(1, "block size (%u) is not a power of 2", opt_b);
44778556Sobrien	if (opt_b < bpb.bpbBytesPerSec)
44878556Sobrien	    errx(1, "block size (%u) is too small; minimum is %u",
44978556Sobrien		 opt_b, bpb.bpbBytesPerSec);
45078556Sobrien	if (opt_b > bpb.bpbBytesPerSec * MAXSPC)
45178556Sobrien	    errx(1, "block size (%u) is too large; maximum is %u",
45278556Sobrien		 opt_b, bpb.bpbBytesPerSec * MAXSPC);
45378556Sobrien	bpb.bpbSecPerClust = opt_b / bpb.bpbBytesPerSec;
45478556Sobrien    }
45578556Sobrien    if (opt_c) {
45678556Sobrien	if (!powerof2(opt_c))
45778556Sobrien	    errx(1, "sectors/cluster (%u) is not a power of 2", opt_c);
45878556Sobrien	bpb.bpbSecPerClust = opt_c;
45978556Sobrien    }
46078556Sobrien    if (opt_r)
46178556Sobrien	bpb.bpbResSectors = opt_r;
46278556Sobrien    if (opt_n) {
46378556Sobrien	if (opt_n > MAXNFT)
46478556Sobrien	    errx(1, "number of FATs (%u) is too large; maximum is %u",
46578556Sobrien		 opt_n, MAXNFT);
46678556Sobrien	bpb.bpbFATs = opt_n;
46778556Sobrien    }
46878556Sobrien    if (opt_e)
46978556Sobrien	bpb.bpbRootDirEnts = opt_e;
47078556Sobrien    if (mflag) {
47178556Sobrien	if (opt_m < 0xf0)
47278556Sobrien	    errx(1, "illegal media descriptor (%#x)", opt_m);
47378556Sobrien	bpb.bpbMedia = opt_m;
47478556Sobrien    }
47578556Sobrien    if (opt_a)
47678556Sobrien	bpb.bpbBigFATsecs = opt_a;
47778556Sobrien    if (opt_i)
47878556Sobrien	bpb.bpbFSInfo = opt_i;
47978556Sobrien    if (opt_k)
48078556Sobrien	bpb.bpbBackup = opt_k;
48178556Sobrien    bss = 1;
48278556Sobrien    bname = NULL;
48378556Sobrien    fd1 = -1;
48478556Sobrien    if (opt_B) {
48578556Sobrien	bname = opt_B;
48678556Sobrien	if (!strchr(bname, '/')) {
48778556Sobrien	    snprintf(buf, sizeof(buf), "/boot/%s", bname);
48878556Sobrien	    if (!(bname = strdup(buf)))
48978556Sobrien		err(1, NULL);
49078556Sobrien	}
49178556Sobrien	if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb))
49278556Sobrien	    err(1, "%s", bname);
49378556Sobrien	if (!S_ISREG(sb.st_mode) || sb.st_size % bpb.bpbBytesPerSec ||
49478556Sobrien	    sb.st_size < bpb.bpbBytesPerSec ||
49578556Sobrien	    sb.st_size > bpb.bpbBytesPerSec * MAXU16)
49678556Sobrien	    errx(1, "%s: inappropriate file type or format", bname);
49778556Sobrien	bss = sb.st_size / bpb.bpbBytesPerSec;
49878556Sobrien    }
49978556Sobrien    if (!bpb.bpbFATs)
50078556Sobrien	bpb.bpbFATs = 2;
50178556Sobrien    if (!fat) {
50278556Sobrien	if (bpb.bpbHugeSectors < (bpb.bpbResSectors ? bpb.bpbResSectors : bss) +
50378556Sobrien	    howmany((RESFTE + (bpb.bpbSecPerClust ? MINCLS16 : MAXCLS12 + 1)) *
50478556Sobrien		(bpb.bpbSecPerClust ? 16 : 12) / BPN,
50578556Sobrien		bpb.bpbBytesPerSec * NPB) *
50678556Sobrien	    bpb.bpbFATs +
50778556Sobrien	    howmany(bpb.bpbRootDirEnts ? bpb.bpbRootDirEnts : DEFRDE,
50878556Sobrien		    bpb.bpbBytesPerSec / sizeof(struct de)) +
50978556Sobrien	    (bpb.bpbSecPerClust ? MINCLS16 : MAXCLS12 + 1) *
51078556Sobrien	    (bpb.bpbSecPerClust ? bpb.bpbSecPerClust :
51178556Sobrien	     howmany(DEFBLK, bpb.bpbBytesPerSec)))
51278556Sobrien	    fat = 12;
51378556Sobrien	else if (bpb.bpbRootDirEnts || bpb.bpbHugeSectors <
51478556Sobrien		 (bpb.bpbResSectors ? bpb.bpbResSectors : bss) +
51578556Sobrien		 howmany((RESFTE + MAXCLS16) * 2, bpb.bpbBytesPerSec) *
51678556Sobrien		 bpb.bpbFATs +
51778556Sobrien		 howmany(DEFRDE, bpb.bpbBytesPerSec / sizeof(struct de)) +
51878556Sobrien		 (MAXCLS16 + 1) *
51978556Sobrien		 (bpb.bpbSecPerClust ? bpb.bpbSecPerClust :
52078556Sobrien		  howmany(8192, bpb.bpbBytesPerSec)))
52178556Sobrien	    fat = 16;
52278556Sobrien	else
52378556Sobrien	    fat = 32;
52478556Sobrien    }
52578556Sobrien    x = bss;
52678556Sobrien    if (fat == 32) {
52778556Sobrien	if (!bpb.bpbFSInfo) {
52878556Sobrien	    if (x == MAXU16 || x == bpb.bpbBackup)
52978556Sobrien		errx(1, "no room for info sector");
53078556Sobrien	    bpb.bpbFSInfo = x;
53178556Sobrien	}
53278556Sobrien	if (bpb.bpbFSInfo != MAXU16 && x <= bpb.bpbFSInfo)
53378556Sobrien	    x = bpb.bpbFSInfo + 1;
53478556Sobrien	if (!bpb.bpbBackup) {
53578556Sobrien	    if (x == MAXU16)
53678556Sobrien		errx(1, "no room for backup sector");
53778556Sobrien	    bpb.bpbBackup = x;
53878556Sobrien	} else if (bpb.bpbBackup != MAXU16 && bpb.bpbBackup == bpb.bpbFSInfo)
53978556Sobrien	    errx(1, "backup sector would overwrite info sector");
54078556Sobrien	if (bpb.bpbBackup != MAXU16 && x <= bpb.bpbBackup)
54178556Sobrien	    x = bpb.bpbBackup + 1;
54278556Sobrien    }
54378556Sobrien    if (!bpb.bpbResSectors)
54478556Sobrien	bpb.bpbResSectors = fat == 32 ?
54578556Sobrien	    MAX(x, MAX(16384 / bpb.bpbBytesPerSec, 4)) : x;
54678556Sobrien    else if (bpb.bpbResSectors < x)
54778556Sobrien	errx(1, "too few reserved sectors (need %d have %d)", x,
54878556Sobrien	     bpb.bpbResSectors);
54978556Sobrien    if (fat != 32 && !bpb.bpbRootDirEnts)
55078556Sobrien	bpb.bpbRootDirEnts = DEFRDE;
55178556Sobrien    rds = howmany(bpb.bpbRootDirEnts, bpb.bpbBytesPerSec / sizeof(struct de));
55278556Sobrien    if (!bpb.bpbSecPerClust)
55378556Sobrien	for (bpb.bpbSecPerClust = howmany(fat == 16 ? DEFBLK16 :
55478556Sobrien					  DEFBLK, bpb.bpbBytesPerSec);
55578556Sobrien	     bpb.bpbSecPerClust < MAXSPC &&
55678556Sobrien	     bpb.bpbResSectors +
55778556Sobrien	     howmany((RESFTE + maxcls(fat)) * (fat / BPN),
55878556Sobrien		     bpb.bpbBytesPerSec * NPB) *
55978556Sobrien	     bpb.bpbFATs +
56078556Sobrien	     rds +
56178556Sobrien	     (u_int64_t) (maxcls(fat) + 1) *
56278556Sobrien	     bpb.bpbSecPerClust <= bpb.bpbHugeSectors;
56378556Sobrien	     bpb.bpbSecPerClust <<= 1)
56478556Sobrien	    continue;
56578556Sobrien    if (fat != 32 && bpb.bpbBigFATsecs > MAXU16)
56678556Sobrien	errx(1, "too many sectors/FAT for FAT12/16");
56778556Sobrien    x1 = bpb.bpbResSectors + rds;
56878556Sobrien    x = bpb.bpbBigFATsecs ? bpb.bpbBigFATsecs : 1;
56978556Sobrien    if (x1 + (u_int64_t)x * bpb.bpbFATs > bpb.bpbHugeSectors)
57078556Sobrien	errx(1, "meta data exceeds file system size");
57178556Sobrien    x1 += x * bpb.bpbFATs;
57278556Sobrien    x = (u_int64_t)(bpb.bpbHugeSectors - x1) * bpb.bpbBytesPerSec * NPB /
57378556Sobrien	(bpb.bpbSecPerClust * bpb.bpbBytesPerSec * NPB + fat /
57478556Sobrien	 BPN * bpb.bpbFATs);
57578556Sobrien    x2 = howmany((RESFTE + MIN(x, maxcls(fat))) * (fat / BPN),
57678556Sobrien		 bpb.bpbBytesPerSec * NPB);
57778556Sobrien    if (!bpb.bpbBigFATsecs) {
57878556Sobrien	bpb.bpbBigFATsecs = x2;
57978556Sobrien	x1 += (bpb.bpbBigFATsecs - 1) * bpb.bpbFATs;
58078556Sobrien    }
58178556Sobrien    cls = (bpb.bpbHugeSectors - x1) / bpb.bpbSecPerClust;
58278556Sobrien    x = (u_int64_t)bpb.bpbBigFATsecs * bpb.bpbBytesPerSec * NPB / (fat / BPN) -
58378556Sobrien	RESFTE;
58478556Sobrien    if (cls > x)
58578556Sobrien	cls = x;
58678556Sobrien    if (bpb.bpbBigFATsecs < x2)
58778556Sobrien	warnx("warning: sectors/FAT limits file system to %u clusters",
58878556Sobrien	      cls);
58978556Sobrien    if (cls < mincls(fat))
59078556Sobrien	errx(1, "%u clusters too few clusters for FAT%u, need %u", cls, fat,
59178556Sobrien	    mincls(fat));
59278556Sobrien    if (cls > maxcls(fat)) {
59378556Sobrien	cls = maxcls(fat);
59478556Sobrien	bpb.bpbHugeSectors = x1 + (cls + 1) * bpb.bpbSecPerClust - 1;
59578556Sobrien	warnx("warning: FAT type limits file system to %u sectors",
59678556Sobrien	      bpb.bpbHugeSectors);
59778556Sobrien    }
59878556Sobrien    printf("%s: %u sector%s in %u FAT%u cluster%s "
59978556Sobrien	   "(%u bytes/cluster)\n", fname, cls * bpb.bpbSecPerClust,
60078556Sobrien	   cls * bpb.bpbSecPerClust == 1 ? "" : "s", cls, fat,
60178556Sobrien	   cls == 1 ? "" : "s", bpb.bpbBytesPerSec * bpb.bpbSecPerClust);
60278556Sobrien    if (!bpb.bpbMedia)
60378556Sobrien	bpb.bpbMedia = !bpb.bpbHiddenSecs ? 0xf0 : 0xf8;
60478556Sobrien    if (fat == 32)
60578556Sobrien	bpb.bpbRootClust = RESFTE;
60678556Sobrien    if (bpb.bpbHiddenSecs + bpb.bpbHugeSectors <= MAXU16) {
60778556Sobrien	bpb.bpbSectors = bpb.bpbHugeSectors;
60878556Sobrien	bpb.bpbHugeSectors = 0;
60978556Sobrien    }
61078556Sobrien    if (fat != 32) {
61178556Sobrien	bpb.bpbFATsecs = bpb.bpbBigFATsecs;
61278556Sobrien	bpb.bpbBigFATsecs = 0;
61378556Sobrien    }
61478556Sobrien    print_bpb(&bpb);
61578556Sobrien    if (!opt_N) {
61678556Sobrien	gettimeofday(&tv, NULL);
61778556Sobrien	now = tv.tv_sec;
61878556Sobrien	tm = localtime(&now);
61978556Sobrien	if (!(img = malloc(bpb.bpbBytesPerSec)))
62078556Sobrien	    err(1, NULL);
62178556Sobrien	dir = bpb.bpbResSectors + (bpb.bpbFATsecs ? bpb.bpbFATsecs :
62278556Sobrien				   bpb.bpbBigFATsecs) * bpb.bpbFATs;
62378556Sobrien	memset(&si_sa, 0, sizeof(si_sa));
62478556Sobrien	si_sa.sa_handler = infohandler;
62578556Sobrien	if (sigaction(SIGINFO, &si_sa, NULL) == -1)
62678556Sobrien		err(1, "sigaction SIGINFO");
62778556Sobrien	for (lsn = 0; lsn < dir + (fat == 32 ? bpb.bpbSecPerClust : rds); lsn++) {
62878556Sobrien	    if (got_siginfo) {
62978556Sobrien		    fprintf(stderr,"%s: writing sector %u of %u (%u%%)\n",
63078556Sobrien			fname, lsn,
63178556Sobrien			(dir + (fat == 32 ? bpb.bpbSecPerClust: rds)),
63278556Sobrien			(lsn * 100) / (dir +
63378556Sobrien			    (fat == 32 ? bpb.bpbSecPerClust: rds)));
63478556Sobrien		    got_siginfo = 0;
63578556Sobrien	    }
63678556Sobrien	    x = lsn;
63778556Sobrien	    if (opt_B &&
63878556Sobrien		fat == 32 && bpb.bpbBackup != MAXU16 &&
63978556Sobrien		bss <= bpb.bpbBackup && x >= bpb.bpbBackup) {
64078556Sobrien		x -= bpb.bpbBackup;
64178556Sobrien		if (!x && lseek(fd1, opt_ofs, SEEK_SET))
64278556Sobrien		    err(1, "%s", bname);
64378556Sobrien	    }
64478556Sobrien	    if (opt_B && x < bss) {
64578556Sobrien		if ((n = read(fd1, img, bpb.bpbBytesPerSec)) == -1)
64678556Sobrien		    err(1, "%s", bname);
64778556Sobrien		if ((unsigned)n != bpb.bpbBytesPerSec)
64878556Sobrien		    errx(1, "%s: can't read sector %u", bname, x);
64978556Sobrien	    } else
65078556Sobrien		memset(img, 0, bpb.bpbBytesPerSec);
65178556Sobrien	    if (!lsn ||
65278556Sobrien		(fat == 32 && bpb.bpbBackup != MAXU16 &&
65378556Sobrien		 lsn == bpb.bpbBackup)) {
65478556Sobrien		x1 = sizeof(struct bs);
65578556Sobrien		bsbpb = (struct bsbpb *)(img + x1);
65678556Sobrien		mk2(bsbpb->bpbBytesPerSec, bpb.bpbBytesPerSec);
65778556Sobrien		mk1(bsbpb->bpbSecPerClust, bpb.bpbSecPerClust);
65878556Sobrien		mk2(bsbpb->bpbResSectors, bpb.bpbResSectors);
65978556Sobrien		mk1(bsbpb->bpbFATs, bpb.bpbFATs);
66078556Sobrien		mk2(bsbpb->bpbRootDirEnts, bpb.bpbRootDirEnts);
66178556Sobrien		mk2(bsbpb->bpbSectors, bpb.bpbSectors);
66278556Sobrien		mk1(bsbpb->bpbMedia, bpb.bpbMedia);
66378556Sobrien		mk2(bsbpb->bpbFATsecs, bpb.bpbFATsecs);
66478556Sobrien		mk2(bsbpb->bpbSecPerTrack, bpb.bpbSecPerTrack);
66578556Sobrien		mk2(bsbpb->bpbHeads, bpb.bpbHeads);
66678556Sobrien		mk4(bsbpb->bpbHiddenSecs, bpb.bpbHiddenSecs);
66778556Sobrien		mk4(bsbpb->bpbHugeSectors, bpb.bpbHugeSectors);
66878556Sobrien		x1 += sizeof(struct bsbpb);
66978556Sobrien		if (fat == 32) {
67078556Sobrien		    bsxbpb = (struct bsxbpb *)(img + x1);
67178556Sobrien		    mk4(bsxbpb->bpbBigFATsecs, bpb.bpbBigFATsecs);
67278556Sobrien		    mk2(bsxbpb->bpbExtFlags, 0);
67378556Sobrien		    mk2(bsxbpb->bpbFSVers, 0);
67478556Sobrien		    mk4(bsxbpb->bpbRootClust, bpb.bpbRootClust);
67578556Sobrien		    mk2(bsxbpb->bpbFSInfo, bpb.bpbFSInfo);
67678556Sobrien		    mk2(bsxbpb->bpbBackup, bpb.bpbBackup);
67778556Sobrien		    x1 += sizeof(struct bsxbpb);
67878556Sobrien		}
67978556Sobrien		bsx = (struct bsx *)(img + x1);
68078556Sobrien		mk1(bsx->exBootSignature, 0x29);
68178556Sobrien		if (Iflag)
68278556Sobrien		    x = opt_I;
68378556Sobrien		else
68478556Sobrien		    x = (((u_int)(1 + tm->tm_mon) << 8 |
68578556Sobrien			  (u_int)tm->tm_mday) +
68678556Sobrien			 ((u_int)tm->tm_sec << 8 |
68778556Sobrien			  (u_int)(tv.tv_usec / 10))) << 16 |
68878556Sobrien			((u_int)(1900 + tm->tm_year) +
68978556Sobrien			 ((u_int)tm->tm_hour << 8 |
69078556Sobrien			  (u_int)tm->tm_min));
69178556Sobrien		mk4(bsx->exVolumeID, x);
69278556Sobrien		mklabel(bsx->exVolumeLabel, opt_L ? opt_L : "NO_NAME");
69378556Sobrien		sprintf(buf, "FAT%u", fat);
69478556Sobrien		setstr(bsx->exFileSysType, buf, sizeof(bsx->exFileSysType));
69578556Sobrien		if (!opt_B) {
69678556Sobrien		    x1 += sizeof(struct bsx);
69778556Sobrien		    bs = (struct bs *)img;
69878556Sobrien		    mk1(bs->bsJump[0], 0xeb);
69978556Sobrien		    mk1(bs->bsJump[1], x1 - 2);
70078556Sobrien		    mk1(bs->bsJump[2], 0x90);
70178556Sobrien		    setstr(bs->bsOemName, opt_O ? opt_O : "BSD4.4  ",
70278556Sobrien			   sizeof(bs->bsOemName));
70378556Sobrien		    memcpy(img + x1, bootcode, sizeof(bootcode));
70478556Sobrien		    mk2(img + MINBPS - 2, DOSMAGIC);
70578556Sobrien		}
70678556Sobrien	    } else if (fat == 32 && bpb.bpbFSInfo != MAXU16 &&
70778556Sobrien		       (lsn == bpb.bpbFSInfo ||
70878556Sobrien			(bpb.bpbBackup != MAXU16 &&
70978556Sobrien			 lsn == bpb.bpbBackup + bpb.bpbFSInfo))) {
71078556Sobrien		mk4(img, 0x41615252);
71178556Sobrien		mk4(img + MINBPS - 28, 0x61417272);
71278556Sobrien		mk4(img + MINBPS - 24, 0xffffffff);
71378556Sobrien		mk4(img + MINBPS - 20, bpb.bpbRootClust);
71478556Sobrien		mk2(img + MINBPS - 2, DOSMAGIC);
71578556Sobrien	    } else if (lsn >= bpb.bpbResSectors && lsn < dir &&
71678556Sobrien		       !((lsn - bpb.bpbResSectors) %
71778556Sobrien			 (bpb.bpbFATsecs ? bpb.bpbFATsecs :
71878556Sobrien			  bpb.bpbBigFATsecs))) {
71978556Sobrien		mk1(img[0], bpb.bpbMedia);
72078556Sobrien		for (x = 1; x < fat * (fat == 32 ? 3 : 2) / 8; x++)
72178556Sobrien		    mk1(img[x], fat == 32 && x % 4 == 3 ? 0x0f : 0xff);
72278556Sobrien	    } else if (lsn == dir && opt_L) {
72378556Sobrien		de = (struct de *)img;
72478556Sobrien		mklabel(de->deName, opt_L);
72578556Sobrien		mk1(de->deAttributes, 050);
72678556Sobrien		x = (u_int)tm->tm_hour << 11 |
72778556Sobrien		    (u_int)tm->tm_min << 5 |
72878556Sobrien		    (u_int)tm->tm_sec >> 1;
72978556Sobrien		mk2(de->deMTime, x);
73078556Sobrien		x = (u_int)(tm->tm_year - 80) << 9 |
73178556Sobrien		    (u_int)(tm->tm_mon + 1) << 5 |
73278556Sobrien		    (u_int)tm->tm_mday;
73378556Sobrien		mk2(de->deMDate, x);
73478556Sobrien	    }
73578556Sobrien	    if ((n = write(fd, img, bpb.bpbBytesPerSec)) == -1)
73678556Sobrien		err(1, "%s", fname);
73778556Sobrien	    if ((unsigned)n != bpb.bpbBytesPerSec)
73878556Sobrien		errx(1, "%s: can't write sector %u", fname, lsn);
73978556Sobrien	}
74078556Sobrien    }
74178556Sobrien    return 0;
74278556Sobrien}
74378556Sobrien
74478556Sobrien/*
74578556Sobrien * Exit with error if file system is mounted.
74678556Sobrien */
74778556Sobrienstatic void
74878556Sobriencheck_mounted(const char *fname, mode_t mode)
74978556Sobrien{
75078556Sobrien    struct statfs *mp;
75178556Sobrien    const char *s1, *s2;
75278556Sobrien    size_t len;
75378556Sobrien    int n, r;
75478556Sobrien
75578556Sobrien    if (!(n = getmntinfo(&mp, MNT_NOWAIT)))
75678556Sobrien	err(1, "getmntinfo");
75778556Sobrien    len = strlen(_PATH_DEV);
75878556Sobrien    s1 = fname;
75978556Sobrien    if (!strncmp(s1, _PATH_DEV, len))
76078556Sobrien	s1 += len;
76178556Sobrien    r = S_ISCHR(mode) && s1 != fname && *s1 == 'r';
76278556Sobrien    for (; n--; mp++) {
76378556Sobrien	s2 = mp->f_mntfromname;
76478556Sobrien	if (!strncmp(s2, _PATH_DEV, len))
76578556Sobrien	    s2 += len;
76678556Sobrien	if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) ||
76778556Sobrien	    !strcmp(s1, s2))
76878556Sobrien	    errx(1, "%s is mounted on %s", fname, mp->f_mntonname);
76978556Sobrien    }
77078556Sobrien}
77178556Sobrien
77278556Sobrien/*
77378556Sobrien * Get a standard format.
77478556Sobrien */
77578556Sobrienstatic void
77678556Sobriengetstdfmt(const char *fmt, struct bpb *bpb)
77778556Sobrien{
77878556Sobrien    u_int x, i;
77978556Sobrien
78078556Sobrien    x = sizeof(stdfmt) / sizeof(stdfmt[0]);
78178556Sobrien    for (i = 0; i < x && strcmp(fmt, stdfmt[i].name); i++);
78278556Sobrien    if (i == x)
78378556Sobrien	errx(1, "%s: unknown standard format", fmt);
78478556Sobrien    *bpb = stdfmt[i].bpb;
78578556Sobrien}
78678556Sobrien
78778556Sobrien/*
78878556Sobrien * Get disk slice, partition, and geometry information.
78978556Sobrien */
79078556Sobrienstatic void
79178556Sobriengetdiskinfo(int fd, const char *fname, const char *dtype, __unused int oflag,
79278556Sobrien	    struct bpb *bpb)
79378556Sobrien{
79478556Sobrien    struct disklabel *lp, dlp;
79578556Sobrien    struct fd_type type;
79678556Sobrien    off_t ms, hs = 0;
79778556Sobrien
79878556Sobrien    lp = NULL;
79978556Sobrien
80078556Sobrien    /* If the user specified a disk type, try to use that */
80178556Sobrien    if (dtype != NULL) {
80278556Sobrien	lp = getdiskbyname(dtype);
80378556Sobrien    }
80478556Sobrien
80578556Sobrien    /* Maybe it's a floppy drive */
80678556Sobrien    if (lp == NULL) {
80778556Sobrien	if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) {
80878556Sobrien	    struct stat st;
80978556Sobrien
81078556Sobrien	    if (fstat(fd, &st))
81178556Sobrien		err(1, "Cannot get disk size");
81278556Sobrien	    /* create a fake geometry for a file image */
81378556Sobrien	    ms = st.st_size;
81478556Sobrien	    dlp.d_secsize = 512;
81578556Sobrien	    dlp.d_nsectors = 63;
81678556Sobrien	    dlp.d_ntracks = 255;
81778556Sobrien	    dlp.d_secperunit = ms / dlp.d_secsize;
81878556Sobrien	    lp = &dlp;
81978556Sobrien	} else if (ioctl(fd, FD_GTYPE, &type) != -1) {
82078556Sobrien	    dlp.d_secsize = 128 << type.secsize;
82178556Sobrien	    dlp.d_nsectors = type.sectrac;
82278556Sobrien	    dlp.d_ntracks = type.heads;
82378556Sobrien	    dlp.d_secperunit = ms / dlp.d_secsize;
82478556Sobrien	    lp = &dlp;
82578556Sobrien	}
82678556Sobrien    }
82778556Sobrien
82878556Sobrien    /* Maybe it's a fixed drive */
82978556Sobrien    if (lp == NULL) {
83078556Sobrien	if (bpb->bpbBytesPerSec)
83178556Sobrien	    dlp.d_secsize = bpb->bpbBytesPerSec;
83278556Sobrien	if (ioctl(fd, DIOCGDINFO, &dlp) == -1) {
83378556Sobrien	    if (bpb->bpbBytesPerSec == 0 && ioctl(fd, DIOCGSECTORSIZE,
83478556Sobrien						  &dlp.d_secsize) == -1)
83578556Sobrien		errx(1, "Cannot get sector size, %s", strerror(errno));
83678556Sobrien
83778556Sobrien	    dlp.d_secperunit = ms / dlp.d_secsize;
83878556Sobrien
83978556Sobrien	    if (bpb->bpbSecPerTrack == 0 && ioctl(fd, DIOCGFWSECTORS,
84078556Sobrien						  &dlp.d_nsectors) == -1) {
84178556Sobrien		warnx("Cannot get number of sectors per track, %s", strerror(errno));
84278556Sobrien		dlp.d_nsectors = 63;
84378556Sobrien	    }
84478556Sobrien	    if (bpb->bpbHeads == 0 &&
84578556Sobrien	        ioctl(fd, DIOCGFWHEADS, &dlp.d_ntracks) == -1) {
84678556Sobrien		warnx("Cannot get number of heads, %s", strerror(errno));
84778556Sobrien		if (dlp.d_secperunit <= 63*1*1024)
84878556Sobrien		    dlp.d_ntracks = 1;
84978556Sobrien		else if (dlp.d_secperunit <= 63*16*1024)
85078556Sobrien		    dlp.d_ntracks = 16;
85178556Sobrien		else
85278556Sobrien		    dlp.d_ntracks = 255;
85378556Sobrien	    }
85478556Sobrien	}
85578556Sobrien
85678556Sobrien	hs = (ms / dlp.d_secsize) - dlp.d_secperunit;
85778556Sobrien	lp = &dlp;
85878556Sobrien    }
85978556Sobrien
86078556Sobrien    if (bpb->bpbBytesPerSec == 0)
86178556Sobrien	bpb->bpbBytesPerSec = ckgeom(fname, lp->d_secsize, "bytes/sector");
86278556Sobrien    if (bpb->bpbSecPerTrack == 0)
86378556Sobrien	bpb->bpbSecPerTrack = ckgeom(fname, lp->d_nsectors, "sectors/track");
86478556Sobrien    if (bpb->bpbHeads == 0)
86578556Sobrien	bpb->bpbHeads = ckgeom(fname, lp->d_ntracks, "drive heads");
86678556Sobrien    if (bpb->bpbHugeSectors == 0)
86778556Sobrien	bpb->bpbHugeSectors = lp->d_secperunit;
86878556Sobrien    if (bpb->bpbHiddenSecs == 0)
86978556Sobrien	bpb->bpbHiddenSecs = hs;
87078556Sobrien}
87178556Sobrien
87278556Sobrien/*
87378556Sobrien * Print out BPB values.
87478556Sobrien */
87578556Sobrienstatic void
87678556Sobrienprint_bpb(struct bpb *bpb)
87778556Sobrien{
87878556Sobrien    printf("BytesPerSec=%u SecPerClust=%u ResSectors=%u FATs=%u",
87978556Sobrien	   bpb->bpbBytesPerSec, bpb->bpbSecPerClust, bpb->bpbResSectors,
88078556Sobrien	   bpb->bpbFATs);
88178556Sobrien    if (bpb->bpbRootDirEnts)
88278556Sobrien	printf(" RootDirEnts=%u", bpb->bpbRootDirEnts);
88378556Sobrien    if (bpb->bpbSectors)
88478556Sobrien	printf(" Sectors=%u", bpb->bpbSectors);
88578556Sobrien    printf(" Media=%#x", bpb->bpbMedia);
88678556Sobrien    if (bpb->bpbFATsecs)
88778556Sobrien	printf(" FATsecs=%u", bpb->bpbFATsecs);
88878556Sobrien    printf(" SecPerTrack=%u Heads=%u HiddenSecs=%u", bpb->bpbSecPerTrack,
88978556Sobrien	   bpb->bpbHeads, bpb->bpbHiddenSecs);
89078556Sobrien    if (bpb->bpbHugeSectors)
89178556Sobrien	printf(" HugeSectors=%u", bpb->bpbHugeSectors);
89278556Sobrien    if (!bpb->bpbFATsecs) {
89378556Sobrien	printf(" FATsecs=%u RootCluster=%u", bpb->bpbBigFATsecs,
89478556Sobrien	       bpb->bpbRootClust);
89578556Sobrien	printf(" FSInfo=");
89678556Sobrien	printf(bpb->bpbFSInfo == MAXU16 ? "%#x" : "%u", bpb->bpbFSInfo);
89778556Sobrien	printf(" Backup=");
89878556Sobrien	printf(bpb->bpbBackup == MAXU16 ? "%#x" : "%u", bpb->bpbBackup);
89978556Sobrien    }
90078556Sobrien    printf("\n");
90178556Sobrien}
90278556Sobrien
90378556Sobrien/*
90478556Sobrien * Check a disk geometry value.
90578556Sobrien */
90678556Sobrienstatic u_int
90778556Sobrienckgeom(const char *fname, u_int val, const char *msg)
90878556Sobrien{
90978556Sobrien    if (!val)
91078556Sobrien	errx(1, "%s: no default %s", fname, msg);
91178556Sobrien    if (val > MAXU16)
91278556Sobrien	errx(1, "%s: illegal %s %d", fname, msg, val);
91378556Sobrien    return val;
91478556Sobrien}
91578556Sobrien
91678556Sobrien/*
91778556Sobrien * Convert and check a numeric option argument.
91878556Sobrien */
91978556Sobrienstatic u_int
92078556Sobrienargtou(const char *arg, u_int lo, u_int hi, const char *msg)
92178556Sobrien{
92278556Sobrien    char *s;
92378556Sobrien    u_long x;
92478556Sobrien
92578556Sobrien    errno = 0;
92678556Sobrien    x = strtoul(arg, &s, 0);
92778556Sobrien    if (errno || !*arg || *s || x < lo || x > hi)
92878556Sobrien	errx(1, "%s: bad %s", arg, msg);
92978556Sobrien    return x;
93078556Sobrien}
93178556Sobrien
93278556Sobrien/*
93378556Sobrien * Same for off_t, with optional skmgpP suffix
93478556Sobrien */
93578556Sobrienstatic off_t
93678556Sobrienargtooff(const char *arg, const char *msg)
93778556Sobrien{
93878556Sobrien    char *s;
93978556Sobrien    off_t x;
94078556Sobrien
94178556Sobrien    errno = 0;
94278556Sobrien    x = strtoll(arg, &s, 0);
94378556Sobrien    /* allow at most one extra char */
94478556Sobrien    if (errno || x < 0 || (s[0] && s[1]) )
94578556Sobrien	errx(1, "%s: bad %s", arg, msg);
94678556Sobrien    if (*s) {	/* the extra char is the multiplier */
94778556Sobrien	switch (*s) {
94878556Sobrien	default:
94978556Sobrien	    errx(1, "%s: bad %s", arg, msg);
95078556Sobrien	    /* notreached */
95178556Sobrien
95278556Sobrien	case 's':		/* sector */
95378556Sobrien	case 'S':
95478556Sobrien	    x <<= 9;		/* times 512 */
95578556Sobrien	    break;
95678556Sobrien
95778556Sobrien	case 'k':		/* kilobyte */
95878556Sobrien	case 'K':
95978556Sobrien	    x <<= 10;		/* times 1024 */
96078556Sobrien	    break;
96178556Sobrien
96278556Sobrien	case 'm':		/* megabyte */
96378556Sobrien	case 'M':
96478556Sobrien	    x <<= 20;		/* times 1024*1024 */
96578556Sobrien	    break;
96678556Sobrien
96778556Sobrien	case 'g':		/* gigabyte */
96878556Sobrien	case 'G':
96978556Sobrien	    x <<= 30;		/* times 1024*1024*1024 */
97078556Sobrien	    break;
97178556Sobrien
97278556Sobrien	case 'p':		/* partition start */
97378556Sobrien	case 'P':
97478556Sobrien	case 'l':		/* partition length */
97578556Sobrien	case 'L':
97678556Sobrien	    errx(1, "%s: not supported yet %s", arg, msg);
97778556Sobrien	    /* notreached */
97878556Sobrien	}
97978556Sobrien    }
98078556Sobrien    return x;
98178556Sobrien}
98278556Sobrien
98378556Sobrien/*
98478556Sobrien * Check a volume label.
98578556Sobrien */
98678556Sobrienstatic int
98778556Sobrienoklabel(const char *src)
98878556Sobrien{
98978556Sobrien    int c, i;
99078556Sobrien
99178556Sobrien    for (i = 0; i <= 11; i++) {
99278556Sobrien	c = (u_char)*src++;
99378556Sobrien	if (c < ' ' + !i || strchr("\"*+,./:;<=>?[\\]|", c))
99478556Sobrien	    break;
99578556Sobrien    }
99678556Sobrien    return i && !c;
99778556Sobrien}
99878556Sobrien
99978556Sobrien/*
100078556Sobrien * Make a volume label.
100178556Sobrien */
100278556Sobrienstatic void
100378556Sobrienmklabel(u_int8_t *dest, const char *src)
100478556Sobrien{
100578556Sobrien    int c, i;
100678556Sobrien
100778556Sobrien    for (i = 0; i < 11; i++) {
100878556Sobrien	c = *src ? toupper(*src++) : ' ';
100978556Sobrien	*dest++ = !i && c == '\xe5' ? 5 : c;
101078556Sobrien    }
101178556Sobrien}
101278556Sobrien
101378556Sobrien/*
101478556Sobrien * Copy string, padding with spaces.
101578556Sobrien */
101678556Sobrienstatic void
101778556Sobriensetstr(u_int8_t *dest, const char *src, size_t len)
101878556Sobrien{
101978556Sobrien    while (len--)
102078556Sobrien	*dest++ = *src ? *src++ : ' ';
102178556Sobrien}
102278556Sobrien
102378556Sobrien/*
102478556Sobrien * Print usage message.
102578556Sobrien */
102678556Sobrienstatic void
102778556Sobrienusage(void)
102878556Sobrien{
102978556Sobrien	fprintf(stderr,
103078556Sobrien	    "usage: newfs_msdos [ -options ] special [disktype]\n"
103178556Sobrien	    "where the options are:\n"
103278556Sobrien	    "\t-@ create file system at specified offset\n"
103378556Sobrien	    "\t-B get bootstrap from file\n"
103478556Sobrien	    "\t-C create image file with specified size\n"
103578556Sobrien	    "\t-F FAT type (12, 16, or 32)\n"
103678556Sobrien	    "\t-I volume ID\n"
103778556Sobrien	    "\t-L volume label\n"
103878556Sobrien	    "\t-N don't create file system: just print out parameters\n"
103978556Sobrien	    "\t-O OEM string\n"
104078556Sobrien	    "\t-S bytes/sector\n"
104178556Sobrien	    "\t-a sectors/FAT\n"
104278556Sobrien	    "\t-b block size\n"
104378556Sobrien	    "\t-c sectors/cluster\n"
104478556Sobrien	    "\t-e root directory entries\n"
104578556Sobrien	    "\t-f standard format\n"
104678556Sobrien	    "\t-h drive heads\n"
104778556Sobrien	    "\t-i file system info sector\n"
104878556Sobrien	    "\t-k backup boot sector\n"
104978556Sobrien	    "\t-m media descriptor\n"
105078556Sobrien	    "\t-n number of FATs\n"
105178556Sobrien	    "\t-o hidden sectors\n"
105278556Sobrien	    "\t-r reserved sectors\n"
105378556Sobrien	    "\t-s file system size (sectors)\n"
105478556Sobrien	    "\t-u sectors/track\n");
105578556Sobrien	exit(1);
105678556Sobrien}
105778556Sobrien
105878556Sobrienstatic void
105978556Sobrieninfohandler(int sig __unused)
106078556Sobrien{
106178556Sobrien
106278556Sobrien	got_siginfo = 1;
106378556Sobrien}
106478556Sobrien