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
main(int argc,char * argv[])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
set_option(const option_t * options,const char * option,char * buf,size_t len)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
set_option_var(const option_t * options,const char * var,const char * val,char * buf,size_t len)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
set_tstamp(fsnode * cur)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 *
get_fstype(const char * type)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 *
copy_opts(const option_t * o)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
get_tstamp(const char * b,struct stat * st)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
usage(fstype_t * fstype,fsinfo_t * fsoptions)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