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