xref: /netbsd-src/usr.sbin/makefs/makefs.c (revision dc0564c197aed1acb883e38062870256bcb51f77)
1*dc0564c1Schristos /*	$NetBSD: makefs.c,v 1.59 2024/10/27 18:35:52 christos Exp $	*/
2de8b3ad2Slukem 
3de8b3ad2Slukem /*
4e5f38473Slukem  * Copyright (c) 2001-2003 Wasabi Systems, Inc.
5de8b3ad2Slukem  * All rights reserved.
6de8b3ad2Slukem  *
7de8b3ad2Slukem  * Written by Luke Mewburn for Wasabi Systems, Inc.
8de8b3ad2Slukem  *
9de8b3ad2Slukem  * Redistribution and use in source and binary forms, with or without
10de8b3ad2Slukem  * modification, are permitted provided that the following conditions
11de8b3ad2Slukem  * are met:
12de8b3ad2Slukem  * 1. Redistributions of source code must retain the above copyright
13de8b3ad2Slukem  *    notice, this list of conditions and the following disclaimer.
14de8b3ad2Slukem  * 2. Redistributions in binary form must reproduce the above copyright
15de8b3ad2Slukem  *    notice, this list of conditions and the following disclaimer in the
16de8b3ad2Slukem  *    documentation and/or other materials provided with the distribution.
17de8b3ad2Slukem  * 3. All advertising materials mentioning features or use of this software
18de8b3ad2Slukem  *    must display the following acknowledgement:
19de8b3ad2Slukem  *      This product includes software developed for the NetBSD Project by
20de8b3ad2Slukem  *      Wasabi Systems, Inc.
21de8b3ad2Slukem  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22de8b3ad2Slukem  *    or promote products derived from this software without specific prior
23de8b3ad2Slukem  *    written permission.
24de8b3ad2Slukem  *
25de8b3ad2Slukem  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26de8b3ad2Slukem  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27de8b3ad2Slukem  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28de8b3ad2Slukem  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29de8b3ad2Slukem  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30de8b3ad2Slukem  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31de8b3ad2Slukem  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32de8b3ad2Slukem  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33de8b3ad2Slukem  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34de8b3ad2Slukem  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35de8b3ad2Slukem  * POSSIBILITY OF SUCH DAMAGE.
36de8b3ad2Slukem  */
37de8b3ad2Slukem 
38b2f78261Sjmc #if HAVE_NBTOOL_CONFIG_H
39b2f78261Sjmc #include "nbtool_config.h"
40b2f78261Sjmc #endif
41b2f78261Sjmc 
42cafb53fcSlukem #include <sys/cdefs.h>
439fbd8888Stv #if defined(__RCSID) && !defined(__lint)
44*dc0564c1Schristos __RCSID("$NetBSD: makefs.c,v 1.59 2024/10/27 18:35:52 christos Exp $");
45cafb53fcSlukem #endif	/* !__lint */
46cafb53fcSlukem 
47de8b3ad2Slukem #include <assert.h>
48de8b3ad2Slukem #include <ctype.h>
49de8b3ad2Slukem #include <errno.h>
50c71f9f27Ssimonb #include <limits.h>
51de8b3ad2Slukem #include <stdio.h>
52de8b3ad2Slukem #include <stdlib.h>
53de8b3ad2Slukem #include <string.h>
54de8b3ad2Slukem #include <unistd.h>
553d364f54Schristos #include <stdbool.h>
56e4989541Schristos #include <util.h>
57de8b3ad2Slukem 
58de8b3ad2Slukem #include "makefs.h"
595781235dSlukem #include "mtree.h"
603550dc98Sfvdl #include "cd9660.h"
61de8b3ad2Slukem 
62de8b3ad2Slukem /*
63de8b3ad2Slukem  * list of supported file systems and dispatch functions
64de8b3ad2Slukem  */
65de8b3ad2Slukem typedef struct {
66de8b3ad2Slukem 	const char	*type;
672406596eSjmc 	void		(*prepare_options)(fsinfo_t *);
68de8b3ad2Slukem 	int		(*parse_options)(const char *, fsinfo_t *);
692406596eSjmc 	void		(*cleanup_options)(fsinfo_t *);
70de8b3ad2Slukem 	void		(*make_fs)(const char *, const char *, fsnode *,
71de8b3ad2Slukem 				fsinfo_t *);
72de8b3ad2Slukem } fstype_t;
73de8b3ad2Slukem 
74de8b3ad2Slukem static fstype_t fstypes[] = {
753d364f54Schristos #define ENTRY(name) { \
763d364f54Schristos 	# name, name ## _prep_opts, name ## _parse_opts, \
773d364f54Schristos 	name ## _cleanup_opts, name ## _makefs  \
783d364f54Schristos }
793d364f54Schristos 	ENTRY(ffs),
803d364f54Schristos 	ENTRY(cd9660),
813d364f54Schristos 	ENTRY(chfs),
823d364f54Schristos 	ENTRY(v7fs),
833d364f54Schristos 	ENTRY(msdos),
84e2036ad8Sreinoud 	ENTRY(udf),
85f6a7372bSchristos 	{ .type = NULL	},
86de8b3ad2Slukem };
87de8b3ad2Slukem 
88b2f78261Sjmc u_int		debug;
89de8b3ad2Slukem struct timespec	start_time;
90799916c0Schristos struct stat stampst;
91de8b3ad2Slukem 
92de8b3ad2Slukem static fstype_t *get_fstype(const char *);
93799916c0Schristos static int get_tstamp(const char *, struct stat *);
94e4989541Schristos static void usage(fstype_t *, fsinfo_t *) __dead;
95*dc0564c1Schristos static u_int parse_debug(char *);
96de8b3ad2Slukem 
97de8b3ad2Slukem int
98de8b3ad2Slukem main(int argc, char *argv[])
99de8b3ad2Slukem {
100de8b3ad2Slukem 	struct timeval	 start;
101de8b3ad2Slukem 	fstype_t	*fstype;
102de8b3ad2Slukem 	fsinfo_t	 fsoptions;
103de8b3ad2Slukem 	fsnode		*root;
104855b7dceSchristos 	int	 	 ch, i;
105855b7dceSchristos 	size_t		 len;
106de8b3ad2Slukem 	char		*specfile;
107de8b3ad2Slukem 
108bf7e1203Slukem 	setprogname(argv[0]);
109bf7e1203Slukem 
110de8b3ad2Slukem 	debug = 0;
111de8b3ad2Slukem 	if ((fstype = get_fstype(DEFAULT_FSTYPE)) == NULL)
112ec5d1d82Stsutsui 		errx(EXIT_FAILURE,
113ec5d1d82Stsutsui 		    "Unknown default fs type `%s'.", DEFAULT_FSTYPE);
114f4821030Slukem 
115f4821030Slukem 		/* set default fsoptions */
116de8b3ad2Slukem 	(void)memset(&fsoptions, 0, sizeof(fsoptions));
117de8b3ad2Slukem 	fsoptions.fd = -1;
118f4821030Slukem 	fsoptions.sectorsize = -1;
1192406596eSjmc 
1202406596eSjmc 	if (fstype->prepare_options)
1212406596eSjmc 		fstype->prepare_options(&fsoptions);
122f4821030Slukem 
123de8b3ad2Slukem 	specfile = NULL;
124799916c0Schristos #ifdef CLOCK_REALTIME
125799916c0Schristos 	ch = clock_gettime(CLOCK_REALTIME, &start_time);
126799916c0Schristos #else
127799916c0Schristos 	ch = gettimeofday(&start, NULL);
1289fbd8888Stv 	start_time.tv_sec = start.tv_sec;
1299fbd8888Stv 	start_time.tv_nsec = start.tv_usec * 1000;
130799916c0Schristos #endif
131799916c0Schristos 	if (ch == -1)
132ec5d1d82Stsutsui 		err(EXIT_FAILURE, "Unable to get system time");
133de8b3ad2Slukem 
134799916c0Schristos 
135ba42c78dSsimonb 	while ((ch = getopt(argc, argv, "B:b:d:f:F:LM:m:N:O:o:rs:S:t:T:xZ")) != -1) {
136de8b3ad2Slukem 		switch (ch) {
137de8b3ad2Slukem 
138de8b3ad2Slukem 		case 'B':
139de8b3ad2Slukem 			if (strcmp(optarg, "be") == 0 ||
140e5f38473Slukem 			    strcmp(optarg, "4321") == 0 ||
141de8b3ad2Slukem 			    strcmp(optarg, "big") == 0) {
142de8b3ad2Slukem #if BYTE_ORDER == LITTLE_ENDIAN
143de8b3ad2Slukem 				fsoptions.needswap = 1;
144de8b3ad2Slukem #endif
145de8b3ad2Slukem 			} else if (strcmp(optarg, "le") == 0 ||
146e5f38473Slukem 			    strcmp(optarg, "1234") == 0 ||
147de8b3ad2Slukem 			    strcmp(optarg, "little") == 0) {
148de8b3ad2Slukem #if BYTE_ORDER == BIG_ENDIAN
149de8b3ad2Slukem 				fsoptions.needswap = 1;
150de8b3ad2Slukem #endif
151de8b3ad2Slukem 			} else {
152de8b3ad2Slukem 				warnx("Invalid endian `%s'.", optarg);
153e4989541Schristos 				usage(fstype, &fsoptions);
154de8b3ad2Slukem 			}
155de8b3ad2Slukem 			break;
156de8b3ad2Slukem 
157de8b3ad2Slukem 		case 'b':
158de8b3ad2Slukem 			len = strlen(optarg) - 1;
159de8b3ad2Slukem 			if (optarg[len] == '%') {
160de8b3ad2Slukem 				optarg[len] = '\0';
161855b7dceSchristos 				fsoptions.freeblockpc = (int)
162d0824326Slukem 				    strsuftoll("free block percentage",
163ae62d656Slukem 					optarg, 0, 99);
164de8b3ad2Slukem 			} else {
165ae62d656Slukem 				fsoptions.freeblocks =
166d0824326Slukem 				    strsuftoll("free blocks",
167de8b3ad2Slukem 					optarg, 0, LLONG_MAX);
168de8b3ad2Slukem 			}
169de8b3ad2Slukem 			break;
170de8b3ad2Slukem 
171de8b3ad2Slukem 		case 'd':
172*dc0564c1Schristos 			debug = parse_debug(optarg);
173de8b3ad2Slukem 			break;
174de8b3ad2Slukem 
175de8b3ad2Slukem 		case 'f':
176de8b3ad2Slukem 			len = strlen(optarg) - 1;
177de8b3ad2Slukem 			if (optarg[len] == '%') {
178de8b3ad2Slukem 				optarg[len] = '\0';
179855b7dceSchristos 				fsoptions.freefilepc = (int)
180d0824326Slukem 				    strsuftoll("free file percentage",
181ae62d656Slukem 					optarg, 0, 99);
182de8b3ad2Slukem 			} else {
183ae62d656Slukem 				fsoptions.freefiles =
184d0824326Slukem 				    strsuftoll("free files",
185de8b3ad2Slukem 					optarg, 0, LLONG_MAX);
186de8b3ad2Slukem 			}
187de8b3ad2Slukem 			break;
188de8b3ad2Slukem 
189de8b3ad2Slukem 		case 'F':
190de8b3ad2Slukem 			specfile = optarg;
191de8b3ad2Slukem 			break;
192de8b3ad2Slukem 
193ba42c78dSsimonb 		case 'L':
194ba42c78dSsimonb 			fsoptions.follow = true;
195ba42c78dSsimonb 			break;
196ba42c78dSsimonb 
197de8b3ad2Slukem 		case 'M':
198ae62d656Slukem 			fsoptions.minsize =
199d0824326Slukem 			    strsuftoll("minimum size", optarg, 1LL, LLONG_MAX);
200de8b3ad2Slukem 			break;
201de8b3ad2Slukem 
2025781235dSlukem 		case 'N':
2035781235dSlukem 			if (! setup_getid(optarg))
204ec5d1d82Stsutsui 				errx(EXIT_FAILURE,
2055781235dSlukem 			    "Unable to use user and group databases in `%s'",
2065781235dSlukem 				    optarg);
2075781235dSlukem 			break;
2085781235dSlukem 
209de8b3ad2Slukem 		case 'm':
210ae62d656Slukem 			fsoptions.maxsize =
211d0824326Slukem 			    strsuftoll("maximum size", optarg, 1LL, LLONG_MAX);
212de8b3ad2Slukem 			break;
213de8b3ad2Slukem 
2142d692a95Schristos 		case 'O':
2152d692a95Schristos 			fsoptions.offset =
2162d692a95Schristos 			    strsuftoll("offset", optarg, 0LL, LLONG_MAX);
2172d692a95Schristos 			break;
2182d692a95Schristos 
219de8b3ad2Slukem 		case 'o':
220de8b3ad2Slukem 		{
221de8b3ad2Slukem 			char *p;
222de8b3ad2Slukem 
223de8b3ad2Slukem 			while ((p = strsep(&optarg, ",")) != NULL) {
224de8b3ad2Slukem 				if (*p == '\0')
225ec5d1d82Stsutsui 					errx(EXIT_FAILURE, "Empty option");
226de8b3ad2Slukem 				if (! fstype->parse_options(p, &fsoptions))
227e4989541Schristos 					usage(fstype, &fsoptions);
228de8b3ad2Slukem 			}
229de8b3ad2Slukem 			break;
230de8b3ad2Slukem 		}
231de8b3ad2Slukem 
232c06c93b2Schristos 		case 'r':
233c06c93b2Schristos 			fsoptions.replace = 1;
234c06c93b2Schristos 			break;
235c06c93b2Schristos 
236de8b3ad2Slukem 		case 's':
237de8b3ad2Slukem 			fsoptions.minsize = fsoptions.maxsize =
238d0824326Slukem 			    strsuftoll("size", optarg, 1LL, LLONG_MAX);
239de8b3ad2Slukem 			break;
240de8b3ad2Slukem 
241de8b3ad2Slukem 		case 'S':
242ae62d656Slukem 			fsoptions.sectorsize =
243d0824326Slukem 			    (int)strsuftoll("sector size", optarg,
244ae62d656Slukem 				1LL, INT_MAX);
245de8b3ad2Slukem 			break;
246de8b3ad2Slukem 
247de8b3ad2Slukem 		case 't':
2482406596eSjmc 			/* Check current one and cleanup if necessary. */
2492406596eSjmc 			if (fstype->cleanup_options)
2502406596eSjmc 				fstype->cleanup_options(&fsoptions);
2512406596eSjmc 			fsoptions.fs_specific = NULL;
252de8b3ad2Slukem 			if ((fstype = get_fstype(optarg)) == NULL)
253ec5d1d82Stsutsui 				errx(EXIT_FAILURE,
254ec5d1d82Stsutsui 				    "Unknown fs type `%s'.", optarg);
2552406596eSjmc 			fstype->prepare_options(&fsoptions);
256de8b3ad2Slukem 			break;
257de8b3ad2Slukem 
258799916c0Schristos 		case 'T':
259799916c0Schristos 			if (get_tstamp(optarg, &stampst) == -1)
260ec5d1d82Stsutsui 				errx(EXIT_FAILURE,
261ec5d1d82Stsutsui 				    "Cannot get timestamp from `%s'", optarg);
262799916c0Schristos 			break;
263799916c0Schristos 
2641bcb9d76Sthorpej 		case 'x':
2650fe82aa4Schristos 			fsoptions.onlyspec++;
2661bcb9d76Sthorpej 			break;
2671bcb9d76Sthorpej 
26807f6254fSsjg 		case 'Z':
26907f6254fSsjg 			fsoptions.sparse = 1;
27007f6254fSsjg 			break;
27107f6254fSsjg 
272de8b3ad2Slukem 		case '?':
273de8b3ad2Slukem 		default:
274e4989541Schristos 			usage(fstype, &fsoptions);
275de8b3ad2Slukem 			/* NOTREACHED */
276de8b3ad2Slukem 
277de8b3ad2Slukem 		}
278de8b3ad2Slukem 	}
279de8b3ad2Slukem 	if (debug) {
280de8b3ad2Slukem 		printf("debug mask: 0x%08x\n", debug);
281de8b3ad2Slukem 		printf("start time: %ld.%ld, %s",
282ae963680Slukem 		    (long)start_time.tv_sec, (long)start_time.tv_nsec,
283de8b3ad2Slukem 		    ctime(&start_time.tv_sec));
284de8b3ad2Slukem 	}
285de8b3ad2Slukem 	argc -= optind;
286de8b3ad2Slukem 	argv += optind;
287de8b3ad2Slukem 
288f1cc0951Schristos 	if (argc < 2)
289e4989541Schristos 		usage(fstype, &fsoptions);
290de8b3ad2Slukem 
2911bcb9d76Sthorpej 	/* -x must be accompanied by -F */
2926ddeaceaSlukem 	if (fsoptions.onlyspec != 0 && specfile == NULL)
293ec5d1d82Stsutsui 		errx(EXIT_FAILURE, "-x requires -F mtree-specfile.");
2941bcb9d76Sthorpej 
295de8b3ad2Slukem 				/* walk the tree */
296de8b3ad2Slukem 	TIMER_START(start);
297ba42c78dSsimonb 	root = walk_dir(argv[1], ".", NULL, NULL, fsoptions.replace,
298ba42c78dSsimonb 	    fsoptions.follow);
299de8b3ad2Slukem 	TIMER_RESULTS(start, "walk_dir");
300de8b3ad2Slukem 
301f1cc0951Schristos 	/* append extra directory */
302f1cc0951Schristos 	for (i = 2; i < argc; i++) {
303f1cc0951Schristos 		struct stat sb;
304f1cc0951Schristos 		if (stat(argv[i], &sb) == -1)
305ec5d1d82Stsutsui 			err(EXIT_FAILURE, "Can't stat `%s'", argv[i]);
306f1cc0951Schristos 		if (!S_ISDIR(sb.st_mode))
307ec5d1d82Stsutsui 			errx(EXIT_FAILURE, "%s: not a directory", argv[i]);
308f1cc0951Schristos 		TIMER_START(start);
309ba42c78dSsimonb 		root = walk_dir(argv[i], ".", NULL, root, fsoptions.replace,
310ba42c78dSsimonb 		    fsoptions.follow);
311f1cc0951Schristos 		TIMER_RESULTS(start, "walk_dir2");
312f1cc0951Schristos 	}
313f1cc0951Schristos 
314de8b3ad2Slukem 	if (specfile) {		/* apply a specfile */
315de8b3ad2Slukem 		TIMER_START(start);
316d0c4ff45Sdbj 		apply_specfile(specfile, argv[1], root, fsoptions.onlyspec);
317de8b3ad2Slukem 		TIMER_RESULTS(start, "apply_specfile");
318de8b3ad2Slukem 	}
319de8b3ad2Slukem 
320de8b3ad2Slukem 	if (debug & DEBUG_DUMP_FSNODES) {
321aa99e59fSlukem 		printf("\nparent: %s\n", argv[1]);
322f1cc0951Schristos 		dump_fsnodes(root);
323de8b3ad2Slukem 		putchar('\n');
324de8b3ad2Slukem 	}
325de8b3ad2Slukem 
326de8b3ad2Slukem 				/* build the file system */
327de8b3ad2Slukem 	TIMER_START(start);
328de8b3ad2Slukem 	fstype->make_fs(argv[0], argv[1], root, &fsoptions);
329de8b3ad2Slukem 	TIMER_RESULTS(start, "make_fs");
330de8b3ad2Slukem 
3310e392af9Sdbj 	free_fsnodes(root);
3320e392af9Sdbj 
333ec5d1d82Stsutsui 	exit(EXIT_SUCCESS);
334de8b3ad2Slukem 	/* NOTREACHED */
335de8b3ad2Slukem }
336de8b3ad2Slukem 
3371c35cd38Schristos int
33850d02345Schristos set_option(const option_t *options, const char *option, char *buf, size_t len)
3391c35cd38Schristos {
3401c35cd38Schristos 	char *var, *val;
3411c35cd38Schristos 	int retval;
3421c35cd38Schristos 
3431c35cd38Schristos 	assert(option != NULL);
3441c35cd38Schristos 
345e4989541Schristos 	var = estrdup(option);
34636e64830Schristos 	for (val = var; *val; val++)
34736e64830Schristos 		if (*val == '=') {
3481c35cd38Schristos 			*val++ = '\0';
34936e64830Schristos 			break;
35036e64830Schristos 		}
35150d02345Schristos 	retval = set_option_var(options, var, val, buf, len);
3521c35cd38Schristos 	free(var);
3531c35cd38Schristos 	return retval;
3541c35cd38Schristos }
355de8b3ad2Slukem 
356855b7dceSchristos void
357855b7dceSchristos print_options(FILE *fp, const option_t *options)
358855b7dceSchristos {
359855b7dceSchristos 	for (size_t i = 0; options[i].name != NULL; i++) {
360855b7dceSchristos 		fprintf(fp, "%s=", options[i].name);
361855b7dceSchristos 		switch (options[i].type) {
362855b7dceSchristos 		case OPT_BOOL:
363855b7dceSchristos 			fputs(*(bool *)options[i].value ? "true\n" : "false\n",
364855b7dceSchristos 			    fp);
365855b7dceSchristos 			break;
366855b7dceSchristos 		case OPT_STRARRAY:
367855b7dceSchristos 		case OPT_STRPTR:
368855b7dceSchristos 		case OPT_STRBUF:
369855b7dceSchristos 			fprintf(fp, "%s\n", *(const char **)options[i].value);
370855b7dceSchristos 			break;
371855b7dceSchristos 		case OPT_INT64:
372855b7dceSchristos 			fprintf(fp, "%" PRIu64 "\n",
373855b7dceSchristos 			    *(uint64_t *)options[i].value);
374855b7dceSchristos 			break;
375855b7dceSchristos 		case OPT_INT32:
376855b7dceSchristos 			fprintf(fp, "%" PRIu32 "\n",
377855b7dceSchristos 			    *(uint32_t *)options[i].value);
378855b7dceSchristos 			break;
379855b7dceSchristos 		case OPT_INT16:
380855b7dceSchristos 			fprintf(fp, "%" PRIu16 "\n",
381855b7dceSchristos 			    *(uint16_t *)options[i].value);
382855b7dceSchristos 			break;
383855b7dceSchristos 		case OPT_INT8:
384855b7dceSchristos 			fprintf(fp, "%" PRIu8 "\n",
385855b7dceSchristos 			    *(uint8_t *)options[i].value);
386855b7dceSchristos 			break;
387855b7dceSchristos 		default:
388855b7dceSchristos 			warnx("Unknown type %d in option %s", options[i].type,
389855b7dceSchristos 			    options[i].name);
390855b7dceSchristos 			return;
391855b7dceSchristos 		}
392855b7dceSchristos 	}
393855b7dceSchristos }
394855b7dceSchristos 
395de8b3ad2Slukem int
39650d02345Schristos set_option_var(const option_t *options, const char *var, const char *val,
39750d02345Schristos     char *buf, size_t len)
398de8b3ad2Slukem {
3993d364f54Schristos 	char *s;
4003d364f54Schristos 	size_t i;
4013d364f54Schristos 
4027a9c8c65Schristos #define NUM(type) \
4039eb809c6Schristos 	if (!*val) { \
4047a9c8c65Schristos 		*(type *)options[i].value = 1; \
40536e64830Schristos 		break; \
40636e64830Schristos 	} \
4077a9c8c65Schristos 	*(type *)options[i].value = (type)strsuftoll(options[i].desc, val, \
4083d364f54Schristos 	    options[i].minimum, options[i].maximum); break
409de8b3ad2Slukem 
410de8b3ad2Slukem 	for (i = 0; options[i].name != NULL; i++) {
4117176d59dSchristos 		if (var[1] == '\0') {
4127176d59dSchristos 			if (options[i].letter != var[0])
413de8b3ad2Slukem 				continue;
4147176d59dSchristos 		} else if (strcmp(options[i].name, var) != 0)
4153d364f54Schristos 			continue;
4163d364f54Schristos 		switch (options[i].type) {
4173d364f54Schristos 		case OPT_BOOL:
4183d364f54Schristos 			*(bool *)options[i].value = 1;
4193d364f54Schristos 			break;
4203d364f54Schristos 		case OPT_STRARRAY:
4213d364f54Schristos 			strlcpy((void *)options[i].value, val, (size_t)
4223d364f54Schristos 			    options[i].maximum);
4233d364f54Schristos 			break;
4243d364f54Schristos 		case OPT_STRPTR:
425e4989541Schristos 			s = estrdup(val);
4263d364f54Schristos 			*(char **)options[i].value = s;
4273d364f54Schristos 			break;
42850d02345Schristos 		case OPT_STRBUF:
42950d02345Schristos 			if (buf == NULL)
43050d02345Schristos 				abort();
43150d02345Schristos 			strlcpy(buf, val, len);
43250d02345Schristos 			break;
4333d364f54Schristos 		case OPT_INT64:
4347a9c8c65Schristos 			NUM(uint64_t);
4353d364f54Schristos 		case OPT_INT32:
4367a9c8c65Schristos 			NUM(uint32_t);
4373d364f54Schristos 		case OPT_INT16:
4387a9c8c65Schristos 			NUM(uint16_t);
4393d364f54Schristos 		case OPT_INT8:
4407a9c8c65Schristos 			NUM(uint8_t);
4413d364f54Schristos 		default:
4423d364f54Schristos 			warnx("Unknown type %d in option %s", options[i].type,
4433d364f54Schristos 			    val);
4443d364f54Schristos 			return 0;
4453d364f54Schristos 		}
446855b7dceSchristos 		return (int)i;
447de8b3ad2Slukem 	}
448de8b3ad2Slukem 	warnx("Unknown option `%s'", var);
449562664d1Schristos 	return -1;
450de8b3ad2Slukem }
451de8b3ad2Slukem 
452de8b3ad2Slukem 
453de8b3ad2Slukem static fstype_t *
454de8b3ad2Slukem get_fstype(const char *type)
455de8b3ad2Slukem {
456de8b3ad2Slukem 	int i;
457de8b3ad2Slukem 
458de8b3ad2Slukem 	for (i = 0; fstypes[i].type != NULL; i++)
459de8b3ad2Slukem 		if (strcmp(fstypes[i].type, type) == 0)
460de8b3ad2Slukem 			return (&fstypes[i]);
461de8b3ad2Slukem 	return (NULL);
462de8b3ad2Slukem }
463de8b3ad2Slukem 
464e4989541Schristos option_t *
465e4989541Schristos copy_opts(const option_t *o)
466e4989541Schristos {
467e4989541Schristos 	size_t i;
468e4989541Schristos 	for (i = 0; o[i].name; i++)
469e4989541Schristos 		continue;
470e4989541Schristos 	i++;
471e4989541Schristos 	return memcpy(ecalloc(i, sizeof(*o)), o, i * sizeof(*o));
472e4989541Schristos }
473e4989541Schristos 
474799916c0Schristos static int
475799916c0Schristos get_tstamp(const char *b, struct stat *st)
476799916c0Schristos {
477799916c0Schristos 	time_t when;
478799916c0Schristos 	char *eb;
479799916c0Schristos 	long long l;
480799916c0Schristos 
481799916c0Schristos 	if (stat(b, st) != -1)
482799916c0Schristos 		return 0;
483799916c0Schristos 
484799916c0Schristos #ifndef HAVE_NBTOOL_CONFIG_H
485799916c0Schristos 	errno = 0;
486d0bd9a5fSjoerg 	if ((when = parsedate(b, NULL, NULL)) == -1 && errno != 0)
487799916c0Schristos #endif
488799916c0Schristos 	{
489799916c0Schristos 		errno = 0;
490799916c0Schristos 		l = strtoll(b, &eb, 0);
491799916c0Schristos 		if (b == eb || *eb || errno)
492799916c0Schristos 			return -1;
493799916c0Schristos 		when = (time_t)l;
494799916c0Schristos 	}
495799916c0Schristos 
496799916c0Schristos 	st->st_ino = 1;
497799916c0Schristos #if HAVE_STRUCT_STAT_BIRTHTIME
498799916c0Schristos 	st->st_birthtime =
499799916c0Schristos #endif
500799916c0Schristos 	st->st_mtime = st->st_ctime = st->st_atime = when;
501799916c0Schristos 	return 0;
502799916c0Schristos }
503799916c0Schristos 
504*dc0564c1Schristos static struct {
505*dc0564c1Schristos 	const char *n;
506*dc0564c1Schristos 	u_int v;
507*dc0564c1Schristos } nv[] = {
508*dc0564c1Schristos 	DEBUG_STRINGS
509*dc0564c1Schristos };
510*dc0564c1Schristos 
511de8b3ad2Slukem static void
512e4989541Schristos usage(fstype_t *fstype, fsinfo_t *fsoptions)
513de8b3ad2Slukem {
514de8b3ad2Slukem 	const char *prog;
515de8b3ad2Slukem 
516de8b3ad2Slukem 	prog = getprogname();
517de8b3ad2Slukem 	fprintf(stderr,
518*dc0564c1Schristos "Usage: %s [-rxZ] [-B endian] [-b free-blocks] [-d debug-mask|comma-separated-option]\n"
5192d692a95Schristos "\t[-F mtree-specfile] [-f free-files] [-M minimum-size] [-m maximum-size]\n"
5202d692a95Schristos "\t[-N userdb-dir] [-O offset] [-o fs-options] [-S sector-size]\n"
521516ffc06Swiz "\t[-s image-size] [-T <timestamp/file>] [-t fs-type]"
522799916c0Schristos " image-file directory [extra-directory ...]\n",
523de8b3ad2Slukem 	    prog);
524e4989541Schristos 
525*dc0564c1Schristos 	fprintf(stderr, "\nDebugging options:\n");
526*dc0564c1Schristos 	for (size_t i = 0; i < __arraycount(nv); i++)
527*dc0564c1Schristos 		fprintf(stderr, "\t0x%8.8x\t%s\n", nv[i].v, nv[i].n);
528*dc0564c1Schristos 
529e4989541Schristos 	if (fstype) {
530e4989541Schristos 		size_t i;
531e4989541Schristos 		option_t *o = fsoptions->fs_options;
532e4989541Schristos 
533e4989541Schristos 		fprintf(stderr, "\n%s specific options:\n", fstype->type);
534e4989541Schristos 		for (i = 0; o[i].name != NULL; i++)
5352246e723Schristos 			fprintf(stderr, "\t%c%c%20.20s\t%s\n",
5362246e723Schristos 			    o[i].letter ? o[i].letter : ' ',
5372246e723Schristos 			    o[i].letter ? ',' : ' ',
538e4989541Schristos 			    o[i].name, o[i].desc);
539e4989541Schristos 	}
540ec5d1d82Stsutsui 	exit(EXIT_FAILURE);
541de8b3ad2Slukem }
542*dc0564c1Schristos 
543*dc0564c1Schristos 
544*dc0564c1Schristos static u_int
545*dc0564c1Schristos parse_debug(char *str)
546*dc0564c1Schristos {
547*dc0564c1Schristos 	char *ep;
548*dc0564c1Schristos 	u_int d;
549*dc0564c1Schristos 	size_t i;
550*dc0564c1Schristos 
551*dc0564c1Schristos 	errno = 0;
552*dc0564c1Schristos 	d = (u_int)strtoul(str, &ep, 0);
553*dc0564c1Schristos 	if (str != ep && !*ep && errno == 0)
554*dc0564c1Schristos 		return d;
555*dc0564c1Schristos 	d = 0;
556*dc0564c1Schristos 	for (char *a = strtok(str, ","); a != NULL; a = strtok(NULL, ",")) {
557*dc0564c1Schristos 		for (i = 0; i < __arraycount(nv); i++)
558*dc0564c1Schristos 			if (strcmp(nv[i].n, a) == 0) {
559*dc0564c1Schristos 				d |= nv[i].v;
560*dc0564c1Schristos 				break;
561*dc0564c1Schristos 			}
562*dc0564c1Schristos 		if (i == __arraycount(nv))
563*dc0564c1Schristos 			errx(EXIT_FAILURE, "Unknown debug option `%s'", a);
564*dc0564c1Schristos 	}
565*dc0564c1Schristos 	return d;
566*dc0564c1Schristos }
567*dc0564c1Schristos 
568