1214921Scognet/* $NetBSD: makefs.c,v 1.26 2006/10/22 21:11:56 christos Exp $ */ 2185222Ssam 3185222Ssam/* 4185222Ssam * Copyright (c) 2001-2003 Wasabi Systems, Inc. 5185222Ssam * All rights reserved. 6185222Ssam * 7185222Ssam * Written by Luke Mewburn for Wasabi Systems, Inc. 8185222Ssam * 9185222Ssam * Redistribution and use in source and binary forms, with or without 10185222Ssam * modification, are permitted provided that the following conditions 11185222Ssam * are met: 12185222Ssam * 1. Redistributions of source code must retain the above copyright 13185222Ssam * notice, this list of conditions and the following disclaimer. 14185222Ssam * 2. Redistributions in binary form must reproduce the above copyright 15185222Ssam * notice, this list of conditions and the following disclaimer in the 16185222Ssam * documentation and/or other materials provided with the distribution. 17185222Ssam * 3. All advertising materials mentioning features or use of this software 18185222Ssam * must display the following acknowledgement: 19185222Ssam * This product includes software developed for the NetBSD Project by 20185222Ssam * Wasabi Systems, Inc. 21185222Ssam * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22185222Ssam * or promote products derived from this software without specific prior 23185222Ssam * written permission. 24185222Ssam * 25185222Ssam * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26185222Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27185222Ssam * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28185222Ssam * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29185222Ssam * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30185222Ssam * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31185222Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32185222Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33185222Ssam * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34185222Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35185222Ssam * POSSIBILITY OF SUCH DAMAGE. 36185222Ssam */ 37185222Ssam 38185222Ssam#include <sys/cdefs.h> 39186334Ssam__FBSDID("$FreeBSD$"); 40185222Ssam 41223306Smarcel#include <sys/types.h> 42223306Smarcel#include <sys/stat.h> 43185222Ssam#include <assert.h> 44185222Ssam#include <ctype.h> 45185222Ssam#include <errno.h> 46185222Ssam#include <limits.h> 47185222Ssam#include <stdio.h> 48185222Ssam#include <stdlib.h> 49185222Ssam#include <string.h> 50217769Smckusick#include <time.h> 51185222Ssam#include <unistd.h> 52185222Ssam 53185222Ssam#include "makefs.h" 54185222Ssam#include "mtree.h" 55185222Ssam 56185222Ssam/* 57185222Ssam * list of supported file systems and dispatch functions 58185222Ssam */ 59185222Ssamtypedef struct { 60185222Ssam const char *type; 61214921Scognet void (*prepare_options)(fsinfo_t *); 62185222Ssam int (*parse_options)(const char *, fsinfo_t *); 63214921Scognet void (*cleanup_options)(fsinfo_t *); 64185222Ssam void (*make_fs)(const char *, const char *, fsnode *, 65185222Ssam fsinfo_t *); 66185222Ssam} fstype_t; 67185222Ssam 68185222Ssamstatic fstype_t fstypes[] = { 69214921Scognet { "ffs", ffs_prep_opts, ffs_parse_opts, ffs_cleanup_opts, ffs_makefs }, 70214921Scognet { "cd9660", cd9660_prep_opts, cd9660_parse_opts, cd9660_cleanup_opts, 71214921Scognet cd9660_makefs}, 72214921Scognet { .type = NULL }, 73185222Ssam}; 74185222Ssam 75185222Ssamu_int debug; 76247041Sbrooksint dupsok; 77185222Ssamstruct timespec start_time; 78185222Ssam 79185222Ssamstatic fstype_t *get_fstype(const char *); 80185222Ssamstatic void usage(void); 81185222Ssamint main(int, char *[]); 82185222Ssam 83185222Ssamint 84185222Ssammain(int argc, char *argv[]) 85185222Ssam{ 86223306Smarcel struct stat sb; 87185222Ssam struct timeval start; 88185222Ssam fstype_t *fstype; 89185222Ssam fsinfo_t fsoptions; 90185222Ssam fsnode *root; 91230795Sjkim int ch, i, len; 92223306Smarcel char *subtree; 93185222Ssam char *specfile; 94185222Ssam 95185222Ssam setprogname(argv[0]); 96185222Ssam 97185222Ssam debug = 0; 98185222Ssam if ((fstype = get_fstype(DEFAULT_FSTYPE)) == NULL) 99185222Ssam errx(1, "Unknown default fs type `%s'.", DEFAULT_FSTYPE); 100185222Ssam 101185222Ssam /* set default fsoptions */ 102185222Ssam (void)memset(&fsoptions, 0, sizeof(fsoptions)); 103185222Ssam fsoptions.fd = -1; 104185222Ssam fsoptions.sectorsize = -1; 105185222Ssam 106214921Scognet if (fstype->prepare_options) 107214921Scognet fstype->prepare_options(&fsoptions); 108214921Scognet 109185222Ssam specfile = NULL; 110185222Ssam if (gettimeofday(&start, NULL) == -1) 111185222Ssam err(1, "Unable to get system time"); 112185222Ssam 113185222Ssam start_time.tv_sec = start.tv_sec; 114185222Ssam start_time.tv_nsec = start.tv_usec * 1000; 115185222Ssam 116254397Sgjb while ((ch = getopt(argc, argv, "B:b:Dd:f:F:M:m:N:o:ps:S:t:xZ")) != -1) { 117185222Ssam switch (ch) { 118185222Ssam 119185222Ssam case 'B': 120185222Ssam if (strcmp(optarg, "be") == 0 || 121185222Ssam strcmp(optarg, "4321") == 0 || 122185222Ssam strcmp(optarg, "big") == 0) { 123185222Ssam#if BYTE_ORDER == LITTLE_ENDIAN 124185222Ssam fsoptions.needswap = 1; 125185222Ssam#endif 126185222Ssam } else if (strcmp(optarg, "le") == 0 || 127185222Ssam strcmp(optarg, "1234") == 0 || 128185222Ssam strcmp(optarg, "little") == 0) { 129185222Ssam#if BYTE_ORDER == BIG_ENDIAN 130185222Ssam fsoptions.needswap = 1; 131185222Ssam#endif 132185222Ssam } else { 133185222Ssam warnx("Invalid endian `%s'.", optarg); 134185222Ssam usage(); 135185222Ssam } 136185222Ssam break; 137185222Ssam 138185222Ssam case 'b': 139185222Ssam len = strlen(optarg) - 1; 140185222Ssam if (optarg[len] == '%') { 141185222Ssam optarg[len] = '\0'; 142185222Ssam fsoptions.freeblockpc = 143185222Ssam strsuftoll("free block percentage", 144185222Ssam optarg, 0, 99); 145185222Ssam } else { 146185222Ssam fsoptions.freeblocks = 147185222Ssam strsuftoll("free blocks", 148185222Ssam optarg, 0, LLONG_MAX); 149185222Ssam } 150185222Ssam break; 151185222Ssam 152247041Sbrooks case 'D': 153247041Sbrooks dupsok = 1; 154247041Sbrooks break; 155247041Sbrooks 156185222Ssam case 'd': 157214921Scognet debug = strtoll(optarg, NULL, 0); 158185222Ssam break; 159185222Ssam 160185222Ssam case 'f': 161185222Ssam len = strlen(optarg) - 1; 162185222Ssam if (optarg[len] == '%') { 163185222Ssam optarg[len] = '\0'; 164185222Ssam fsoptions.freefilepc = 165185222Ssam strsuftoll("free file percentage", 166185222Ssam optarg, 0, 99); 167185222Ssam } else { 168185222Ssam fsoptions.freefiles = 169185222Ssam strsuftoll("free files", 170185222Ssam optarg, 0, LLONG_MAX); 171185222Ssam } 172185222Ssam break; 173185222Ssam 174185222Ssam case 'F': 175185222Ssam specfile = optarg; 176185222Ssam break; 177185222Ssam 178185222Ssam case 'M': 179185222Ssam fsoptions.minsize = 180185222Ssam strsuftoll("minimum size", optarg, 1LL, LLONG_MAX); 181185222Ssam break; 182185222Ssam 183185222Ssam case 'N': 184185222Ssam if (! setup_getid(optarg)) 185185222Ssam errx(1, 186185222Ssam "Unable to use user and group databases in `%s'", 187185222Ssam optarg); 188185222Ssam break; 189185222Ssam 190185222Ssam case 'm': 191185222Ssam fsoptions.maxsize = 192185222Ssam strsuftoll("maximum size", optarg, 1LL, LLONG_MAX); 193185222Ssam break; 194185222Ssam 195185222Ssam case 'o': 196185222Ssam { 197185222Ssam char *p; 198185222Ssam 199185222Ssam while ((p = strsep(&optarg, ",")) != NULL) { 200185222Ssam if (*p == '\0') 201185222Ssam errx(1, "Empty option"); 202185222Ssam if (! fstype->parse_options(p, &fsoptions)) 203185222Ssam usage(); 204185222Ssam } 205185222Ssam break; 206185222Ssam } 207239562Shrs case 'p': 208254397Sgjb /* Deprecated in favor of 'Z' */ 209239562Shrs fsoptions.sparse = 1; 210239562Shrs break; 211185222Ssam 212185222Ssam case 's': 213185222Ssam fsoptions.minsize = fsoptions.maxsize = 214185222Ssam strsuftoll("size", optarg, 1LL, LLONG_MAX); 215185222Ssam break; 216185222Ssam 217185222Ssam case 'S': 218185222Ssam fsoptions.sectorsize = 219185222Ssam (int)strsuftoll("sector size", optarg, 220185222Ssam 1LL, INT_MAX); 221185222Ssam break; 222185222Ssam 223185222Ssam case 't': 224214921Scognet /* Check current one and cleanup if necessary. */ 225214921Scognet if (fstype->cleanup_options) 226214921Scognet fstype->cleanup_options(&fsoptions); 227214921Scognet fsoptions.fs_specific = NULL; 228185222Ssam if ((fstype = get_fstype(optarg)) == NULL) 229185222Ssam errx(1, "Unknown fs type `%s'.", optarg); 230214921Scognet fstype->prepare_options(&fsoptions); 231185222Ssam break; 232185222Ssam 233185222Ssam case 'x': 234185222Ssam fsoptions.onlyspec = 1; 235185222Ssam break; 236185222Ssam 237254397Sgjb case 'Z': 238254397Sgjb /* Superscedes 'p' for compatibility with NetBSD makefs(8) */ 239254397Sgjb fsoptions.sparse = 1; 240254397Sgjb break; 241254397Sgjb 242185222Ssam case '?': 243185222Ssam default: 244185222Ssam usage(); 245185222Ssam /* NOTREACHED */ 246185222Ssam 247185222Ssam } 248185222Ssam } 249185222Ssam if (debug) { 250185222Ssam printf("debug mask: 0x%08x\n", debug); 251185222Ssam printf("start time: %ld.%ld, %s", 252185222Ssam (long)start_time.tv_sec, (long)start_time.tv_nsec, 253185222Ssam ctime(&start_time.tv_sec)); 254185222Ssam } 255185222Ssam argc -= optind; 256185222Ssam argv += optind; 257185222Ssam 258230795Sjkim if (argc < 2) 259185222Ssam usage(); 260185222Ssam 261185222Ssam /* -x must be accompanied by -F */ 262185222Ssam if (fsoptions.onlyspec != 0 && specfile == NULL) 263185222Ssam errx(1, "-x requires -F mtree-specfile."); 264185222Ssam 265223306Smarcel /* Accept '-' as meaning "read from standard input". */ 266223306Smarcel if (strcmp(argv[1], "-") == 0) 267223306Smarcel sb.st_mode = S_IFREG; 268223306Smarcel else { 269223306Smarcel if (stat(argv[1], &sb) == -1) 270223306Smarcel err(1, "Can't stat `%s'", argv[1]); 271223306Smarcel } 272185222Ssam 273223306Smarcel switch (sb.st_mode & S_IFMT) { 274223306Smarcel case S_IFDIR: /* walk the tree */ 275223306Smarcel subtree = argv[1]; 276223306Smarcel TIMER_START(start); 277230795Sjkim root = walk_dir(subtree, ".", NULL, NULL); 278223306Smarcel TIMER_RESULTS(start, "walk_dir"); 279223306Smarcel break; 280223306Smarcel case S_IFREG: /* read the manifest file */ 281223306Smarcel subtree = "."; 282223306Smarcel TIMER_START(start); 283223306Smarcel root = read_mtree(argv[1], NULL); 284223306Smarcel TIMER_RESULTS(start, "manifest"); 285223306Smarcel break; 286223306Smarcel default: 287223306Smarcel errx(1, "%s: not a file or directory", argv[1]); 288223306Smarcel /* NOTREACHED */ 289223306Smarcel } 290223306Smarcel 291230795Sjkim /* append extra directory */ 292230795Sjkim for (i = 2; i < argc; i++) { 293230795Sjkim if (stat(argv[i], &sb) == -1) 294230795Sjkim err(1, "Can't stat `%s'", argv[i]); 295230795Sjkim if (!S_ISDIR(sb.st_mode)) 296230795Sjkim errx(1, "%s: not a directory", argv[i]); 297230795Sjkim TIMER_START(start); 298230795Sjkim root = walk_dir(argv[i], ".", NULL, root); 299230795Sjkim TIMER_RESULTS(start, "walk_dir2"); 300230795Sjkim } 301230795Sjkim 302185222Ssam if (specfile) { /* apply a specfile */ 303185222Ssam TIMER_START(start); 304223306Smarcel apply_specfile(specfile, subtree, root, fsoptions.onlyspec); 305185222Ssam TIMER_RESULTS(start, "apply_specfile"); 306185222Ssam } 307185222Ssam 308185222Ssam if (debug & DEBUG_DUMP_FSNODES) { 309223306Smarcel printf("\nparent: %s\n", subtree); 310230795Sjkim dump_fsnodes(root); 311185222Ssam putchar('\n'); 312185222Ssam } 313185222Ssam 314185222Ssam /* build the file system */ 315185222Ssam TIMER_START(start); 316223306Smarcel fstype->make_fs(argv[0], subtree, root, &fsoptions); 317185222Ssam TIMER_RESULTS(start, "make_fs"); 318185222Ssam 319214921Scognet free_fsnodes(root); 320214921Scognet 321185222Ssam exit(0); 322185222Ssam /* NOTREACHED */ 323185222Ssam} 324185222Ssam 325185222Ssam 326185222Ssamint 327185222Ssamset_option(option_t *options, const char *var, const char *val) 328185222Ssam{ 329185222Ssam int i; 330185222Ssam 331185222Ssam for (i = 0; options[i].name != NULL; i++) { 332185222Ssam if (strcmp(options[i].name, var) != 0) 333185222Ssam continue; 334185222Ssam *options[i].value = (int)strsuftoll(options[i].desc, val, 335185222Ssam options[i].minimum, options[i].maximum); 336185222Ssam return (1); 337185222Ssam } 338185222Ssam warnx("Unknown option `%s'", var); 339185222Ssam return (0); 340185222Ssam} 341185222Ssam 342185222Ssam 343185222Ssamstatic fstype_t * 344185222Ssamget_fstype(const char *type) 345185222Ssam{ 346185222Ssam int i; 347185222Ssam 348185222Ssam for (i = 0; fstypes[i].type != NULL; i++) 349185222Ssam if (strcmp(fstypes[i].type, type) == 0) 350185222Ssam return (&fstypes[i]); 351185222Ssam return (NULL); 352185222Ssam} 353185222Ssam 354185222Ssamstatic void 355185222Ssamusage(void) 356185222Ssam{ 357185222Ssam const char *prog; 358185222Ssam 359185222Ssam prog = getprogname(); 360185222Ssam fprintf(stderr, 361185222Ssam"usage: %s [-t fs-type] [-o fs-options] [-d debug-mask] [-B endian]\n" 362185222Ssam"\t[-S sector-size] [-M minimum-size] [-m maximum-size] [-s image-size]\n" 363254397Sgjb"\t[-b free-blocks] [-f free-files] [-F mtree-specfile] [-xZ]\n" 364230795Sjkim"\t[-N userdb-dir] image-file directory | manifest [extra-directory ...]\n", 365185222Ssam prog); 366185222Ssam exit(1); 367185222Ssam} 368