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