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