1/* $NetBSD: makefs.c,v 1.58 2024/05/08 15:57:56 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.58 2024/05/08 15:57:56 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(ffs), 80 ENTRY(cd9660), 81 ENTRY(chfs), 82 ENTRY(v7fs), 83 ENTRY(msdos), 84 ENTRY(udf), 85 { .type = NULL }, 86}; 87 88u_int debug; 89struct timespec start_time; 90struct stat stampst; 91 92static fstype_t *get_fstype(const char *); 93static int get_tstamp(const char *, struct stat *); 94static void usage(fstype_t *, fsinfo_t *) __dead; 95 96int 97main(int argc, char *argv[]) 98{ 99 struct timeval start; 100 fstype_t *fstype; 101 fsinfo_t fsoptions; 102 fsnode *root; 103 int ch, i; 104 size_t len; 105 char *specfile; 106 107 setprogname(argv[0]); 108 109 debug = 0; 110 if ((fstype = get_fstype(DEFAULT_FSTYPE)) == NULL) 111 errx(EXIT_FAILURE, 112 "Unknown default fs type `%s'.", DEFAULT_FSTYPE); 113 114 /* set default fsoptions */ 115 (void)memset(&fsoptions, 0, sizeof(fsoptions)); 116 fsoptions.fd = -1; 117 fsoptions.sectorsize = -1; 118 119 if (fstype->prepare_options) 120 fstype->prepare_options(&fsoptions); 121 122 specfile = NULL; 123#ifdef CLOCK_REALTIME 124 ch = clock_gettime(CLOCK_REALTIME, &start_time); 125#else 126 ch = gettimeofday(&start, NULL); 127 start_time.tv_sec = start.tv_sec; 128 start_time.tv_nsec = start.tv_usec * 1000; 129#endif 130 if (ch == -1) 131 err(EXIT_FAILURE, "Unable to get system time"); 132 133 134 while ((ch = getopt(argc, argv, "B:b:d:f:F:LM:m:N:O:o:rs:S:t:T:xZ")) != -1) { 135 switch (ch) { 136 137 case 'B': 138 if (strcmp(optarg, "be") == 0 || 139 strcmp(optarg, "4321") == 0 || 140 strcmp(optarg, "big") == 0) { 141#if BYTE_ORDER == LITTLE_ENDIAN 142 fsoptions.needswap = 1; 143#endif 144 } else if (strcmp(optarg, "le") == 0 || 145 strcmp(optarg, "1234") == 0 || 146 strcmp(optarg, "little") == 0) { 147#if BYTE_ORDER == BIG_ENDIAN 148 fsoptions.needswap = 1; 149#endif 150 } else { 151 warnx("Invalid endian `%s'.", optarg); 152 usage(fstype, &fsoptions); 153 } 154 break; 155 156 case 'b': 157 len = strlen(optarg) - 1; 158 if (optarg[len] == '%') { 159 optarg[len] = '\0'; 160 fsoptions.freeblockpc = (int) 161 strsuftoll("free block percentage", 162 optarg, 0, 99); 163 } else { 164 fsoptions.freeblocks = 165 strsuftoll("free blocks", 166 optarg, 0, LLONG_MAX); 167 } 168 break; 169 170 case 'd': 171 debug = (int)strtoll(optarg, NULL, 0); 172 break; 173 174 case 'f': 175 len = strlen(optarg) - 1; 176 if (optarg[len] == '%') { 177 optarg[len] = '\0'; 178 fsoptions.freefilepc = (int) 179 strsuftoll("free file percentage", 180 optarg, 0, 99); 181 } else { 182 fsoptions.freefiles = 183 strsuftoll("free files", 184 optarg, 0, LLONG_MAX); 185 } 186 break; 187 188 case 'F': 189 specfile = optarg; 190 break; 191 192 case 'L': 193 fsoptions.follow = true; 194 break; 195 196 case 'M': 197 fsoptions.minsize = 198 strsuftoll("minimum size", optarg, 1LL, LLONG_MAX); 199 break; 200 201 case 'N': 202 if (! setup_getid(optarg)) 203 errx(EXIT_FAILURE, 204 "Unable to use user and group databases in `%s'", 205 optarg); 206 break; 207 208 case 'm': 209 fsoptions.maxsize = 210 strsuftoll("maximum size", optarg, 1LL, LLONG_MAX); 211 break; 212 213 case 'O': 214 fsoptions.offset = 215 strsuftoll("offset", optarg, 0LL, LLONG_MAX); 216 break; 217 218 case 'o': 219 { 220 char *p; 221 222 while ((p = strsep(&optarg, ",")) != NULL) { 223 if (*p == '\0') 224 errx(EXIT_FAILURE, "Empty option"); 225 if (! fstype->parse_options(p, &fsoptions)) 226 usage(fstype, &fsoptions); 227 } 228 break; 229 } 230 231 case 'r': 232 fsoptions.replace = 1; 233 break; 234 235 case 's': 236 fsoptions.minsize = fsoptions.maxsize = 237 strsuftoll("size", optarg, 1LL, LLONG_MAX); 238 break; 239 240 case 'S': 241 fsoptions.sectorsize = 242 (int)strsuftoll("sector size", optarg, 243 1LL, INT_MAX); 244 break; 245 246 case 't': 247 /* Check current one and cleanup if necessary. */ 248 if (fstype->cleanup_options) 249 fstype->cleanup_options(&fsoptions); 250 fsoptions.fs_specific = NULL; 251 if ((fstype = get_fstype(optarg)) == NULL) 252 errx(EXIT_FAILURE, 253 "Unknown fs type `%s'.", optarg); 254 fstype->prepare_options(&fsoptions); 255 break; 256 257 case 'T': 258 if (get_tstamp(optarg, &stampst) == -1) 259 errx(EXIT_FAILURE, 260 "Cannot get timestamp from `%s'", optarg); 261 break; 262 263 case 'x': 264 fsoptions.onlyspec++; 265 break; 266 267 case 'Z': 268 fsoptions.sparse = 1; 269 break; 270 271 case '?': 272 default: 273 usage(fstype, &fsoptions); 274 /* NOTREACHED */ 275 276 } 277 } 278 if (debug) { 279 printf("debug mask: 0x%08x\n", debug); 280 printf("start time: %ld.%ld, %s", 281 (long)start_time.tv_sec, (long)start_time.tv_nsec, 282 ctime(&start_time.tv_sec)); 283 } 284 argc -= optind; 285 argv += optind; 286 287 if (argc < 2) 288 usage(fstype, &fsoptions); 289 290 /* -x must be accompanied by -F */ 291 if (fsoptions.onlyspec != 0 && specfile == NULL) 292 errx(EXIT_FAILURE, "-x requires -F mtree-specfile."); 293 294 /* walk the tree */ 295 TIMER_START(start); 296 root = walk_dir(argv[1], ".", NULL, NULL, fsoptions.replace, 297 fsoptions.follow); 298 TIMER_RESULTS(start, "walk_dir"); 299 300 /* append extra directory */ 301 for (i = 2; i < argc; i++) { 302 struct stat sb; 303 if (stat(argv[i], &sb) == -1) 304 err(EXIT_FAILURE, "Can't stat `%s'", argv[i]); 305 if (!S_ISDIR(sb.st_mode)) 306 errx(EXIT_FAILURE, "%s: not a directory", argv[i]); 307 TIMER_START(start); 308 root = walk_dir(argv[i], ".", NULL, root, fsoptions.replace, 309 fsoptions.follow); 310 TIMER_RESULTS(start, "walk_dir2"); 311 } 312 313 if (specfile) { /* apply a specfile */ 314 TIMER_START(start); 315 apply_specfile(specfile, argv[1], root, fsoptions.onlyspec); 316 TIMER_RESULTS(start, "apply_specfile"); 317 } 318 319 if (debug & DEBUG_DUMP_FSNODES) { 320 printf("\nparent: %s\n", argv[1]); 321 dump_fsnodes(root); 322 putchar('\n'); 323 } 324 325 /* build the file system */ 326 TIMER_START(start); 327 fstype->make_fs(argv[0], argv[1], root, &fsoptions); 328 TIMER_RESULTS(start, "make_fs"); 329 330 free_fsnodes(root); 331 332 exit(EXIT_SUCCESS); 333 /* NOTREACHED */ 334} 335 336int 337set_option(const option_t *options, const char *option, char *buf, size_t len) 338{ 339 char *var, *val; 340 int retval; 341 342 assert(option != NULL); 343 344 var = estrdup(option); 345 for (val = var; *val; val++) 346 if (*val == '=') { 347 *val++ = '\0'; 348 break; 349 } 350 retval = set_option_var(options, var, val, buf, len); 351 free(var); 352 return retval; 353} 354 355void 356print_options(FILE *fp, const option_t *options) 357{ 358 for (size_t i = 0; options[i].name != NULL; i++) { 359 fprintf(fp, "%s=", options[i].name); 360 switch (options[i].type) { 361 case OPT_BOOL: 362 fputs(*(bool *)options[i].value ? "true\n" : "false\n", 363 fp); 364 break; 365 case OPT_STRARRAY: 366 case OPT_STRPTR: 367 case OPT_STRBUF: 368 fprintf(fp, "%s\n", *(const char **)options[i].value); 369 break; 370 case OPT_INT64: 371 fprintf(fp, "%" PRIu64 "\n", 372 *(uint64_t *)options[i].value); 373 break; 374 case OPT_INT32: 375 fprintf(fp, "%" PRIu32 "\n", 376 *(uint32_t *)options[i].value); 377 break; 378 case OPT_INT16: 379 fprintf(fp, "%" PRIu16 "\n", 380 *(uint16_t *)options[i].value); 381 break; 382 case OPT_INT8: 383 fprintf(fp, "%" PRIu8 "\n", 384 *(uint8_t *)options[i].value); 385 break; 386 default: 387 warnx("Unknown type %d in option %s", options[i].type, 388 options[i].name); 389 return; 390 } 391 } 392} 393 394int 395set_option_var(const option_t *options, const char *var, const char *val, 396 char *buf, size_t len) 397{ 398 char *s; 399 size_t i; 400 401#define NUM(type) \ 402 if (!*val) { \ 403 *(type *)options[i].value = 1; \ 404 break; \ 405 } \ 406 *(type *)options[i].value = (type)strsuftoll(options[i].desc, val, \ 407 options[i].minimum, options[i].maximum); break 408 409 for (i = 0; options[i].name != NULL; i++) { 410 if (var[1] == '\0') { 411 if (options[i].letter != var[0]) 412 continue; 413 } else if (strcmp(options[i].name, var) != 0) 414 continue; 415 switch (options[i].type) { 416 case OPT_BOOL: 417 *(bool *)options[i].value = 1; 418 break; 419 case OPT_STRARRAY: 420 strlcpy((void *)options[i].value, val, (size_t) 421 options[i].maximum); 422 break; 423 case OPT_STRPTR: 424 s = estrdup(val); 425 *(char **)options[i].value = s; 426 break; 427 case OPT_STRBUF: 428 if (buf == NULL) 429 abort(); 430 strlcpy(buf, val, len); 431 break; 432 case OPT_INT64: 433 NUM(uint64_t); 434 case OPT_INT32: 435 NUM(uint32_t); 436 case OPT_INT16: 437 NUM(uint16_t); 438 case OPT_INT8: 439 NUM(uint8_t); 440 default: 441 warnx("Unknown type %d in option %s", options[i].type, 442 val); 443 return 0; 444 } 445 return (int)i; 446 } 447 warnx("Unknown option `%s'", var); 448 return -1; 449} 450 451 452static fstype_t * 453get_fstype(const char *type) 454{ 455 int i; 456 457 for (i = 0; fstypes[i].type != NULL; i++) 458 if (strcmp(fstypes[i].type, type) == 0) 459 return (&fstypes[i]); 460 return (NULL); 461} 462 463option_t * 464copy_opts(const option_t *o) 465{ 466 size_t i; 467 for (i = 0; o[i].name; i++) 468 continue; 469 i++; 470 return memcpy(ecalloc(i, sizeof(*o)), o, i * sizeof(*o)); 471} 472 473static int 474get_tstamp(const char *b, struct stat *st) 475{ 476 time_t when; 477 char *eb; 478 long long l; 479 480 if (stat(b, st) != -1) 481 return 0; 482 483#ifndef HAVE_NBTOOL_CONFIG_H 484 errno = 0; 485 if ((when = parsedate(b, NULL, NULL)) == -1 && errno != 0) 486#endif 487 { 488 errno = 0; 489 l = strtoll(b, &eb, 0); 490 if (b == eb || *eb || errno) 491 return -1; 492 when = (time_t)l; 493 } 494 495 st->st_ino = 1; 496#if HAVE_STRUCT_STAT_BIRTHTIME 497 st->st_birthtime = 498#endif 499 st->st_mtime = st->st_ctime = st->st_atime = when; 500 return 0; 501} 502 503static void 504usage(fstype_t *fstype, fsinfo_t *fsoptions) 505{ 506 const char *prog; 507 508 prog = getprogname(); 509 fprintf(stderr, 510"Usage: %s [-rxZ] [-B endian] [-b free-blocks] [-d debug-mask]\n" 511"\t[-F mtree-specfile] [-f free-files] [-M minimum-size] [-m maximum-size]\n" 512"\t[-N userdb-dir] [-O offset] [-o fs-options] [-S sector-size]\n" 513"\t[-s image-size] [-T <timestamp/file>] [-t fs-type]" 514" image-file directory [extra-directory ...]\n", 515 prog); 516 517 if (fstype) { 518 size_t i; 519 option_t *o = fsoptions->fs_options; 520 521 fprintf(stderr, "\n%s specific options:\n", fstype->type); 522 for (i = 0; o[i].name != NULL; i++) 523 fprintf(stderr, "\t%c%c%20.20s\t%s\n", 524 o[i].letter ? o[i].letter : ' ', 525 o[i].letter ? ',' : ' ', 526 o[i].name, o[i].desc); 527 } 528 exit(EXIT_FAILURE); 529} 530