]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/makefs/makefs.c
Import OpenCSD v.1.4.0.
[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                 case '?':
277                 default:
278                         usage(fstype, &fsoptions);
279                         /* NOTREACHED */
280
281                 }
282         }
283         if (debug) {
284                 printf("debug mask: 0x%08x\n", debug);
285                 printf("start time: %ld.%ld, %s",
286                     (long)start_time.tv_sec, (long)start_time.tv_nsec,
287                     ctime(&start_time.tv_sec));
288         }
289         argc -= optind;
290         argv += optind;
291
292         if (argc < 2)
293                 usage(fstype, &fsoptions);
294
295         /* -x must be accompanied by -F */
296         if (fsoptions.onlyspec != 0 && specfile == NULL)
297                 errx(1, "-x requires -F mtree-specfile.");
298
299         /* Accept '-' as meaning "read from standard input". */
300         if (strcmp(argv[1], "-") == 0)
301                 sb.st_mode = S_IFREG;
302         else {
303                 if (stat(argv[1], &sb) == -1)
304                         err(1, "Can't stat `%s'", argv[1]);
305         }
306
307         switch (sb.st_mode & S_IFMT) {
308         case S_IFDIR:           /* walk the tree */
309                 subtree = argv[1];
310                 TIMER_START(start);
311                 root = walk_dir(subtree, ".", NULL, NULL);
312                 TIMER_RESULTS(start, "walk_dir");
313                 break;
314         case S_IFREG:           /* read the manifest file */
315                 subtree = ".";
316                 TIMER_START(start);
317                 root = read_mtree(argv[1], NULL);
318                 TIMER_RESULTS(start, "manifest");
319                 break;
320         default:
321                 errx(1, "%s: not a file or directory", argv[1]);
322                 /* NOTREACHED */
323         }
324
325         /* append extra directory */
326         for (i = 2; i < argc; i++) {
327                 if (stat(argv[i], &sb) == -1)
328                         err(1, "Can't stat `%s'", argv[i]);
329                 if (!S_ISDIR(sb.st_mode))
330                         errx(1, "%s: not a directory", argv[i]);
331                 TIMER_START(start);
332                 root = walk_dir(argv[i], ".", NULL, root);
333                 TIMER_RESULTS(start, "walk_dir2");
334         }
335
336         if (specfile) {         /* apply a specfile */
337                 TIMER_START(start);
338                 apply_specfile(specfile, subtree, root, fsoptions.onlyspec);
339                 TIMER_RESULTS(start, "apply_specfile");
340         }
341
342         if (debug & DEBUG_DUMP_FSNODES) {
343                 printf("\nparent: %s\n", subtree);
344                 dump_fsnodes(root);
345                 putchar('\n');
346         }
347
348                                 /* build the file system */
349         TIMER_START(start);
350         fstype->make_fs(argv[0], subtree, root, &fsoptions);
351         TIMER_RESULTS(start, "make_fs");
352
353         free_fsnodes(root);
354
355         exit(0);
356         /* NOTREACHED */
357 }
358
359 int
360 set_option(const option_t *options, const char *option, char *buf, size_t len)
361 {
362         char *var, *val;
363         int retval;
364
365         assert(option != NULL);
366
367         var = estrdup(option);
368         for (val = var; *val; val++)
369                 if (*val == '=') {
370                         *val++ = '\0';
371                         break;
372                 }
373         retval = set_option_var(options, var, val, buf, len);
374         free(var);
375         return retval;
376 }
377
378 int
379 set_option_var(const option_t *options, const char *var, const char *val,
380     char *buf, size_t len)
381 {
382         char *s;
383         size_t i;
384
385 #define NUM(type) \
386         if (!*val) { \
387                 *(type *)options[i].value = 1; \
388                 break; \
389         } \
390         *(type *)options[i].value = (type)strsuftoll(options[i].desc, val, \
391             options[i].minimum, options[i].maximum); break
392
393         for (i = 0; options[i].name != NULL; i++) {
394                 if (var[1] == '\0') {
395                         if (options[i].letter != var[0])
396                                 continue;
397                 } else if (strcmp(options[i].name, var) != 0)
398                         continue;
399                 switch (options[i].type) {
400                 case OPT_BOOL:
401                         *(bool *)options[i].value = 1;
402                         break;
403                 case OPT_STRARRAY:
404                         strlcpy((void *)options[i].value, val, (size_t)
405                             options[i].maximum);
406                         break;
407                 case OPT_STRPTR:
408                         s = estrdup(val);
409                         *(char **)options[i].value = s;
410                         break;
411                 case OPT_STRBUF:
412                         if (buf == NULL)
413                                 abort();
414                         strlcpy(buf, val, len);
415                         break;
416                 case OPT_INT64:
417                         NUM(uint64_t);
418                 case OPT_INT32:
419                         NUM(uint32_t);
420                 case OPT_INT16:
421                         NUM(uint16_t);
422                 case OPT_INT8:
423                         NUM(uint8_t);
424                 default:
425                         warnx("Unknown type %d in option %s", options[i].type,
426                             val);
427                         return 0;
428                 }
429                 return i;
430         }
431         warnx("Unknown option `%s'", var);
432         return -1;
433 }
434
435
436 static fstype_t *
437 get_fstype(const char *type)
438 {
439         int i;
440         
441         for (i = 0; fstypes[i].type != NULL; i++)
442                 if (strcmp(fstypes[i].type, type) == 0)
443                         return (&fstypes[i]);
444         return (NULL);
445 }
446
447 option_t *
448 copy_opts(const option_t *o)
449 {
450         size_t i;
451
452         for (i = 0; o[i].name; i++)
453                 continue;
454         i++;
455         return memcpy(ecalloc(i, sizeof(*o)), o, i * sizeof(*o));
456 }
457
458 static int
459 get_tstamp(const char *b, struct stat *st)
460 {
461         time_t when;
462         char *eb;
463         long long l;
464
465         if (stat(b, st) != -1)
466                 return 0;
467
468         {
469                 errno = 0;
470                 l = strtoll(b, &eb, 0);
471                 if (b == eb || *eb || errno)
472                         return -1;
473                 when = (time_t)l;
474         }
475
476         st->st_ino = 1;
477 #ifdef HAVE_STRUCT_STAT_BIRTHTIME
478         st->st_birthtime =
479 #endif
480         st->st_mtime = st->st_ctime = st->st_atime = when;
481         return 0;
482 }
483
484 static void
485 usage(fstype_t *fstype, fsinfo_t *fsoptions)
486 {
487         const char *prog;
488
489         prog = getprogname();
490         fprintf(stderr,
491 "Usage: %s [-xZ] [-B endian] [-b free-blocks] [-d debug-mask]\n"
492 "\t[-F mtree-specfile] [-f free-files] [-M minimum-size] [-m maximum-size]\n"
493 "\t[-N userdb-dir] [-O offset] [-o fs-options] [-R roundup-size]\n"
494 "\t[-S sector-size] [-s image-size] [-T <timestamp/file>] [-t fs-type]\n"
495 "\timage-file directory | manifest [extra-directory ...]\n",
496             prog);
497
498         if (fstype) {
499                 size_t i;
500                 option_t *o = fsoptions->fs_options;
501
502                 fprintf(stderr, "\n%s specific options:\n", fstype->type);
503                 for (i = 0; o[i].name != NULL; i++)
504                         fprintf(stderr, "\t%c%c%20.20s\t%s\n",
505                             o[i].letter ? o[i].letter : ' ',
506                             o[i].letter ? ',' : ' ',
507                             o[i].name, o[i].desc);
508         }
509         exit(1);
510 }