main netmisc / makefs / makefs.c
  1/*	$NetBSD: makefs.c,v 1.59 2024/10/27 18:35:52 christos Exp $	*/
  2
  3/*
  4 * Copyright (c) 2001-2003 Wasabi Systems, Inc.
  5 * All rights reserved.
  6 *
  7 * Written by Luke Mewburn for Wasabi Systems, Inc.
  8 *
  9 * Redistribution and use in source and binary forms, with or without
 10 * modification, are permitted provided that the following conditions
 11 * are met:
 12 * 1. Redistributions of source code must retain the above copyright
 13 *    notice, this list of conditions and the following disclaimer.
 14 * 2. Redistributions in binary form must reproduce the above copyright
 15 *    notice, this list of conditions and the following disclaimer in the
 16 *    documentation and/or other materials provided with the distribution.
 17 * 3. All advertising materials mentioning features or use of this software
 18 *    must display the following acknowledgement:
 19 *      This product includes software developed for the NetBSD Project by
 20 *      Wasabi Systems, Inc.
 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
 22 *    or promote products derived from this software without specific prior
 23 *    written permission.
 24 *
 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 28 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 35 * POSSIBILITY OF SUCH DAMAGE.
 36 */
 37
 38#if HAVE_NBTOOL_CONFIG_H
 39#include "nbtool_config.h"
 40#endif
 41
 42#include <sys/cdefs.h>
 43#if defined(__RCSID) && !defined(__lint)
 44__RCSID("$NetBSD: makefs.c,v 1.59 2024/10/27 18:35:52 christos Exp $");
 45#endif	/* !__lint */
 46
 47#include <assert.h>
 48#include <ctype.h>
 49#include <errno.h>
 50#include <limits.h>
 51#include <stdio.h>
 52#include <stdlib.h>
 53#include <string.h>
 54#include <unistd.h>
 55#include <stdbool.h>
 56#include <util.h>
 57
 58#include "makefs.h"
 59#include "mtree.h"
 60#include "cd9660.h"
 61
 62/*
 63 * list of supported file systems and dispatch functions
 64 */
 65typedef struct {
 66	const char	*type;
 67	void		(*prepare_options)(fsinfo_t *);
 68	int		(*parse_options)(const char *, fsinfo_t *);
 69	void		(*cleanup_options)(fsinfo_t *);
 70	void		(*make_fs)(const char *, const char *, fsnode *,
 71				fsinfo_t *);
 72} fstype_t;
 73
 74static fstype_t fstypes[] = {
 75#define ENTRY(name) { \
 76	# name, name ## _prep_opts, name ## _parse_opts, \
 77	name ## _cleanup_opts, name ## _makefs  \
 78}
 79	ENTRY(cd9660),
 80	{ .type = NULL	},
 81};
 82
 83u_int		debug;
 84struct timespec	start_time;
 85struct stat stampst;
 86
 87static fstype_t *get_fstype(const char *);
 88static int get_tstamp(const char *, struct stat *);
 89static void usage(fstype_t *, fsinfo_t *) __dead;
 90static u_int parse_debug(char *);
 91
 92int
 93main(int argc, char *argv[])
 94{
 95	struct timeval	 start;
 96	fstype_t	*fstype;
 97	fsinfo_t	 fsoptions;
 98	fsnode		*root;
 99	int	 	 ch, i;
100	size_t		 len;
101	char		*specfile;
102
103	setprogname(argv[0]);
104
105	debug = 0;
106	if ((fstype = get_fstype(DEFAULT_FSTYPE)) == NULL)
107		errx(EXIT_FAILURE,
108		    "Unknown default fs type `%s'.", DEFAULT_FSTYPE);
109
110		/* set default fsoptions */
111	(void)memset(&fsoptions, 0, sizeof(fsoptions));
112	fsoptions.fd = -1;
113	fsoptions.sectorsize = -1;
114
115	if (fstype->prepare_options)
116		fstype->prepare_options(&fsoptions);
117
118	specfile = NULL;
119#ifdef CLOCK_REALTIME
120	ch = clock_gettime(CLOCK_REALTIME, &start_time);
121#else
122	ch = gettimeofday(&start, NULL);
123	start_time.tv_sec = start.tv_sec;
124	start_time.tv_nsec = start.tv_usec * 1000;
125#endif
126	if (ch == -1)
127		err(EXIT_FAILURE, "Unable to get system time");
128
129
130	while ((ch = getopt(argc, argv, "B:b:d:f:F:LM:m:N:O:o:rs:S:t:T:xZ")) != -1) {
131		switch (ch) {
132
133		case 'B':
134			if (strcmp(optarg, "be") == 0 ||
135			    strcmp(optarg, "4321") == 0 ||
136			    strcmp(optarg, "big") == 0) {
137#if BYTE_ORDER == LITTLE_ENDIAN
138				fsoptions.needswap = 1;
139#endif
140			} else if (strcmp(optarg, "le") == 0 ||
141			    strcmp(optarg, "1234") == 0 ||
142			    strcmp(optarg, "little") == 0) {
143#if BYTE_ORDER == BIG_ENDIAN
144				fsoptions.needswap = 1;
145#endif
146			} else {
147				warnx("Invalid endian `%s'.", optarg);
148				usage(fstype, &fsoptions);
149			}
150			break;
151
152		case 'b':
153			len = strlen(optarg) - 1;
154			if (optarg[len] == '%') {
155				optarg[len] = '\0';
156				fsoptions.freeblockpc = (int)
157				    strsuftoll("free block percentage",
158					optarg, 0, 99);
159			} else {
160				fsoptions.freeblocks =
161				    strsuftoll("free blocks",
162					optarg, 0, LLONG_MAX);
163			}
164			break;
165
166		case 'd':
167			debug = parse_debug(optarg);
168			break;
169
170		case 'f':
171			len = strlen(optarg) - 1;
172			if (optarg[len] == '%') {
173				optarg[len] = '\0';
174				fsoptions.freefilepc = (int)
175				    strsuftoll("free file percentage",
176					optarg, 0, 99);
177			} else {
178				fsoptions.freefiles =
179				    strsuftoll("free files",
180					optarg, 0, LLONG_MAX);
181			}
182			break;
183
184		case 'F':
185			specfile = optarg;
186			break;
187
188		case 'L':
189			fsoptions.follow = true;
190			break;
191
192		case 'M':
193			fsoptions.minsize =
194			    strsuftoll("minimum size", optarg, 1LL, LLONG_MAX);
195			break;
196
197		case 'N':
198			if (! setup_getid(optarg))
199				errx(EXIT_FAILURE,
200			    "Unable to use user and group databases in `%s'",
201				    optarg);
202			break;
203
204		case 'm':
205			fsoptions.maxsize =
206			    strsuftoll("maximum size", optarg, 1LL, LLONG_MAX);
207			break;
208
209		case 'O':
210			fsoptions.offset =
211			    strsuftoll("offset", optarg, 0LL, LLONG_MAX);
212			break;
213
214		case 'o':
215		{
216			char *p;
217
218			while ((p = strsep(&optarg, ",")) != NULL) {
219				if (*p == '\0')
220					errx(EXIT_FAILURE, "Empty option");
221				if (! fstype->parse_options(p, &fsoptions))
222					usage(fstype, &fsoptions);
223			}
224			break;
225		}
226
227		case 'r':
228			fsoptions.replace = 1;
229			break;
230
231		case 's':
232			fsoptions.minsize = fsoptions.maxsize =
233			    strsuftoll("size", optarg, 1LL, LLONG_MAX);
234			break;
235
236		case 'S':
237			fsoptions.sectorsize =
238			    (int)strsuftoll("sector size", optarg,
239				1LL, INT_MAX);
240			break;
241
242		case 't':
243			/* Check current one and cleanup if necessary. */
244			if (fstype->cleanup_options)
245				fstype->cleanup_options(&fsoptions);
246			fsoptions.fs_specific = NULL;
247			if ((fstype = get_fstype(optarg)) == NULL)
248				errx(EXIT_FAILURE,
249				    "Unknown fs type `%s'.", optarg);
250			fstype->prepare_options(&fsoptions);
251			break;
252
253		case 'T':
254			if (get_tstamp(optarg, &stampst) == -1)
255				errx(EXIT_FAILURE,
256				    "Cannot get timestamp from `%s'", optarg);
257			break;
258
259		case 'x':
260			fsoptions.onlyspec++;
261			break;
262
263		case 'Z':
264			fsoptions.sparse = 1;
265			break;
266
267		case '?':
268		default:
269			usage(fstype, &fsoptions);
270			/* NOTREACHED */
271
272		}
273	}
274	if (debug) {
275		printf("debug mask: 0x%08x\n", debug);
276		printf("start time: %ld.%ld, %s",
277		    (long)start_time.tv_sec, (long)start_time.tv_nsec,
278		    ctime(&start_time.tv_sec));
279	}
280	argc -= optind;
281	argv += optind;
282
283	if (argc < 2)
284		usage(fstype, &fsoptions);
285
286	/* -x must be accompanied by -F */
287	if (fsoptions.onlyspec != 0 && specfile == NULL)
288		errx(EXIT_FAILURE, "-x requires -F mtree-specfile.");
289
290				/* walk the tree */
291	TIMER_START(start);
292	root = walk_dir(argv[1], ".", NULL, NULL, fsoptions.replace,
293	    fsoptions.follow);
294	TIMER_RESULTS(start, "walk_dir");
295
296	/* append extra directory */
297	for (i = 2; i < argc; i++) {
298		struct stat sb;
299		if (stat(argv[i], &sb) == -1)
300			err(EXIT_FAILURE, "Can't stat `%s'", argv[i]);
301		if (!S_ISDIR(sb.st_mode))
302			errx(EXIT_FAILURE, "%s: not a directory", argv[i]);
303		TIMER_START(start);
304		root = walk_dir(argv[i], ".", NULL, root, fsoptions.replace,
305		    fsoptions.follow);
306		TIMER_RESULTS(start, "walk_dir2");
307	}
308
309	if (specfile) {		/* apply a specfile */
310		TIMER_START(start);
311		apply_specfile(specfile, argv[1], root, fsoptions.onlyspec);
312		TIMER_RESULTS(start, "apply_specfile");
313	}
314
315	if (debug & DEBUG_DUMP_FSNODES) {
316		printf("\nparent: %s\n", argv[1]);
317		dump_fsnodes(root);
318		putchar('\n');
319	}
320
321				/* build the file system */
322	TIMER_START(start);
323	fstype->make_fs(argv[0], argv[1], root, &fsoptions);
324	TIMER_RESULTS(start, "make_fs");
325
326	free_fsnodes(root);
327
328	exit(EXIT_SUCCESS);
329	/* NOTREACHED */
330}
331
332int
333set_option(const option_t *options, const char *option, char *buf, size_t len)
334{
335	char *var, *val;
336	int retval;
337
338	assert(option != NULL);
339
340	var = estrdup(option);
341	for (val = var; *val; val++)
342		if (*val == '=') {
343			*val++ = '\0';
344			break;
345		}
346	retval = set_option_var(options, var, val, buf, len);
347	free(var);
348	return retval;
349}
350
351void
352print_options(FILE *fp, const option_t *options)
353{
354	for (size_t i = 0; options[i].name != NULL; i++) {
355		fprintf(fp, "%s=", options[i].name);
356		switch (options[i].type) {
357		case OPT_BOOL:
358			fputs(*(bool *)options[i].value ? "true\n" : "false\n",
359			    fp); 
360			break;
361		case OPT_STRARRAY:
362		case OPT_STRPTR:
363		case OPT_STRBUF:
364			fprintf(fp, "%s\n", *(const char **)options[i].value);
365			break;
366		case OPT_INT64:
367			fprintf(fp, "%" PRIu64 "\n",
368			    *(uint64_t *)options[i].value);
369			break;
370		case OPT_INT32:
371			fprintf(fp, "%" PRIu32 "\n",
372			    *(uint32_t *)options[i].value);
373			break;
374		case OPT_INT16:
375			fprintf(fp, "%" PRIu16 "\n",
376			    *(uint16_t *)options[i].value);
377			break;
378		case OPT_INT8:
379			fprintf(fp, "%" PRIu8 "\n",
380			    *(uint8_t *)options[i].value);
381			break;
382		default:
383			warnx("Unknown type %d in option %s", options[i].type,
384			    options[i].name);
385			return;
386		}
387	}
388}
389
390int
391set_option_var(const option_t *options, const char *var, const char *val,
392    char *buf, size_t len)
393{
394	char *s;
395	size_t i;
396
397#define NUM(type) \
398	if (!*val) { \
399		*(type *)options[i].value = 1; \
400		break; \
401	} \
402	*(type *)options[i].value = (type)strsuftoll(options[i].desc, val, \
403	    options[i].minimum, options[i].maximum); break
404
405	for (i = 0; options[i].name != NULL; i++) {
406		if (var[1] == '\0') {
407			if (options[i].letter != var[0])
408				continue;
409		} else if (strcmp(options[i].name, var) != 0)
410			continue;
411		switch (options[i].type) {
412		case OPT_BOOL:
413			*(bool *)options[i].value = 1;
414			break;
415		case OPT_STRARRAY:
416			strlcpy((void *)options[i].value, val, (size_t)
417			    options[i].maximum);
418			break;
419		case OPT_STRPTR:
420			s = estrdup(val);
421			*(char **)options[i].value = s;
422			break;
423		case OPT_STRBUF:
424			if (buf == NULL)
425				abort();
426			strlcpy(buf, val, len);
427			break;
428		case OPT_INT64:
429			NUM(uint64_t);
430		case OPT_INT32:
431			NUM(uint32_t);
432		case OPT_INT16:
433			NUM(uint16_t);
434		case OPT_INT8:
435			NUM(uint8_t);
436		default:
437			warnx("Unknown type %d in option %s", options[i].type,
438			    val);
439			return 0;
440		}
441		return (int)i;
442	}
443	warnx("Unknown option `%s'", var);
444	return -1;
445}
446
447
448static fstype_t *
449get_fstype(const char *type)
450{
451	int i;
452
453	for (i = 0; fstypes[i].type != NULL; i++)
454		if (strcmp(fstypes[i].type, type) == 0)
455			return (&fstypes[i]);
456	return (NULL);
457}
458
459option_t *
460copy_opts(const option_t *o)
461{
462	size_t i;
463	for (i = 0; o[i].name; i++)
464		continue;
465	i++;
466	return memcpy(ecalloc(i, sizeof(*o)), o, i * sizeof(*o));
467}
468
469static int
470get_tstamp(const char *b, struct stat *st)
471{
472	time_t when;
473	char *eb;
474	long long l;
475
476	if (stat(b, st) != -1)
477		return 0;
478
479#ifndef HAVE_NBTOOL_CONFIG_H
480	errno = 0;
481	if ((when = parsedate(b, NULL, NULL)) == -1 && errno != 0)
482#endif
483	{
484		errno = 0;
485		l = strtoll(b, &eb, 0);
486		if (b == eb || *eb || errno)
487			return -1;
488		when = (time_t)l;
489	}
490
491	st->st_ino = 1;
492#if HAVE_STRUCT_STAT_BIRTHTIME
493	st->st_birthtime =
494#endif
495	st->st_mtime = st->st_ctime = st->st_atime = when;
496	return 0;
497}
498
499static struct {
500	const char *n;
501	u_int v;
502} nv[] = {
503	DEBUG_STRINGS
504};
505
506static void
507usage(fstype_t *fstype, fsinfo_t *fsoptions)
508{
509	const char *prog;
510
511	prog = getprogname();
512	fprintf(stderr,
513"Usage: %s [-rxZ] [-B endian] [-b free-blocks] [-d debug-mask|comma-separated-option]\n"
514"\t[-F mtree-specfile] [-f free-files] [-M minimum-size] [-m maximum-size]\n"
515"\t[-N userdb-dir] [-O offset] [-o fs-options] [-S sector-size]\n"
516"\t[-s image-size] [-T <timestamp/file>] [-t fs-type]"
517" image-file directory [extra-directory ...]\n",
518	    prog);
519
520	fprintf(stderr, "\nDebugging options:\n");
521	for (size_t i = 0; i < __arraycount(nv); i++)
522		fprintf(stderr, "\t0x%8.8x\t%s\n", nv[i].v, nv[i].n);
523
524	if (fstype) {
525		size_t i;
526		option_t *o = fsoptions->fs_options;
527
528		fprintf(stderr, "\n%s specific options:\n", fstype->type);
529		for (i = 0; o[i].name != NULL; i++)
530			fprintf(stderr, "\t%c%c%20.20s\t%s\n",
531			    o[i].letter ? o[i].letter : ' ',
532			    o[i].letter ? ',' : ' ',
533			    o[i].name, o[i].desc);
534	}
535	exit(EXIT_FAILURE);
536}
537
538
539static u_int
540parse_debug(char *str)
541{
542	char *ep;
543	u_int d;
544	size_t i;
545
546	errno = 0;
547	d = (u_int)strtoul(str, &ep, 0);
548	if (str != ep && !*ep && errno == 0)
549		return d;
550	d = 0;
551	for (char *a = strtok(str, ","); a != NULL; a = strtok(NULL, ",")) {
552		for (i = 0; i < __arraycount(nv); i++)
553			if (strcmp(nv[i].n, a) == 0) {
554				d |= nv[i].v;
555				break;
556			}
557		if (i == __arraycount(nv))
558			errx(EXIT_FAILURE, "Unknown debug option `%s'", a);
559	}
560	return d;
561}