1 /* $NetBSD: makefs.c,v 1.26 2006/10/22 21:11: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 #include <sys/cdefs.h> 39 __FBSDID("$FreeBSD$"); 40 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <assert.h> 44 #include <ctype.h> 45 #include <errno.h> 46 #include <limits.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <time.h> 51 #include <unistd.h> 52 #include <stdbool.h> 53 54 #include "makefs.h" 55 #include "mtree.h" 56 57 /* 58 * list of supported file systems and dispatch functions 59 */ 60 typedef struct { 61 const char *type; 62 void (*prepare_options)(fsinfo_t *); 63 int (*parse_options)(const char *, fsinfo_t *); 64 void (*cleanup_options)(fsinfo_t *); 65 void (*make_fs)(const char *, const char *, fsnode *, 66 fsinfo_t *); 67 } fstype_t; 68 69 static fstype_t fstypes[] = { 70 #define ENTRY(name) { \ 71 # name, name ## _prep_opts, name ## _parse_opts, \ 72 name ## _cleanup_opts, name ## _makefs \ 73 } 74 ENTRY(ffs), 75 ENTRY(cd9660), 76 { .type = NULL }, 77 }; 78 79 u_int debug; 80 int dupsok; 81 struct timespec start_time; 82 struct stat stampst; 83 84 static fstype_t *get_fstype(const char *); 85 static int get_tstamp(const char *, struct stat *); 86 static void usage(fstype_t *, fsinfo_t *); 87 88 int 89 main(int argc, char *argv[]) 90 { 91 struct stat sb; 92 struct timeval start; 93 fstype_t *fstype; 94 fsinfo_t fsoptions; 95 fsnode *root; 96 int ch, i, len; 97 char *subtree; 98 char *specfile; 99 100 setprogname(argv[0]); 101 102 debug = 0; 103 if ((fstype = get_fstype(DEFAULT_FSTYPE)) == NULL) 104 errx(1, "Unknown default fs type `%s'.", DEFAULT_FSTYPE); 105 106 /* set default fsoptions */ 107 (void)memset(&fsoptions, 0, sizeof(fsoptions)); 108 fsoptions.fd = -1; 109 fsoptions.sectorsize = -1; 110 111 if (fstype->prepare_options) 112 fstype->prepare_options(&fsoptions); 113 114 specfile = NULL; 115 #ifdef CLOCK_REALTIME 116 ch = clock_gettime(CLOCK_REALTIME, &start_time); 117 #else 118 ch = gettimeofday(&start, NULL); 119 start_time.tv_sec = start.tv_sec; 120 start_time.tv_nsec = start.tv_usec * 1000; 121 #endif 122 if (ch == -1) 123 err(1, "Unable to get system time"); 124 125 126 while ((ch = getopt(argc, argv, "B:b:Dd:f:F:M:m:N:o:pR:s:S:t:T:xZ")) != -1) { 127 switch (ch) { 128 129 case 'B': 130 if (strcmp(optarg, "be") == 0 || 131 strcmp(optarg, "4321") == 0 || 132 strcmp(optarg, "big") == 0) { 133 #if BYTE_ORDER == LITTLE_ENDIAN 134 fsoptions.needswap = 1; 135 #endif 136 } else if (strcmp(optarg, "le") == 0 || 137 strcmp(optarg, "1234") == 0 || 138 strcmp(optarg, "little") == 0) { 139 #if BYTE_ORDER == BIG_ENDIAN 140 fsoptions.needswap = 1; 141 #endif 142 } else { 143 warnx("Invalid endian `%s'.", optarg); 144 usage(fstype, &fsoptions); 145 } 146 break; 147 148 case 'b': 149 len = strlen(optarg) - 1; 150 if (optarg[len] == '%') { 151 optarg[len] = '\0'; 152 fsoptions.freeblockpc = 153 strsuftoll("free block percentage", 154 optarg, 0, 99); 155 } else { 156 fsoptions.freeblocks = 157 strsuftoll("free blocks", 158 optarg, 0, LLONG_MAX); 159 } 160 break; 161 162 case 'D': 163 dupsok = 1; 164 break; 165 166 case 'd': 167 debug = strtoll(optarg, NULL, 0); 168 break; 169 170 case 'f': 171 len = strlen(optarg) - 1; 172 if (optarg[len] == '%') { 173 optarg[len] = '\0'; 174 fsoptions.freefilepc = 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 'M': 189 fsoptions.minsize = 190 strsuftoll("minimum size", optarg, 1LL, LLONG_MAX); 191 break; 192 193 case 'N': 194 if (! setup_getid(optarg)) 195 errx(1, 196 "Unable to use user and group databases in `%s'", 197 optarg); 198 break; 199 200 case 'm': 201 fsoptions.maxsize = 202 strsuftoll("maximum size", optarg, 1LL, LLONG_MAX); 203 break; 204 205 case 'o': 206 { 207 char *p; 208 209 while ((p = strsep(&optarg, ",")) != NULL) { 210 if (*p == '\0') 211 errx(1, "Empty option"); 212 if (! fstype->parse_options(p, &fsoptions)) 213 usage(fstype, &fsoptions); 214 } 215 break; 216 } 217 case 'p': 218 /* Deprecated in favor of 'Z' */ 219 fsoptions.sparse = 1; 220 break; 221 222 case 'R': 223 /* Round image size up to specified block size */ 224 fsoptions.roundup = 225 strsuftoll("roundup-size", optarg, 0, LLONG_MAX); 226 break; 227 228 case 's': 229 fsoptions.minsize = fsoptions.maxsize = 230 strsuftoll("size", optarg, 1LL, LLONG_MAX); 231 break; 232 233 case 'S': 234 fsoptions.sectorsize = 235 (int)strsuftoll("sector size", optarg, 236 1LL, INT_MAX); 237 break; 238 239 case 't': 240 /* Check current one and cleanup if necessary. */ 241 if (fstype->cleanup_options) 242 fstype->cleanup_options(&fsoptions); 243 fsoptions.fs_specific = NULL; 244 if ((fstype = get_fstype(optarg)) == NULL) 245 errx(1, "Unknown fs type `%s'.", optarg); 246 fstype->prepare_options(&fsoptions); 247 break; 248 249 case 'T': 250 if (get_tstamp(optarg, &stampst) == -1) 251 errx(1, "Cannot get timestamp from `%s'", 252 optarg); 253 break; 254 255 case 'x': 256 fsoptions.onlyspec = 1; 257 break; 258 259 case 'Z': 260 /* Superscedes 'p' for compatibility with NetBSD makefs(8) */ 261 fsoptions.sparse = 1; 262 break; 263 264 case '?': 265 default: 266 usage(fstype, &fsoptions); 267 /* NOTREACHED */ 268 269 } 270 } 271 if (debug) { 272 printf("debug mask: 0x%08x\n", debug); 273 printf("start time: %ld.%ld, %s", 274 (long)start_time.tv_sec, (long)start_time.tv_nsec, 275 ctime(&start_time.tv_sec)); 276 } 277 argc -= optind; 278 argv += optind; 279 280 if (argc < 2) 281 usage(fstype, &fsoptions); 282 283 /* -x must be accompanied by -F */ 284 if (fsoptions.onlyspec != 0 && specfile == NULL) 285 errx(1, "-x requires -F mtree-specfile."); 286 287 /* Accept '-' as meaning "read from standard input". */ 288 if (strcmp(argv[1], "-") == 0) 289 sb.st_mode = S_IFREG; 290 else { 291 if (stat(argv[1], &sb) == -1) 292 err(1, "Can't stat `%s'", argv[1]); 293 } 294 295 switch (sb.st_mode & S_IFMT) { 296 case S_IFDIR: /* walk the tree */ 297 subtree = argv[1]; 298 TIMER_START(start); 299 root = walk_dir(subtree, ".", NULL, NULL); 300 TIMER_RESULTS(start, "walk_dir"); 301 break; 302 case S_IFREG: /* read the manifest file */ 303 subtree = "."; 304 TIMER_START(start); 305 root = read_mtree(argv[1], NULL); 306 TIMER_RESULTS(start, "manifest"); 307 break; 308 default: 309 errx(1, "%s: not a file or directory", argv[1]); 310 /* NOTREACHED */ 311 } 312 313 /* append extra directory */ 314 for (i = 2; i < argc; i++) { 315 if (stat(argv[i], &sb) == -1) 316 err(1, "Can't stat `%s'", argv[i]); 317 if (!S_ISDIR(sb.st_mode)) 318 errx(1, "%s: not a directory", argv[i]); 319 TIMER_START(start); 320 root = walk_dir(argv[i], ".", NULL, root); 321 TIMER_RESULTS(start, "walk_dir2"); 322 } 323 324 if (specfile) { /* apply a specfile */ 325 TIMER_START(start); 326 apply_specfile(specfile, subtree, root, fsoptions.onlyspec); 327 TIMER_RESULTS(start, "apply_specfile"); 328 } 329 330 if (debug & DEBUG_DUMP_FSNODES) { 331 printf("\nparent: %s\n", subtree); 332 dump_fsnodes(root); 333 putchar('\n'); 334 } 335 336 /* build the file system */ 337 TIMER_START(start); 338 fstype->make_fs(argv[0], subtree, root, &fsoptions); 339 TIMER_RESULTS(start, "make_fs"); 340 341 free_fsnodes(root); 342 343 exit(0); 344 /* NOTREACHED */ 345 } 346 347 int 348 set_option(const option_t *options, const char *option, char *buf, size_t len) 349 { 350 char *var, *val; 351 int retval; 352 353 assert(option != NULL); 354 355 if ((var = strdup(option)) == NULL) { 356 err(EXIT_FAILURE, "Allocating memory for copy of option string"); 357 } 358 359 for (val = var; *val; val++) 360 if (*val == '=') { 361 *val++ = '\0'; 362 break; 363 } 364 retval = set_option_var(options, var, val, buf, len); 365 free(var); 366 return retval; 367 } 368 369 int 370 set_option_var(const option_t *options, const char *var, const char *val, 371 char *buf, size_t len) 372 { 373 char *s; 374 size_t i; 375 376 #define NUM(type) \ 377 if (!*val) { \ 378 *(type *)options[i].value = 1; \ 379 break; \ 380 } \ 381 *(type *)options[i].value = (type)strsuftoll(options[i].desc, val, \ 382 options[i].minimum, options[i].maximum); break 383 384 for (i = 0; options[i].name != NULL; i++) { 385 if (var[1] == '\0') { 386 if (options[i].letter != var[0]) 387 continue; 388 } else if (strcmp(options[i].name, var) != 0) 389 continue; 390 switch (options[i].type) { 391 case OPT_BOOL: 392 *(bool *)options[i].value = 1; 393 break; 394 case OPT_STRARRAY: 395 strlcpy((void *)options[i].value, val, (size_t) 396 options[i].maximum); 397 break; 398 case OPT_STRPTR: 399 if ((s = strdup(val)) == NULL) 400 err(1, NULL); 401 *(char **)options[i].value = s; 402 break; 403 case OPT_STRBUF: 404 if (buf == NULL) 405 abort(); 406 strlcpy(buf, val, len); 407 break; 408 case OPT_INT64: 409 NUM(uint64_t); 410 case OPT_INT32: 411 NUM(uint32_t); 412 case OPT_INT16: 413 NUM(uint16_t); 414 case OPT_INT8: 415 NUM(uint8_t); 416 default: 417 warnx("Unknown type %d in option %s", options[i].type, 418 val); 419 return 0; 420 } 421 return i; 422 } 423 warnx("Unknown option `%s'", var); 424 return -1; 425 } 426 427 428 static fstype_t * 429 get_fstype(const char *type) 430 { 431 int i; 432 433 for (i = 0; fstypes[i].type != NULL; i++) 434 if (strcmp(fstypes[i].type, type) == 0) 435 return (&fstypes[i]); 436 return (NULL); 437 } 438 439 option_t * 440 copy_opts(const option_t *o) 441 { 442 size_t i; 443 void *rv; 444 445 for (i = 0; o[i].name; i++) 446 continue; 447 i++; 448 if ((rv = calloc(i, sizeof(*o))) == NULL) 449 err(1, "calloc"); 450 return memcpy(rv, o, i * sizeof(*o)); 451 } 452 453 static int 454 get_tstamp(const char *b, struct stat *st) 455 { 456 time_t when; 457 char *eb; 458 long long l; 459 460 if (stat(b, st) != -1) 461 return 0; 462 463 { 464 errno = 0; 465 l = strtoll(b, &eb, 0); 466 if (b == eb || *eb || errno) 467 return -1; 468 when = (time_t)l; 469 } 470 471 st->st_ino = 1; 472 #ifdef HAVE_STRUCT_STAT_BIRTHTIME 473 st->st_birthtime = 474 #endif 475 st->st_mtime = st->st_ctime = st->st_atime = when; 476 return 0; 477 } 478 479 static void 480 usage(fstype_t *fstype, fsinfo_t *fsoptions) 481 { 482 const char *prog; 483 484 prog = getprogname(); 485 fprintf(stderr, 486 "Usage: %s [-xZ] [-B endian] [-b free-blocks] [-d debug-mask]\n" 487 "\t[-F mtree-specfile] [-f free-files] [-M minimum-size] [-m maximum-size]\n" 488 "\t[-N userdb-dir] [-o fs-options] [-R roundup-size] [-S sector-size]\n" 489 "\t[-s image-size] [-T <timestamp/file>] [-t fs-type]\n" 490 "\timage-file directory | manifest [extra-directory ...]\n", 491 prog); 492 493 if (fstype) { 494 size_t i; 495 option_t *o = fsoptions->fs_options; 496 497 fprintf(stderr, "\n%s specific options:\n", fstype->type); 498 for (i = 0; o[i].name != NULL; i++) 499 fprintf(stderr, "\t%c%c%20.20s\t%s\n", 500 o[i].letter ? o[i].letter : ' ', 501 o[i].letter ? ',' : ' ', 502 o[i].name, o[i].desc); 503 } 504 exit(1); 505 } 506