1*d656534bSElliott Hughes /*-
2*d656534bSElliott Hughes * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*d656534bSElliott Hughes *
4*d656534bSElliott Hughes * Copyright (c) 1998 Robert Nordier
5*d656534bSElliott Hughes * All rights reserved.
6*d656534bSElliott Hughes *
7*d656534bSElliott Hughes * Redistribution and use in source and binary forms, with or without
8*d656534bSElliott Hughes * modification, are permitted provided that the following conditions
9*d656534bSElliott Hughes * are met:
10*d656534bSElliott Hughes * 1. Redistributions of source code must retain the above copyright
11*d656534bSElliott Hughes * notice, this list of conditions and the following disclaimer.
12*d656534bSElliott Hughes * 2. Redistributions in binary form must reproduce the above copyright
13*d656534bSElliott Hughes * notice, this list of conditions and the following disclaimer in
14*d656534bSElliott Hughes * the documentation and/or other materials provided with the
15*d656534bSElliott Hughes * distribution.
16*d656534bSElliott Hughes *
17*d656534bSElliott Hughes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
18*d656534bSElliott Hughes * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19*d656534bSElliott Hughes * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*d656534bSElliott Hughes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
21*d656534bSElliott Hughes * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22*d656534bSElliott Hughes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23*d656534bSElliott Hughes * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24*d656534bSElliott Hughes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25*d656534bSElliott Hughes * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26*d656534bSElliott Hughes * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27*d656534bSElliott Hughes * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*d656534bSElliott Hughes */
29*d656534bSElliott Hughes
30*d656534bSElliott Hughes #ifndef lint
31*d656534bSElliott Hughes static const char rcsid[] =
32*d656534bSElliott Hughes "$FreeBSD$";
33*d656534bSElliott Hughes #endif /* not lint */
34*d656534bSElliott Hughes
35*d656534bSElliott Hughes #include <sys/param.h>
36*d656534bSElliott Hughes #include <sys/stat.h>
37*d656534bSElliott Hughes #include <err.h>
38*d656534bSElliott Hughes #include <errno.h>
39*d656534bSElliott Hughes #include <paths.h>
40*d656534bSElliott Hughes #include <stdio.h>
41*d656534bSElliott Hughes #include <stdlib.h>
42*d656534bSElliott Hughes #include <string.h>
43*d656534bSElliott Hughes #include <unistd.h>
44*d656534bSElliott Hughes
45*d656534bSElliott Hughes #include "mkfs_msdos.h"
46*d656534bSElliott Hughes
47*d656534bSElliott Hughes #define argto1(arg, lo, msg) argtou(arg, lo, 0xff, msg)
48*d656534bSElliott Hughes #define argto2(arg, lo, msg) argtou(arg, lo, 0xffff, msg)
49*d656534bSElliott Hughes #define argto4(arg, lo, msg) argtou(arg, lo, 0xffffffff, msg)
50*d656534bSElliott Hughes #define argtox(arg, lo, msg) argtou(arg, lo, UINT_MAX, msg)
51*d656534bSElliott Hughes
52*d656534bSElliott Hughes static u_int argtou(const char *, u_int, u_int, const char *);
53*d656534bSElliott Hughes static off_t argtooff(const char *, const char *);
54*d656534bSElliott Hughes static void usage(void);
55*d656534bSElliott Hughes
56*d656534bSElliott Hughes static time_t
get_tstamp(const char * b)57*d656534bSElliott Hughes get_tstamp(const char *b)
58*d656534bSElliott Hughes {
59*d656534bSElliott Hughes struct stat st;
60*d656534bSElliott Hughes char *eb;
61*d656534bSElliott Hughes long long l;
62*d656534bSElliott Hughes
63*d656534bSElliott Hughes if (stat(b, &st) != -1)
64*d656534bSElliott Hughes return (time_t)st.st_mtime;
65*d656534bSElliott Hughes
66*d656534bSElliott Hughes errno = 0;
67*d656534bSElliott Hughes l = strtoll(b, &eb, 0);
68*d656534bSElliott Hughes if (b == eb || *eb || errno)
69*d656534bSElliott Hughes errx(EXIT_FAILURE, "Can't parse timestamp '%s'", b);
70*d656534bSElliott Hughes return (time_t)l;
71*d656534bSElliott Hughes }
72*d656534bSElliott Hughes
73*d656534bSElliott Hughes /*
74*d656534bSElliott Hughes * Construct a FAT12, FAT16, or FAT32 file system.
75*d656534bSElliott Hughes */
76*d656534bSElliott Hughes int
main(int argc,char * argv[])77*d656534bSElliott Hughes main(int argc, char *argv[])
78*d656534bSElliott Hughes {
79*d656534bSElliott Hughes static const char opts[] = "@:NAB:C:F:I:L:O:S:a:b:c:e:f:h:i:k:m:n:o:r:s:T:u:";
80*d656534bSElliott Hughes struct msdos_options o;
81*d656534bSElliott Hughes const char *fname, *dtype;
82*d656534bSElliott Hughes char buf[MAXPATHLEN];
83*d656534bSElliott Hughes int ch;
84*d656534bSElliott Hughes
85*d656534bSElliott Hughes memset(&o, 0, sizeof(o));
86*d656534bSElliott Hughes
87*d656534bSElliott Hughes while ((ch = getopt(argc, argv, opts)) != -1)
88*d656534bSElliott Hughes switch (ch) {
89*d656534bSElliott Hughes case '@':
90*d656534bSElliott Hughes o.offset = argtooff(optarg, "offset");
91*d656534bSElliott Hughes break;
92*d656534bSElliott Hughes case 'N':
93*d656534bSElliott Hughes o.no_create = 1;
94*d656534bSElliott Hughes break;
95*d656534bSElliott Hughes case 'A':
96*d656534bSElliott Hughes o.align = true;
97*d656534bSElliott Hughes break;
98*d656534bSElliott Hughes case 'B':
99*d656534bSElliott Hughes o.bootstrap = optarg;
100*d656534bSElliott Hughes break;
101*d656534bSElliott Hughes case 'C':
102*d656534bSElliott Hughes o.create_size = argtooff(optarg, "create size");
103*d656534bSElliott Hughes break;
104*d656534bSElliott Hughes case 'F':
105*d656534bSElliott Hughes if (strcmp(optarg, "12") &&
106*d656534bSElliott Hughes strcmp(optarg, "16") &&
107*d656534bSElliott Hughes strcmp(optarg, "32"))
108*d656534bSElliott Hughes errx(1, "%s: bad FAT type", optarg);
109*d656534bSElliott Hughes o.fat_type = atoi(optarg);
110*d656534bSElliott Hughes break;
111*d656534bSElliott Hughes case 'I':
112*d656534bSElliott Hughes o.volume_id = argto4(optarg, 0, "volume ID");
113*d656534bSElliott Hughes o.volume_id_set = 1;
114*d656534bSElliott Hughes break;
115*d656534bSElliott Hughes case 'L':
116*d656534bSElliott Hughes o.volume_label = optarg;
117*d656534bSElliott Hughes break;
118*d656534bSElliott Hughes case 'O':
119*d656534bSElliott Hughes o.OEM_string = optarg;
120*d656534bSElliott Hughes break;
121*d656534bSElliott Hughes case 'S':
122*d656534bSElliott Hughes o.bytes_per_sector = argto2(optarg, 1, "bytes/sector");
123*d656534bSElliott Hughes break;
124*d656534bSElliott Hughes case 'a':
125*d656534bSElliott Hughes o.sectors_per_fat = argto4(optarg, 1, "sectors/FAT");
126*d656534bSElliott Hughes break;
127*d656534bSElliott Hughes case 'b':
128*d656534bSElliott Hughes o.block_size = argtox(optarg, 1, "block size");
129*d656534bSElliott Hughes o.sectors_per_cluster = 0;
130*d656534bSElliott Hughes break;
131*d656534bSElliott Hughes case 'c':
132*d656534bSElliott Hughes o.sectors_per_cluster = argto1(optarg, 1, "sectors/cluster");
133*d656534bSElliott Hughes o.block_size = 0;
134*d656534bSElliott Hughes break;
135*d656534bSElliott Hughes case 'e':
136*d656534bSElliott Hughes o.directory_entries = argto2(optarg, 1, "directory entries");
137*d656534bSElliott Hughes break;
138*d656534bSElliott Hughes case 'f':
139*d656534bSElliott Hughes o.floppy = optarg;
140*d656534bSElliott Hughes break;
141*d656534bSElliott Hughes case 'h':
142*d656534bSElliott Hughes o.drive_heads = argto2(optarg, 1, "drive heads");
143*d656534bSElliott Hughes break;
144*d656534bSElliott Hughes case 'i':
145*d656534bSElliott Hughes o.info_sector = argto2(optarg, 1, "info sector");
146*d656534bSElliott Hughes break;
147*d656534bSElliott Hughes case 'k':
148*d656534bSElliott Hughes o.backup_sector = argto2(optarg, 1, "backup sector");
149*d656534bSElliott Hughes break;
150*d656534bSElliott Hughes case 'm':
151*d656534bSElliott Hughes o.media_descriptor = argto1(optarg, 0, "media descriptor");
152*d656534bSElliott Hughes o.media_descriptor_set = 1;
153*d656534bSElliott Hughes break;
154*d656534bSElliott Hughes case 'n':
155*d656534bSElliott Hughes o.num_FAT = argto1(optarg, 1, "number of FATs");
156*d656534bSElliott Hughes break;
157*d656534bSElliott Hughes case 'o':
158*d656534bSElliott Hughes o.hidden_sectors = argto4(optarg, 0, "hidden sectors");
159*d656534bSElliott Hughes o.hidden_sectors_set = 1;
160*d656534bSElliott Hughes break;
161*d656534bSElliott Hughes case 'r':
162*d656534bSElliott Hughes o.reserved_sectors = argto2(optarg, 1, "reserved sectors");
163*d656534bSElliott Hughes break;
164*d656534bSElliott Hughes case 's':
165*d656534bSElliott Hughes o.size = argto4(optarg, 1, "file system size");
166*d656534bSElliott Hughes break;
167*d656534bSElliott Hughes case 'T':
168*d656534bSElliott Hughes o.timestamp_set = 1;
169*d656534bSElliott Hughes o.timestamp = get_tstamp(optarg);
170*d656534bSElliott Hughes break;
171*d656534bSElliott Hughes case 'u':
172*d656534bSElliott Hughes o.sectors_per_track = argto2(optarg, 1, "sectors/track");
173*d656534bSElliott Hughes break;
174*d656534bSElliott Hughes default:
175*d656534bSElliott Hughes usage();
176*d656534bSElliott Hughes }
177*d656534bSElliott Hughes argc -= optind;
178*d656534bSElliott Hughes argv += optind;
179*d656534bSElliott Hughes if (argc < 1 || argc > 2)
180*d656534bSElliott Hughes usage();
181*d656534bSElliott Hughes if (o.align) {
182*d656534bSElliott Hughes if (o.reserved_sectors)
183*d656534bSElliott Hughes errx(1, "align (-A) is incompatible with -r");
184*d656534bSElliott Hughes }
185*d656534bSElliott Hughes fname = *argv++;
186*d656534bSElliott Hughes if (!o.create_size && !strchr(fname, '/')) {
187*d656534bSElliott Hughes snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
188*d656534bSElliott Hughes fname = buf;
189*d656534bSElliott Hughes }
190*d656534bSElliott Hughes dtype = *argv;
191*d656534bSElliott Hughes exit(!!mkfs_msdos(fname, dtype, &o));
192*d656534bSElliott Hughes }
193*d656534bSElliott Hughes
194*d656534bSElliott Hughes /*
195*d656534bSElliott Hughes * Convert and check a numeric option argument.
196*d656534bSElliott Hughes */
197*d656534bSElliott Hughes static u_int
argtou(const char * arg,u_int lo,u_int hi,const char * msg)198*d656534bSElliott Hughes argtou(const char *arg, u_int lo, u_int hi, const char *msg)
199*d656534bSElliott Hughes {
200*d656534bSElliott Hughes char *s;
201*d656534bSElliott Hughes u_long x;
202*d656534bSElliott Hughes
203*d656534bSElliott Hughes errno = 0;
204*d656534bSElliott Hughes x = strtoul(arg, &s, 0);
205*d656534bSElliott Hughes if (errno || !*arg || *s || x < lo || x > hi)
206*d656534bSElliott Hughes errx(1, "%s: bad %s", arg, msg);
207*d656534bSElliott Hughes return x;
208*d656534bSElliott Hughes }
209*d656534bSElliott Hughes
210*d656534bSElliott Hughes /*
211*d656534bSElliott Hughes * Same for off_t, with optional skmgpP suffix
212*d656534bSElliott Hughes */
213*d656534bSElliott Hughes static off_t
argtooff(const char * arg,const char * msg)214*d656534bSElliott Hughes argtooff(const char *arg, const char *msg)
215*d656534bSElliott Hughes {
216*d656534bSElliott Hughes char *s;
217*d656534bSElliott Hughes off_t x;
218*d656534bSElliott Hughes
219*d656534bSElliott Hughes errno = 0;
220*d656534bSElliott Hughes x = strtoll(arg, &s, 0);
221*d656534bSElliott Hughes /* allow at most one extra char */
222*d656534bSElliott Hughes if (errno || x < 0 || (s[0] && s[1]) )
223*d656534bSElliott Hughes errx(1, "%s: bad %s", arg, msg);
224*d656534bSElliott Hughes if (*s) { /* the extra char is the multiplier */
225*d656534bSElliott Hughes switch (*s) {
226*d656534bSElliott Hughes default:
227*d656534bSElliott Hughes errx(1, "%s: bad %s", arg, msg);
228*d656534bSElliott Hughes /* notreached */
229*d656534bSElliott Hughes
230*d656534bSElliott Hughes case 's': /* sector */
231*d656534bSElliott Hughes case 'S':
232*d656534bSElliott Hughes x <<= 9; /* times 512 */
233*d656534bSElliott Hughes break;
234*d656534bSElliott Hughes
235*d656534bSElliott Hughes case 'k': /* kilobyte */
236*d656534bSElliott Hughes case 'K':
237*d656534bSElliott Hughes x <<= 10; /* times 1024 */
238*d656534bSElliott Hughes break;
239*d656534bSElliott Hughes
240*d656534bSElliott Hughes case 'm': /* megabyte */
241*d656534bSElliott Hughes case 'M':
242*d656534bSElliott Hughes x <<= 20; /* times 1024*1024 */
243*d656534bSElliott Hughes break;
244*d656534bSElliott Hughes
245*d656534bSElliott Hughes case 'g': /* gigabyte */
246*d656534bSElliott Hughes case 'G':
247*d656534bSElliott Hughes x <<= 30; /* times 1024*1024*1024 */
248*d656534bSElliott Hughes break;
249*d656534bSElliott Hughes
250*d656534bSElliott Hughes case 'p': /* partition start */
251*d656534bSElliott Hughes case 'P':
252*d656534bSElliott Hughes case 'l': /* partition length */
253*d656534bSElliott Hughes case 'L':
254*d656534bSElliott Hughes errx(1, "%s: not supported yet %s", arg, msg);
255*d656534bSElliott Hughes /* notreached */
256*d656534bSElliott Hughes }
257*d656534bSElliott Hughes }
258*d656534bSElliott Hughes return x;
259*d656534bSElliott Hughes }
260*d656534bSElliott Hughes
261*d656534bSElliott Hughes /*
262*d656534bSElliott Hughes * Print usage message.
263*d656534bSElliott Hughes */
264*d656534bSElliott Hughes static void
usage(void)265*d656534bSElliott Hughes usage(void)
266*d656534bSElliott Hughes {
267*d656534bSElliott Hughes fprintf(stderr,
268*d656534bSElliott Hughes "usage: %s [ -options ] special [disktype]\n", getprogname());
269*d656534bSElliott Hughes fprintf(stderr, "where the options are:\n");
270*d656534bSElliott Hughes static struct {
271*d656534bSElliott Hughes char o;
272*d656534bSElliott Hughes const char *h;
273*d656534bSElliott Hughes } opts[] = {
274*d656534bSElliott Hughes #define AOPT(_opt, _type, _name, _min, _desc) { _opt, _desc },
275*d656534bSElliott Hughes ALLOPTS
276*d656534bSElliott Hughes #undef AOPT
277*d656534bSElliott Hughes };
278*d656534bSElliott Hughes for (size_t i = 0; i < nitems(opts); i++)
279*d656534bSElliott Hughes fprintf(stderr, "\t-%c %s\n", opts[i].o, opts[i].h);
280*d656534bSElliott Hughes exit(1);
281*d656534bSElliott Hughes }
282