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