1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2018-2019 HUAWEI, Inc.
4 * http://www.huawei.com/
5 * Created by Li Guifu <[email protected]>
6 */
7 #define _GNU_SOURCE
8 #include <ctype.h>
9 #include <time.h>
10 #include <sys/time.h>
11 #include <stdlib.h>
12 #include <limits.h>
13 #include <libgen.h>
14 #include <sys/stat.h>
15 #include <getopt.h>
16 #include "erofs/config.h"
17 #include "erofs/print.h"
18 #include "erofs/cache.h"
19 #include "erofs/diskbuf.h"
20 #include "erofs/inode.h"
21 #include "erofs/tar.h"
22 #include "erofs/compress.h"
23 #include "erofs/dedupe.h"
24 #include "erofs/xattr.h"
25 #include "erofs/exclude.h"
26 #include "erofs/block_list.h"
27 #include "erofs/compress_hints.h"
28 #include "erofs/blobchunk.h"
29 #include "erofs/fragments.h"
30 #include "erofs/rebuild.h"
31 #include "../lib/liberofs_private.h"
32 #include "../lib/liberofs_uuid.h"
33 #include "../lib/compressor.h"
34
35 static struct option long_options[] = {
36 {"version", no_argument, 0, 'V'},
37 {"help", no_argument, 0, 'h'},
38 {"exclude-path", required_argument, NULL, 2},
39 {"exclude-regex", required_argument, NULL, 3},
40 #ifdef HAVE_LIBSELINUX
41 {"file-contexts", required_argument, NULL, 4},
42 #endif
43 {"force-uid", required_argument, NULL, 5},
44 {"force-gid", required_argument, NULL, 6},
45 {"all-root", no_argument, NULL, 7},
46 #ifndef NDEBUG
47 {"random-pclusterblks", no_argument, NULL, 8},
48 {"random-algorithms", no_argument, NULL, 18},
49 #endif
50 {"max-extent-bytes", required_argument, NULL, 9},
51 {"compress-hints", required_argument, NULL, 10},
52 {"chunksize", required_argument, NULL, 11},
53 {"quiet", no_argument, 0, 12},
54 {"blobdev", required_argument, NULL, 13},
55 {"ignore-mtime", no_argument, NULL, 14},
56 {"preserve-mtime", no_argument, NULL, 15},
57 {"uid-offset", required_argument, NULL, 16},
58 {"gid-offset", required_argument, NULL, 17},
59 {"tar", optional_argument, NULL, 20},
60 {"aufs", no_argument, NULL, 21},
61 {"mount-point", required_argument, NULL, 512},
62 {"xattr-prefix", required_argument, NULL, 19},
63 #ifdef WITH_ANDROID
64 {"product-out", required_argument, NULL, 513},
65 {"fs-config-file", required_argument, NULL, 514},
66 {"block-list-file", required_argument, NULL, 515},
67 #endif
68 {"ovlfs-strip", optional_argument, NULL, 516},
69 {"offset", required_argument, NULL, 517},
70 #ifdef HAVE_ZLIB
71 {"gzip", no_argument, NULL, 518},
72 {"ungzip", optional_argument, NULL, 518},
73 #endif
74 #ifdef HAVE_LIBLZMA
75 {"unlzma", optional_argument, NULL, 519},
76 {"unxz", optional_argument, NULL, 519},
77 #endif
78 #ifdef EROFS_MT_ENABLED
79 {"workers", required_argument, NULL, 520},
80 #endif
81 {"zfeature-bits", required_argument, NULL, 521},
82 {"clean", optional_argument, NULL, 522},
83 {"incremental", optional_argument, NULL, 523},
84 {"root-xattr-isize", required_argument, NULL, 524},
85 {"mkfs-time", no_argument, NULL, 525},
86 {"all-time", no_argument, NULL, 526},
87 {0, 0, 0, 0},
88 };
89
print_available_compressors(FILE * f,const char * delim)90 static void print_available_compressors(FILE *f, const char *delim)
91 {
92 int i = 0;
93 bool comma = false;
94 const struct erofs_algorithm *s;
95
96 while ((s = z_erofs_list_available_compressors(&i)) != NULL) {
97 if (comma)
98 fputs(delim, f);
99 fputs(s->name, f);
100 comma = true;
101 }
102 fputc('\n', f);
103 }
104
usage(int argc,char ** argv)105 static void usage(int argc, char **argv)
106 {
107 int i = 0;
108 const struct erofs_algorithm *s;
109
110 // " 1 2 3 4 5 6 7 8 "
111 // "12345678901234567890123456789012345678901234567890123456789012345678901234567890\n"
112 printf(
113 "Usage: %s [OPTIONS] FILE SOURCE(s)\n"
114 "Generate EROFS image (FILE) from SOURCE(s).\n"
115 "\n"
116 "General options:\n"
117 " -V, --version print the version number of mkfs.erofs and exit\n"
118 " -h, --help display this help and exit\n"
119 "\n"
120 " -b# set block size to # (# = page size by default)\n"
121 " -d<0-9> set output verbosity; 0=quiet, 9=verbose (default=%i)\n"
122 " -x# set xattr tolerance to # (< 0, disable xattrs; default 2)\n"
123 " -zX[,level=Y] X=compressor (Y=compression level, Z=dictionary size, optional)\n"
124 " [,dictsize=Z] alternative compressors can be separated by colons(:)\n"
125 " [:...] supported compressors and their option ranges are:\n",
126 argv[0], EROFS_WARN);
127 while ((s = z_erofs_list_available_compressors(&i)) != NULL) {
128 const char spaces[] = " ";
129
130 printf("%s%s\n", spaces, s->name);
131 if (s->c->setlevel) {
132 if (!strcmp(s->name, "lzma"))
133 /* A little kludge to show the range as disjointed
134 * "0-9,100-109" instead of a continuous "0-109", and to
135 * state what those two subranges respectively mean. */
136 printf("%s [,level=<0-9,100-109>]\t0-9=normal, 100-109=extreme (default=%i)\n",
137 spaces, s->c->default_level);
138 else
139 printf("%s [,level=<0-%i>]\t\t(default=%i)\n",
140 spaces, s->c->best_level, s->c->default_level);
141 }
142 if (s->c->setdictsize) {
143 if (s->c->default_dictsize)
144 printf("%s [,dictsize=<dictsize>]\t(default=%u, max=%u)\n",
145 spaces, s->c->default_dictsize, s->c->max_dictsize);
146 else
147 printf("%s [,dictsize=<dictsize>]\t(default=<auto>, max=%u)\n",
148 spaces, s->c->max_dictsize);
149 }
150 }
151 printf(
152 " -C# specify the size of compress physical cluster in bytes\n"
153 " -EX[,...] X=extended options\n"
154 " -L volume-label set the volume label (maximum 16)\n"
155 " -T# specify a fixed UNIX timestamp # as build time\n"
156 " --all-time the timestamp is also applied to all files (default)\n"
157 " --mkfs-time the timestamp is applied as build time only\n"
158 " -UX use a given filesystem UUID\n"
159 " --all-root make all files owned by root\n"
160 " --blobdev=X specify an extra device X to store chunked data\n"
161 " --chunksize=# generate chunk-based files with #-byte chunks\n"
162 " --clean=X run full clean build (default) or:\n"
163 " --incremental=X run incremental build\n"
164 " (X = data|rvsp; data=full data, rvsp=space is allocated\n"
165 " and filled with zeroes)\n"
166 " --compress-hints=X specify a file to configure per-file compression strategy\n"
167 " --exclude-path=X avoid including file X (X = exact literal path)\n"
168 " --exclude-regex=X avoid including files that match X (X = regular expression)\n"
169 #ifdef HAVE_LIBSELINUX
170 " --file-contexts=X specify a file contexts file to setup selinux labels\n"
171 #endif
172 " --force-uid=# set all file uids to # (# = UID)\n"
173 " --force-gid=# set all file gids to # (# = GID)\n"
174 " --uid-offset=# add offset # to all file uids (# = id offset)\n"
175 " --gid-offset=# add offset # to all file gids (# = id offset)\n"
176 " --ignore-mtime use build time instead of strict per-file modification time\n"
177 " --max-extent-bytes=# set maximum decompressed extent size # in bytes\n"
178 " --mount-point=X X=prefix of target fs path (default: /)\n"
179 " --preserve-mtime keep per-file modification time strictly\n"
180 " --offset=# skip # bytes at the beginning of IMAGE.\n"
181 " --root-xattr-isize=# ensure the inline xattr size of the root directory is # bytes at least\n"
182 " --aufs replace aufs special files with overlayfs metadata\n"
183 " --tar=X generate a full or index-only image from a tarball(-ish) source\n"
184 " (X = f|i|headerball; f=full mode, i=index mode,\n"
185 " headerball=file data is omited in the source stream)\n"
186 " --ovlfs-strip=<0,1> strip overlayfs metadata in the target image (e.g. whiteouts)\n"
187 " --quiet quiet execution (do not write anything to standard output.)\n"
188 #ifndef NDEBUG
189 " --random-pclusterblks randomize pclusterblks for big pcluster (debugging only)\n"
190 " --random-algorithms randomize per-file algorithms (debugging only)\n"
191 #endif
192 #ifdef HAVE_ZLIB
193 " --ungzip[=X] try to filter the tarball stream through gzip\n"
194 " (and optionally dump the raw stream to X together)\n"
195 #endif
196 #ifdef HAVE_LIBLZMA
197 " --unxz[=X] try to filter the tarball stream through xz/lzma/lzip\n"
198 " (and optionally dump the raw stream to X together)\n"
199 #endif
200 #ifdef EROFS_MT_ENABLED
201 " --workers=# set the number of worker threads to # (default: %u)\n"
202 #endif
203 " --xattr-prefix=X X=extra xattr name prefix\n"
204 " --zfeature-bits=# toggle filesystem compression features according to given bits #\n"
205 #ifdef WITH_ANDROID
206 "\n"
207 "Android-specific options:\n"
208 " --product-out=X X=product_out directory\n"
209 " --fs-config-file=X X=fs_config file\n"
210 " --block-list-file=X X=block_list file\n"
211 #endif
212 #ifdef EROFS_MT_ENABLED
213 , erofs_get_available_processors() /* --workers= */
214 #endif
215 );
216 }
217
version(void)218 static void version(void)
219 {
220 printf("mkfs.erofs (erofs-utils) %s\navailable compressors: ",
221 cfg.c_version);
222 print_available_compressors(stdout, ", ");
223 }
224
225 static unsigned int pclustersize_packed, pclustersize_max;
226 static struct erofs_tarfile erofstar = {
227 .global.xattrs = LIST_HEAD_INIT(erofstar.global.xattrs)
228 };
229 static bool tar_mode, rebuild_mode, incremental_mode;
230
231 enum {
232 EROFS_MKFS_DATA_IMPORT_DEFAULT,
233 EROFS_MKFS_DATA_IMPORT_FULLDATA,
234 EROFS_MKFS_DATA_IMPORT_RVSP,
235 EROFS_MKFS_DATA_IMPORT_SPARSE,
236 } dataimport_mode;
237
238 static unsigned int rebuild_src_count;
239 static LIST_HEAD(rebuild_src_list);
240 static u8 fixeduuid[16];
241 static bool valid_fixeduuid;
242
erofs_mkfs_feat_set_legacy_compress(bool en,const char * val,unsigned int vallen)243 static int erofs_mkfs_feat_set_legacy_compress(bool en, const char *val,
244 unsigned int vallen)
245 {
246 if (vallen)
247 return -EINVAL;
248 /* disable compacted indexes and 0padding */
249 cfg.c_legacy_compress = en;
250 return 0;
251 }
252
erofs_mkfs_feat_set_ztailpacking(bool en,const char * val,unsigned int vallen)253 static int erofs_mkfs_feat_set_ztailpacking(bool en, const char *val,
254 unsigned int vallen)
255 {
256 if (vallen)
257 return -EINVAL;
258 cfg.c_ztailpacking = en;
259 return 0;
260 }
261
erofs_mkfs_feat_set_fragments(bool en,const char * val,unsigned int vallen)262 static int erofs_mkfs_feat_set_fragments(bool en, const char *val,
263 unsigned int vallen)
264 {
265 if (!en) {
266 if (vallen)
267 return -EINVAL;
268 cfg.c_fragments = false;
269 return 0;
270 }
271
272 if (vallen) {
273 char *endptr;
274 u64 i = strtoull(val, &endptr, 0);
275
276 if (endptr - val != vallen) {
277 erofs_err("invalid pcluster size %s for the packed file %s", val);
278 return -EINVAL;
279 }
280 pclustersize_packed = i;
281 }
282 cfg.c_fragments = true;
283 return 0;
284 }
285
erofs_mkfs_feat_set_all_fragments(bool en,const char * val,unsigned int vallen)286 static int erofs_mkfs_feat_set_all_fragments(bool en, const char *val,
287 unsigned int vallen)
288 {
289 cfg.c_all_fragments = en;
290 return erofs_mkfs_feat_set_fragments(en, val, vallen);
291 }
292
erofs_mkfs_feat_set_dedupe(bool en,const char * val,unsigned int vallen)293 static int erofs_mkfs_feat_set_dedupe(bool en, const char *val,
294 unsigned int vallen)
295 {
296 if (vallen)
297 return -EINVAL;
298 cfg.c_dedupe = en;
299 return 0;
300 }
301
302 static struct {
303 char *feat;
304 int (*set)(bool en, const char *val, unsigned int len);
305 } z_erofs_mkfs_features[] = {
306 {"legacy-compress", erofs_mkfs_feat_set_legacy_compress},
307 {"ztailpacking", erofs_mkfs_feat_set_ztailpacking},
308 {"fragments", erofs_mkfs_feat_set_fragments},
309 {"all-fragments", erofs_mkfs_feat_set_all_fragments},
310 {"dedupe", erofs_mkfs_feat_set_dedupe},
311 {NULL, NULL},
312 };
313
parse_extended_opts(const char * opts)314 static int parse_extended_opts(const char *opts)
315 {
316 #define MATCH_EXTENTED_OPT(opt, token, keylen) \
317 (keylen == strlen(opt) && !memcmp(token, opt, keylen))
318
319 const char *token, *next, *tokenend, *value __maybe_unused;
320 unsigned int keylen, vallen;
321
322 value = NULL;
323 for (token = opts; *token != '\0'; token = next) {
324 bool clear = false;
325 const char *p = strchr(token, ',');
326
327 next = NULL;
328 if (p) {
329 next = p + 1;
330 } else {
331 p = token + strlen(token);
332 next = p;
333 }
334
335 tokenend = memchr(token, '=', p - token);
336 if (tokenend) {
337 keylen = tokenend - token;
338 vallen = p - tokenend - 1;
339 if (!vallen)
340 return -EINVAL;
341
342 value = tokenend + 1;
343 } else {
344 keylen = p - token;
345 vallen = 0;
346 }
347
348 if (token[0] == '^') {
349 if (keylen < 2)
350 return -EINVAL;
351 ++token;
352 --keylen;
353 clear = true;
354 }
355
356 if (MATCH_EXTENTED_OPT("force-inode-compact", token, keylen)) {
357 if (vallen)
358 return -EINVAL;
359 cfg.c_force_inodeversion = FORCE_INODE_COMPACT;
360 cfg.c_ignore_mtime = true;
361 } else if (MATCH_EXTENTED_OPT("force-inode-extended", token, keylen)) {
362 if (vallen)
363 return -EINVAL;
364 cfg.c_force_inodeversion = FORCE_INODE_EXTENDED;
365 } else if (MATCH_EXTENTED_OPT("nosbcrc", token, keylen)) {
366 if (vallen)
367 return -EINVAL;
368 erofs_sb_clear_sb_chksum(&g_sbi);
369 } else if (MATCH_EXTENTED_OPT("noinline_data", token, keylen)) {
370 if (vallen)
371 return -EINVAL;
372 cfg.c_inline_data = false;
373 } else if (MATCH_EXTENTED_OPT("inline_data", token, keylen)) {
374 if (vallen)
375 return -EINVAL;
376 cfg.c_inline_data = !clear;
377 } else if (MATCH_EXTENTED_OPT("force-inode-blockmap", token, keylen)) {
378 if (vallen)
379 return -EINVAL;
380 cfg.c_force_chunkformat = FORCE_INODE_BLOCK_MAP;
381 } else if (MATCH_EXTENTED_OPT("force-chunk-indexes", token, keylen)) {
382 if (vallen)
383 return -EINVAL;
384 cfg.c_force_chunkformat = FORCE_INODE_CHUNK_INDEXES;
385 } else if (MATCH_EXTENTED_OPT("xattr-name-filter", token, keylen)) {
386 if (vallen)
387 return -EINVAL;
388 cfg.c_xattr_name_filter = !clear;
389 } else {
390 int i, err;
391
392 for (i = 0; z_erofs_mkfs_features[i].feat; ++i) {
393 if (!MATCH_EXTENTED_OPT(z_erofs_mkfs_features[i].feat,
394 token, keylen))
395 continue;
396 err = z_erofs_mkfs_features[i].set(!clear, value, vallen);
397 if (err)
398 return err;
399 break;
400 }
401
402 if (!z_erofs_mkfs_features[i].feat) {
403 erofs_err("unknown extended option %.*s",
404 (int)(p - token), token);
405 return -EINVAL;
406 }
407 }
408 }
409 return 0;
410 }
411
mkfs_apply_zfeature_bits(uintmax_t bits)412 static int mkfs_apply_zfeature_bits(uintmax_t bits)
413 {
414 int i;
415
416 for (i = 0; bits; ++i) {
417 int err;
418
419 if (!z_erofs_mkfs_features[i].feat) {
420 erofs_err("unsupported zfeature bit %u", i);
421 return -EINVAL;
422 }
423 err = z_erofs_mkfs_features[i].set(bits & 1, NULL, 0);
424 if (err) {
425 erofs_err("failed to apply zfeature %s",
426 z_erofs_mkfs_features[i].feat);
427 return err;
428 }
429 bits >>= 1;
430 }
431 return 0;
432 }
433
mkfs_parse_tar_cfg(char * cfg)434 static void mkfs_parse_tar_cfg(char *cfg)
435 {
436 char *p;
437
438 tar_mode = true;
439 if (!cfg)
440 return;
441 p = strchr(cfg, ',');
442 if (p) {
443 *p = '\0';
444 if ((*++p) != '\0')
445 erofstar.mapfile = strdup(p);
446 }
447 if (!strcmp(cfg, "headerball"))
448 erofstar.headeronly_mode = true;
449
450 if (erofstar.headeronly_mode || !strcmp(optarg, "i") ||
451 !strcmp(optarg, "0"))
452 erofstar.index_mode = true;
453 }
454
mkfs_parse_one_compress_alg(char * alg,struct erofs_compr_opts * copts)455 static int mkfs_parse_one_compress_alg(char *alg,
456 struct erofs_compr_opts *copts)
457 {
458 char *p, *q, *opt, *endptr;
459
460 copts->level = -1;
461 copts->dict_size = 0;
462
463 p = strchr(alg, ',');
464 if (p) {
465 copts->alg = strndup(alg, p - alg);
466
467 /* support old '-zlzma,9' form */
468 if (isdigit(*(p + 1))) {
469 copts->level = strtol(p + 1, &endptr, 10);
470 if (*endptr && *endptr != ',') {
471 erofs_err("invalid compression level %s",
472 p + 1);
473 return -EINVAL;
474 }
475 return 0;
476 }
477 } else {
478 copts->alg = strdup(alg);
479 return 0;
480 }
481
482 opt = p + 1;
483 while (opt) {
484 q = strchr(opt, ',');
485 if (q)
486 *q = '\0';
487
488 if ((p = strstr(opt, "level="))) {
489 p += strlen("level=");
490 copts->level = strtol(p, &endptr, 10);
491 if ((endptr == p) || (*endptr && *endptr != ',')) {
492 erofs_err("invalid compression level %s", p);
493 return -EINVAL;
494 }
495 } else if ((p = strstr(opt, "dictsize="))) {
496 p += strlen("dictsize=");
497 copts->dict_size = strtoul(p, &endptr, 10);
498 if (*endptr == 'k' || *endptr == 'K')
499 copts->dict_size <<= 10;
500 else if (*endptr == 'm' || *endptr == 'M')
501 copts->dict_size <<= 20;
502 else if ((endptr == p) || (*endptr && *endptr != ',')) {
503 erofs_err("invalid compression dictsize %s", p);
504 return -EINVAL;
505 }
506 } else {
507 erofs_err("invalid compression option %s", opt);
508 return -EINVAL;
509 }
510
511 opt = q ? q + 1 : NULL;
512 }
513
514 return 0;
515 }
516
mkfs_parse_compress_algs(char * algs)517 static int mkfs_parse_compress_algs(char *algs)
518 {
519 unsigned int i;
520 char *s;
521 int ret;
522
523 for (s = strtok(algs, ":"), i = 0; s; s = strtok(NULL, ":"), ++i) {
524 if (i >= EROFS_MAX_COMPR_CFGS - 1) {
525 erofs_err("too many algorithm types");
526 return -EINVAL;
527 }
528
529 ret = mkfs_parse_one_compress_alg(s, &cfg.c_compr_opts[i]);
530 if (ret)
531 return ret;
532 }
533 return 0;
534 }
535
erofs_rebuild_cleanup(void)536 static void erofs_rebuild_cleanup(void)
537 {
538 struct erofs_sb_info *src, *n;
539
540 list_for_each_entry_safe(src, n, &rebuild_src_list, list) {
541 list_del(&src->list);
542 erofs_put_super(src);
543 erofs_dev_close(src);
544 free(src);
545 }
546 rebuild_src_count = 0;
547 }
548
mkfs_parse_options_cfg(int argc,char * argv[])549 static int mkfs_parse_options_cfg(int argc, char *argv[])
550 {
551 char *endptr;
552 int opt, i, err;
553 bool quiet = false;
554 int tarerofs_decoder = 0;
555 bool has_timestamp = false;
556
557 while ((opt = getopt_long(argc, argv, "C:E:L:T:U:b:d:x:z:Vh",
558 long_options, NULL)) != -1) {
559 switch (opt) {
560 case 'z':
561 i = mkfs_parse_compress_algs(optarg);
562 if (i)
563 return i;
564 break;
565
566 case 'b':
567 i = atoi(optarg);
568 if (i < 512 || i > EROFS_MAX_BLOCK_SIZE) {
569 erofs_err("invalid block size %s", optarg);
570 return -EINVAL;
571 }
572 g_sbi.blkszbits = ilog2(i);
573 break;
574
575 case 'd':
576 i = atoi(optarg);
577 if (i < EROFS_MSG_MIN || i > EROFS_MSG_MAX) {
578 erofs_err("invalid debug level %d", i);
579 return -EINVAL;
580 }
581 cfg.c_dbg_lvl = i;
582 break;
583
584 case 'x':
585 i = strtol(optarg, &endptr, 0);
586 if (*endptr != '\0') {
587 erofs_err("invalid xattr tolerance %s", optarg);
588 return -EINVAL;
589 }
590 cfg.c_inline_xattr_tolerance = i;
591 break;
592
593 case 'E':
594 opt = parse_extended_opts(optarg);
595 if (opt)
596 return opt;
597 break;
598
599 case 'L':
600 if (optarg == NULL ||
601 strlen(optarg) > sizeof(g_sbi.volume_name)) {
602 erofs_err("invalid volume label");
603 return -EINVAL;
604 }
605 strncpy(g_sbi.volume_name, optarg,
606 sizeof(g_sbi.volume_name));
607 break;
608
609 case 'T':
610 cfg.c_unix_timestamp = strtoull(optarg, &endptr, 0);
611 if (cfg.c_unix_timestamp == -1 || *endptr != '\0') {
612 erofs_err("invalid UNIX timestamp %s", optarg);
613 return -EINVAL;
614 }
615 has_timestamp = true;
616 break;
617 case 'U':
618 if (erofs_uuid_parse(optarg, fixeduuid)) {
619 erofs_err("invalid UUID %s", optarg);
620 return -EINVAL;
621 }
622 valid_fixeduuid = true;
623 break;
624 case 2:
625 opt = erofs_parse_exclude_path(optarg, false);
626 if (opt) {
627 erofs_err("failed to parse exclude path: %s",
628 erofs_strerror(opt));
629 return opt;
630 }
631 break;
632 case 3:
633 opt = erofs_parse_exclude_path(optarg, true);
634 if (opt) {
635 erofs_err("failed to parse exclude regex: %s",
636 erofs_strerror(opt));
637 return opt;
638 }
639 break;
640
641 case 4:
642 opt = erofs_selabel_open(optarg);
643 if (opt && opt != -EBUSY)
644 return opt;
645 break;
646 case 5:
647 cfg.c_uid = strtoul(optarg, &endptr, 0);
648 if (cfg.c_uid == -1 || *endptr != '\0') {
649 erofs_err("invalid uid %s", optarg);
650 return -EINVAL;
651 }
652 break;
653 case 6:
654 cfg.c_gid = strtoul(optarg, &endptr, 0);
655 if (cfg.c_gid == -1 || *endptr != '\0') {
656 erofs_err("invalid gid %s", optarg);
657 return -EINVAL;
658 }
659 break;
660 case 7:
661 cfg.c_uid = cfg.c_gid = 0;
662 break;
663 #ifndef NDEBUG
664 case 8:
665 cfg.c_random_pclusterblks = true;
666 break;
667 case 18:
668 cfg.c_random_algorithms = true;
669 break;
670 #endif
671 case 9:
672 cfg.c_max_decompressed_extent_bytes =
673 strtoul(optarg, &endptr, 0);
674 if (*endptr != '\0') {
675 erofs_err("invalid maximum uncompressed extent size %s",
676 optarg);
677 return -EINVAL;
678 }
679 break;
680 case 10:
681 cfg.c_compress_hints_file = optarg;
682 break;
683 case 512:
684 cfg.mount_point = optarg;
685 /* all trailing '/' should be deleted */
686 opt = strlen(cfg.mount_point);
687 if (opt && optarg[opt - 1] == '/')
688 optarg[opt - 1] = '\0';
689 break;
690 #ifdef WITH_ANDROID
691 case 513:
692 cfg.target_out_path = optarg;
693 break;
694 case 514:
695 cfg.fs_config_file = optarg;
696 break;
697 case 515:
698 cfg.block_list_file = optarg;
699 break;
700 #endif
701 case 'C':
702 i = strtoull(optarg, &endptr, 0);
703 if (*endptr != '\0') {
704 erofs_err("invalid physical clustersize %s",
705 optarg);
706 return -EINVAL;
707 }
708 pclustersize_max = i;
709 break;
710 case 11:
711 i = strtol(optarg, &endptr, 0);
712 if (*endptr != '\0') {
713 erofs_err("invalid chunksize %s", optarg);
714 return -EINVAL;
715 }
716 cfg.c_chunkbits = ilog2(i);
717 if ((1 << cfg.c_chunkbits) != i) {
718 erofs_err("chunksize %s must be a power of two",
719 optarg);
720 return -EINVAL;
721 }
722 erofs_sb_set_chunked_file(&g_sbi);
723 break;
724 case 12:
725 quiet = true;
726 break;
727 case 13:
728 cfg.c_blobdev_path = optarg;
729 break;
730 case 14:
731 cfg.c_ignore_mtime = true;
732 break;
733 case 15:
734 cfg.c_ignore_mtime = false;
735 break;
736 case 16:
737 errno = 0;
738 cfg.c_uid_offset = strtoll(optarg, &endptr, 0);
739 if (errno || *endptr != '\0') {
740 erofs_err("invalid uid offset %s", optarg);
741 return -EINVAL;
742 }
743 break;
744 case 17:
745 errno = 0;
746 cfg.c_gid_offset = strtoll(optarg, &endptr, 0);
747 if (errno || *endptr != '\0') {
748 erofs_err("invalid gid offset %s", optarg);
749 return -EINVAL;
750 }
751 break;
752 case 19:
753 errno = 0;
754 opt = erofs_xattr_insert_name_prefix(optarg);
755 if (opt) {
756 erofs_err("failed to parse xattr name prefix: %s",
757 erofs_strerror(opt));
758 return opt;
759 }
760 cfg.c_extra_ea_name_prefixes = true;
761 break;
762 case 20:
763 mkfs_parse_tar_cfg(optarg);
764 break;
765 case 21:
766 erofstar.aufs = true;
767 break;
768 case 516:
769 if (!optarg || !strcmp(optarg, "1"))
770 cfg.c_ovlfs_strip = true;
771 else
772 cfg.c_ovlfs_strip = false;
773 break;
774 case 517:
775 g_sbi.bdev.offset = strtoull(optarg, &endptr, 0);
776 if (*endptr != '\0') {
777 erofs_err("invalid disk offset %s", optarg);
778 return -EINVAL;
779 }
780 break;
781 case 518:
782 case 519:
783 if (optarg)
784 erofstar.dumpfile = strdup(optarg);
785 tarerofs_decoder = EROFS_IOS_DECODER_GZIP + (opt - 518);
786 break;
787 #ifdef EROFS_MT_ENABLED
788 case 520: {
789 unsigned int processors;
790
791 cfg.c_mt_workers = strtoul(optarg, &endptr, 0);
792 if (errno || *endptr != '\0') {
793 erofs_err("invalid worker number %s", optarg);
794 return -EINVAL;
795 }
796
797 processors = erofs_get_available_processors();
798 if (cfg.c_mt_workers > processors)
799 erofs_warn("%d workers exceed %d processors, potentially impacting performance.",
800 cfg.c_mt_workers, processors);
801 break;
802 }
803 #endif
804 case 521:
805 i = strtol(optarg, &endptr, 0);
806 if (errno || *endptr != '\0') {
807 erofs_err("invalid zfeature bits %s", optarg);
808 return -EINVAL;
809 }
810 err = mkfs_apply_zfeature_bits(i);
811 if (err)
812 return err;
813 break;
814 case 522:
815 case 523:
816 if (!optarg || !strcmp(optarg, "data")) {
817 dataimport_mode = EROFS_MKFS_DATA_IMPORT_FULLDATA;
818 } else if (!strcmp(optarg, "rvsp")) {
819 dataimport_mode = EROFS_MKFS_DATA_IMPORT_RVSP;
820 } else {
821 dataimport_mode = strtol(optarg, &endptr, 0);
822 if (errno || *endptr != '\0') {
823 erofs_err("invalid --%s=%s",
824 opt == 523 ? "incremental" : "clean", optarg);
825 return -EINVAL;
826 }
827 }
828 incremental_mode = (opt == 523);
829 break;
830 case 524:
831 cfg.c_root_xattr_isize = strtoull(optarg, &endptr, 0);
832 if (*endptr != '\0') {
833 erofs_err("invalid the minimum inline xattr size %s", optarg);
834 return -EINVAL;
835 }
836 break;
837 case 525:
838 cfg.c_timeinherit = TIMESTAMP_NONE;
839 break;
840 case 526:
841 cfg.c_timeinherit = TIMESTAMP_FIXED;
842 break;
843 case 'V':
844 version();
845 exit(0);
846 case 'h':
847 usage(argc, argv);
848 exit(0);
849
850 default: /* '?' */
851 return -EINVAL;
852 }
853 }
854
855 if (cfg.c_blobdev_path && cfg.c_chunkbits < g_sbi.blkszbits) {
856 erofs_err("--blobdev must be used together with --chunksize");
857 return -EINVAL;
858 }
859
860 /* TODO: can be implemented with (deviceslot) mapped_blkaddr */
861 if (cfg.c_blobdev_path &&
862 cfg.c_force_chunkformat == FORCE_INODE_BLOCK_MAP) {
863 erofs_err("--blobdev cannot work with block map currently");
864 return -EINVAL;
865 }
866
867 if (optind >= argc) {
868 erofs_err("missing argument: FILE");
869 return -EINVAL;
870 }
871
872 cfg.c_img_path = strdup(argv[optind++]);
873 if (!cfg.c_img_path)
874 return -ENOMEM;
875
876 if (optind >= argc) {
877 if (!tar_mode) {
878 erofs_err("missing argument: SOURCE(s)");
879 return -EINVAL;
880 } else {
881 int dupfd;
882
883 dupfd = dup(STDIN_FILENO);
884 if (dupfd < 0) {
885 erofs_err("failed to duplicate STDIN_FILENO: %s",
886 strerror(errno));
887 return -errno;
888 }
889 err = erofs_iostream_open(&erofstar.ios, dupfd,
890 tarerofs_decoder);
891 if (err)
892 return err;
893 }
894 } else {
895 struct stat st;
896
897 cfg.c_src_path = realpath(argv[optind++], NULL);
898 if (!cfg.c_src_path) {
899 erofs_err("failed to parse source directory: %s",
900 erofs_strerror(-errno));
901 return -ENOENT;
902 }
903
904 if (tar_mode) {
905 int fd = open(cfg.c_src_path, O_RDONLY);
906
907 if (fd < 0) {
908 erofs_err("failed to open file: %s", cfg.c_src_path);
909 return -errno;
910 }
911 err = erofs_iostream_open(&erofstar.ios, fd,
912 tarerofs_decoder);
913 if (err)
914 return err;
915
916 if (erofstar.dumpfile) {
917 fd = open(erofstar.dumpfile,
918 O_WRONLY | O_CREAT | O_TRUNC, 0644);
919 if (fd < 0) {
920 erofs_err("failed to open dumpfile: %s",
921 erofstar.dumpfile);
922 return -errno;
923 }
924 erofstar.ios.dumpfd = fd;
925 }
926 } else {
927 err = lstat(cfg.c_src_path, &st);
928 if (err)
929 return -errno;
930 if (S_ISDIR(st.st_mode))
931 erofs_set_fs_root(cfg.c_src_path);
932 else
933 rebuild_mode = true;
934 }
935
936 if (rebuild_mode) {
937 char *srcpath = cfg.c_src_path;
938 struct erofs_sb_info *src;
939
940 do {
941 src = calloc(1, sizeof(struct erofs_sb_info));
942 if (!src) {
943 erofs_rebuild_cleanup();
944 return -ENOMEM;
945 }
946
947 err = erofs_dev_open(src, srcpath, O_RDONLY);
948 if (err) {
949 free(src);
950 erofs_rebuild_cleanup();
951 return err;
952 }
953
954 /* extra device index starts from 1 */
955 src->dev = ++rebuild_src_count;
956 list_add(&src->list, &rebuild_src_list);
957 } while (optind < argc && (srcpath = argv[optind++]));
958 } else if (optind < argc) {
959 erofs_err("unexpected argument: %s\n", argv[optind]);
960 return -EINVAL;
961 }
962 }
963 if (quiet) {
964 cfg.c_dbg_lvl = EROFS_ERR;
965 cfg.c_showprogress = false;
966 }
967
968 if (cfg.c_compr_opts[0].alg && erofs_blksiz(&g_sbi) != getpagesize())
969 erofs_warn("Please note that subpage blocksize with compression isn't yet supported in kernel. "
970 "This compressed image will only work with bs = ps = %u bytes",
971 erofs_blksiz(&g_sbi));
972
973 if (pclustersize_max) {
974 if (pclustersize_max < erofs_blksiz(&g_sbi) ||
975 pclustersize_max % erofs_blksiz(&g_sbi)) {
976 erofs_err("invalid physical clustersize %u",
977 pclustersize_max);
978 return -EINVAL;
979 }
980 cfg.c_mkfs_pclustersize_max = pclustersize_max;
981 cfg.c_mkfs_pclustersize_def = cfg.c_mkfs_pclustersize_max;
982 }
983 if (cfg.c_chunkbits && cfg.c_chunkbits < g_sbi.blkszbits) {
984 erofs_err("chunksize %u must be larger than block size",
985 1u << cfg.c_chunkbits);
986 return -EINVAL;
987 }
988
989 if (pclustersize_packed) {
990 if (pclustersize_packed < erofs_blksiz(&g_sbi) ||
991 pclustersize_packed % erofs_blksiz(&g_sbi)) {
992 erofs_err("invalid pcluster size for the packed file %u",
993 pclustersize_packed);
994 return -EINVAL;
995 }
996 cfg.c_mkfs_pclustersize_packed = pclustersize_packed;
997 }
998
999 if (has_timestamp && cfg.c_timeinherit == TIMESTAMP_UNSPECIFIED)
1000 cfg.c_timeinherit = TIMESTAMP_FIXED;
1001 return 0;
1002 }
1003
erofs_mkfs_default_options(void)1004 static void erofs_mkfs_default_options(void)
1005 {
1006 cfg.c_showprogress = true;
1007 cfg.c_legacy_compress = false;
1008 cfg.c_inline_data = true;
1009 cfg.c_xattr_name_filter = true;
1010 #ifdef EROFS_MT_ENABLED
1011 cfg.c_mt_workers = erofs_get_available_processors();
1012 cfg.c_mkfs_segment_size = 16ULL * 1024 * 1024;
1013 #endif
1014 g_sbi.blkszbits = ilog2(min_t(u32, getpagesize(), EROFS_MAX_BLOCK_SIZE));
1015 cfg.c_mkfs_pclustersize_max = erofs_blksiz(&g_sbi);
1016 cfg.c_mkfs_pclustersize_def = cfg.c_mkfs_pclustersize_max;
1017 g_sbi.feature_incompat = EROFS_FEATURE_INCOMPAT_ZERO_PADDING;
1018 g_sbi.feature_compat = EROFS_FEATURE_COMPAT_SB_CHKSUM |
1019 EROFS_FEATURE_COMPAT_MTIME;
1020 }
1021
1022 /* https://reproducible-builds.org/specs/source-date-epoch/ for more details */
parse_source_date_epoch(void)1023 int parse_source_date_epoch(void)
1024 {
1025 char *source_date_epoch;
1026 unsigned long long epoch = -1ULL;
1027 char *endptr;
1028
1029 source_date_epoch = getenv("SOURCE_DATE_EPOCH");
1030 if (!source_date_epoch)
1031 return 0;
1032
1033 epoch = strtoull(source_date_epoch, &endptr, 10);
1034 if (epoch == -1ULL || *endptr != '\0') {
1035 erofs_err("environment variable $SOURCE_DATE_EPOCH %s is invalid",
1036 source_date_epoch);
1037 return -EINVAL;
1038 }
1039
1040 if (cfg.c_force_inodeversion != FORCE_INODE_EXTENDED)
1041 erofs_info("SOURCE_DATE_EPOCH is set, forcely generate extended inodes instead");
1042
1043 cfg.c_force_inodeversion = FORCE_INODE_EXTENDED;
1044 cfg.c_unix_timestamp = epoch;
1045 cfg.c_timeinherit = TIMESTAMP_CLAMPING;
1046 return 0;
1047 }
1048
erofs_show_progs(int argc,char * argv[])1049 void erofs_show_progs(int argc, char *argv[])
1050 {
1051 if (cfg.c_dbg_lvl >= EROFS_WARN)
1052 printf("%s %s\n", basename(argv[0]), cfg.c_version);
1053 }
1054
erofs_mkfs_rebuild_load_trees(struct erofs_inode * root)1055 static int erofs_mkfs_rebuild_load_trees(struct erofs_inode *root)
1056 {
1057 struct erofs_sb_info *src;
1058 unsigned int extra_devices = 0;
1059 erofs_blk_t nblocks;
1060 int ret, idx;
1061 enum erofs_rebuild_datamode datamode;
1062
1063 switch (dataimport_mode) {
1064 case EROFS_MKFS_DATA_IMPORT_DEFAULT:
1065 datamode = EROFS_REBUILD_DATA_BLOB_INDEX;
1066 break;
1067 case EROFS_MKFS_DATA_IMPORT_FULLDATA:
1068 datamode = EROFS_REBUILD_DATA_FULL;
1069 break;
1070 case EROFS_MKFS_DATA_IMPORT_RVSP:
1071 datamode = EROFS_REBUILD_DATA_RESVSP;
1072 break;
1073 default:
1074 return -EINVAL;
1075 }
1076
1077 list_for_each_entry(src, &rebuild_src_list, list) {
1078 ret = erofs_rebuild_load_tree(root, src, datamode);
1079 if (ret) {
1080 erofs_err("failed to load %s", src->devname);
1081 return ret;
1082 }
1083 if (src->extra_devices > 1) {
1084 erofs_err("%s: unsupported number %u of extra devices",
1085 src->devname, src->extra_devices);
1086 return -EOPNOTSUPP;
1087 }
1088 extra_devices += src->extra_devices;
1089 }
1090
1091 if (datamode != EROFS_REBUILD_DATA_BLOB_INDEX)
1092 return 0;
1093
1094 if (extra_devices != rebuild_src_count) {
1095 erofs_err("extra_devices(%u) is mismatched with source images(%u)",
1096 extra_devices, rebuild_src_count);
1097 return -EOPNOTSUPP;
1098 }
1099
1100 ret = erofs_mkfs_init_devices(&g_sbi, rebuild_src_count);
1101 if (ret)
1102 return ret;
1103
1104 list_for_each_entry(src, &rebuild_src_list, list) {
1105 u8 *tag = NULL;
1106
1107 if (extra_devices) {
1108 nblocks = src->devs[0].blocks;
1109 tag = src->devs[0].tag;
1110 } else {
1111 nblocks = src->primarydevice_blocks;
1112 }
1113 DBG_BUGON(src->dev < 1);
1114 idx = src->dev - 1;
1115 g_sbi.devs[idx].blocks = nblocks;
1116 if (tag && *tag)
1117 memcpy(g_sbi.devs[idx].tag, tag, sizeof(g_sbi.devs[0].tag));
1118 else
1119 /* convert UUID of the source image to a hex string */
1120 sprintf((char *)g_sbi.devs[idx].tag,
1121 "%04x%04x%04x%04x%04x%04x%04x%04x",
1122 (src->uuid[0] << 8) | src->uuid[1],
1123 (src->uuid[2] << 8) | src->uuid[3],
1124 (src->uuid[4] << 8) | src->uuid[5],
1125 (src->uuid[6] << 8) | src->uuid[7],
1126 (src->uuid[8] << 8) | src->uuid[9],
1127 (src->uuid[10] << 8) | src->uuid[11],
1128 (src->uuid[12] << 8) | src->uuid[13],
1129 (src->uuid[14] << 8) | src->uuid[15]);
1130 }
1131 return 0;
1132 }
1133
erofs_mkfs_showsummaries(erofs_blk_t nblocks)1134 static void erofs_mkfs_showsummaries(erofs_blk_t nblocks)
1135 {
1136 char uuid_str[37] = {};
1137 char *incr = incremental_mode ? "new" : "total";
1138
1139 if (!(cfg.c_dbg_lvl > EROFS_ERR && cfg.c_showprogress))
1140 return;
1141
1142 erofs_uuid_unparse_lower(g_sbi.uuid, uuid_str);
1143
1144 fprintf(stdout, "------\nFilesystem UUID: %s\n"
1145 "Filesystem total blocks: %u (of %u-byte blocks)\n"
1146 "Filesystem total inodes: %llu\n"
1147 "Filesystem %s metadata blocks: %u\n"
1148 "Filesystem %s deduplicated bytes (of source files): %llu\n",
1149 uuid_str, nblocks, 1U << g_sbi.blkszbits, g_sbi.inos | 0ULL,
1150 incr, erofs_total_metablocks(g_sbi.bmgr),
1151 incr, g_sbi.saved_by_deduplication | 0ULL);
1152 }
1153
main(int argc,char ** argv)1154 int main(int argc, char **argv)
1155 {
1156 int err = 0;
1157 struct erofs_buffer_head *sb_bh;
1158 struct erofs_inode *root = NULL;
1159 erofs_blk_t nblocks = 0;
1160 struct timeval t;
1161 FILE *packedfile = NULL;
1162 FILE *blklst = NULL;
1163 u32 crc;
1164
1165 erofs_init_configure();
1166 erofs_mkfs_default_options();
1167
1168 err = mkfs_parse_options_cfg(argc, argv);
1169 erofs_show_progs(argc, argv);
1170 if (err) {
1171 if (err == -EINVAL)
1172 fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
1173 return 1;
1174 }
1175
1176 err = parse_source_date_epoch();
1177 if (err) {
1178 fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
1179 return 1;
1180 }
1181
1182 if (cfg.c_unix_timestamp != -1) {
1183 g_sbi.build_time = cfg.c_unix_timestamp;
1184 g_sbi.build_time_nsec = 0;
1185 } else if (!gettimeofday(&t, NULL)) {
1186 g_sbi.build_time = t.tv_sec;
1187 g_sbi.build_time_nsec = t.tv_usec;
1188 }
1189
1190 err = erofs_dev_open(&g_sbi, cfg.c_img_path, O_RDWR |
1191 (incremental_mode ? 0 : O_TRUNC));
1192 if (err) {
1193 fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
1194 return 1;
1195 }
1196
1197 #ifdef WITH_ANDROID
1198 if (cfg.fs_config_file &&
1199 load_canned_fs_config(cfg.fs_config_file) < 0) {
1200 erofs_err("failed to load fs config %s", cfg.fs_config_file);
1201 return 1;
1202 }
1203
1204 if (cfg.block_list_file) {
1205 blklst = fopen(cfg.block_list_file, "w");
1206 if (!blklst || erofs_blocklist_open(blklst, false)) {
1207 erofs_err("failed to open %s", cfg.block_list_file);
1208 return 1;
1209 }
1210 }
1211 #endif
1212 erofs_show_config();
1213 if (cfg.c_fragments || cfg.c_extra_ea_name_prefixes) {
1214 if (!cfg.c_mkfs_pclustersize_packed)
1215 cfg.c_mkfs_pclustersize_packed = cfg.c_mkfs_pclustersize_def;
1216
1217 packedfile = erofs_packedfile_init();
1218 if (IS_ERR(packedfile)) {
1219 erofs_err("failed to initialize packedfile");
1220 return 1;
1221 }
1222 }
1223
1224 if (cfg.c_fragments) {
1225 err = z_erofs_fragments_init();
1226 if (err) {
1227 erofs_err("failed to initialize fragments");
1228 return 1;
1229 }
1230 }
1231
1232 #ifndef NDEBUG
1233 if (cfg.c_random_pclusterblks)
1234 srand(time(NULL));
1235 #endif
1236 if (tar_mode) {
1237 if (dataimport_mode == EROFS_MKFS_DATA_IMPORT_RVSP)
1238 erofstar.rvsp_mode = true;
1239 erofstar.dev = rebuild_src_count + 1;
1240
1241 if (erofstar.mapfile) {
1242 blklst = fopen(erofstar.mapfile, "w");
1243 if (!blklst || erofs_blocklist_open(blklst, true)) {
1244 err = -errno;
1245 erofs_err("failed to open %s", erofstar.mapfile);
1246 goto exit;
1247 }
1248 } else if (erofstar.index_mode) {
1249 /*
1250 * If mapfile is unspecified for tarfs index mode,
1251 * 512-byte block size is enforced here.
1252 */
1253 g_sbi.blkszbits = 9;
1254 }
1255 }
1256
1257 if (rebuild_mode) {
1258 struct erofs_sb_info *src;
1259
1260 erofs_warn("EXPERIMENTAL rebuild mode in use. Use at your own risk!");
1261
1262 src = list_first_entry(&rebuild_src_list, struct erofs_sb_info, list);
1263 if (!src)
1264 goto exit;
1265 err = erofs_read_superblock(src);
1266 if (err) {
1267 erofs_err("failed to read superblock of %s", src->devname);
1268 goto exit;
1269 }
1270 g_sbi.blkszbits = src->blkszbits;
1271 }
1272
1273 if (!incremental_mode) {
1274 g_sbi.bmgr = erofs_buffer_init(&g_sbi, 0);
1275 if (!g_sbi.bmgr) {
1276 err = -ENOMEM;
1277 goto exit;
1278 }
1279 sb_bh = erofs_reserve_sb(g_sbi.bmgr);
1280 if (IS_ERR(sb_bh)) {
1281 err = PTR_ERR(sb_bh);
1282 goto exit;
1283 }
1284 } else {
1285 union {
1286 struct stat st;
1287 erofs_blk_t startblk;
1288 } u;
1289
1290 erofs_warn("EXPERIMENTAL incremental build in use. Use at your own risk!");
1291 err = erofs_read_superblock(&g_sbi);
1292 if (err) {
1293 erofs_err("failed to read superblock of %s", g_sbi.devname);
1294 goto exit;
1295 }
1296
1297 err = erofs_io_fstat(&g_sbi.bdev, &u.st);
1298 if (!err && S_ISREG(u.st.st_mode))
1299 u.startblk = DIV_ROUND_UP(u.st.st_size, erofs_blksiz(&g_sbi));
1300 else
1301 u.startblk = g_sbi.primarydevice_blocks;
1302 g_sbi.bmgr = erofs_buffer_init(&g_sbi, u.startblk);
1303 if (!g_sbi.bmgr) {
1304 err = -ENOMEM;
1305 goto exit;
1306 }
1307 sb_bh = NULL;
1308 }
1309
1310 /* Use the user-defined UUID or generate one for clean builds */
1311 if (valid_fixeduuid)
1312 memcpy(g_sbi.uuid, fixeduuid, sizeof(g_sbi.uuid));
1313 else if (!incremental_mode)
1314 erofs_uuid_generate(g_sbi.uuid);
1315
1316 if (tar_mode && !erofstar.index_mode) {
1317 err = erofs_diskbuf_init(1);
1318 if (err) {
1319 erofs_err("failed to initialize diskbuf: %s",
1320 strerror(-err));
1321 goto exit;
1322 }
1323 }
1324
1325 err = erofs_load_compress_hints(&g_sbi);
1326 if (err) {
1327 erofs_err("failed to load compress hints %s",
1328 cfg.c_compress_hints_file);
1329 goto exit;
1330 }
1331
1332 err = z_erofs_compress_init(&g_sbi, sb_bh);
1333 if (err) {
1334 erofs_err("failed to initialize compressor: %s",
1335 erofs_strerror(err));
1336 goto exit;
1337 }
1338
1339 if (cfg.c_dedupe) {
1340 if (!cfg.c_compr_opts[0].alg) {
1341 erofs_err("Compression is not enabled. Turn on chunk-based data deduplication instead.");
1342 cfg.c_chunkbits = g_sbi.blkszbits;
1343 } else {
1344 err = z_erofs_dedupe_init(erofs_blksiz(&g_sbi));
1345 if (err) {
1346 erofs_err("failed to initialize deduplication: %s",
1347 erofs_strerror(err));
1348 goto exit;
1349 }
1350 }
1351 }
1352
1353 if (cfg.c_chunkbits) {
1354 err = erofs_blob_init(cfg.c_blobdev_path, 1 << cfg.c_chunkbits);
1355 if (err)
1356 return 1;
1357 }
1358
1359 if (((erofstar.index_mode && !erofstar.headeronly_mode) &&
1360 !erofstar.mapfile) || cfg.c_blobdev_path) {
1361 err = erofs_mkfs_init_devices(&g_sbi, 1);
1362 if (err) {
1363 erofs_err("failed to generate device table: %s",
1364 erofs_strerror(err));
1365 goto exit;
1366 }
1367 }
1368
1369 erofs_inode_manager_init();
1370
1371 if (tar_mode) {
1372 root = erofs_rebuild_make_root(&g_sbi);
1373 if (IS_ERR(root)) {
1374 err = PTR_ERR(root);
1375 goto exit;
1376 }
1377
1378 while (!(err = tarerofs_parse_tar(root, &erofstar)));
1379
1380 if (err < 0)
1381 goto exit;
1382
1383 err = erofs_rebuild_dump_tree(root, incremental_mode);
1384 if (err < 0)
1385 goto exit;
1386 } else if (rebuild_mode) {
1387 root = erofs_rebuild_make_root(&g_sbi);
1388 if (IS_ERR(root)) {
1389 err = PTR_ERR(root);
1390 goto exit;
1391 }
1392
1393 err = erofs_mkfs_rebuild_load_trees(root);
1394 if (err)
1395 goto exit;
1396 err = erofs_rebuild_dump_tree(root, incremental_mode);
1397 if (err)
1398 goto exit;
1399 } else {
1400 err = erofs_build_shared_xattrs_from_path(&g_sbi, cfg.c_src_path);
1401 if (err) {
1402 erofs_err("failed to build shared xattrs: %s",
1403 erofs_strerror(err));
1404 goto exit;
1405 }
1406
1407 if (cfg.c_extra_ea_name_prefixes)
1408 erofs_xattr_write_name_prefixes(&g_sbi, packedfile);
1409
1410 root = erofs_mkfs_build_tree_from_path(&g_sbi, cfg.c_src_path);
1411 if (IS_ERR(root)) {
1412 err = PTR_ERR(root);
1413 goto exit;
1414 }
1415 }
1416
1417 if (erofstar.index_mode && g_sbi.extra_devices && !erofstar.mapfile)
1418 g_sbi.devs[0].blocks = BLK_ROUND_UP(&g_sbi, erofstar.offset);
1419
1420 if (erofs_sb_has_fragments(&g_sbi)) {
1421 erofs_update_progressinfo("Handling packed data ...");
1422 err = erofs_flush_packed_inode(&g_sbi);
1423 if (err)
1424 goto exit;
1425 }
1426
1427 if (erofstar.index_mode || cfg.c_chunkbits || g_sbi.extra_devices) {
1428 err = erofs_mkfs_dump_blobs(&g_sbi);
1429 if (err)
1430 goto exit;
1431 }
1432
1433 /* flush all buffers except for the superblock */
1434 err = erofs_bflush(g_sbi.bmgr, NULL);
1435 if (err)
1436 goto exit;
1437
1438 erofs_fixup_root_inode(root);
1439 erofs_iput(root);
1440 root = NULL;
1441
1442 err = erofs_writesb(&g_sbi, sb_bh, &nblocks);
1443 if (err)
1444 goto exit;
1445
1446 /* flush all remaining buffers */
1447 err = erofs_bflush(g_sbi.bmgr, NULL);
1448 if (err)
1449 goto exit;
1450
1451 err = erofs_dev_resize(&g_sbi, nblocks);
1452
1453 if (!err && erofs_sb_has_sb_chksum(&g_sbi)) {
1454 err = erofs_enable_sb_chksum(&g_sbi, &crc);
1455 if (!err)
1456 erofs_info("superblock checksum 0x%08x written", crc);
1457 }
1458 exit:
1459 if (root)
1460 erofs_iput(root);
1461 z_erofs_compress_exit();
1462 z_erofs_dedupe_exit();
1463 blklst = erofs_blocklist_close();
1464 if (blklst)
1465 fclose(blklst);
1466 erofs_dev_close(&g_sbi);
1467 erofs_cleanup_compress_hints();
1468 erofs_cleanup_exclude_rules();
1469 if (cfg.c_chunkbits)
1470 erofs_blob_exit();
1471 if (cfg.c_fragments)
1472 z_erofs_fragments_exit();
1473 erofs_packedfile_exit();
1474 erofs_xattr_cleanup_name_prefixes();
1475 erofs_rebuild_cleanup();
1476 erofs_diskbuf_exit();
1477 erofs_exit_configure();
1478 if (tar_mode) {
1479 erofs_iostream_close(&erofstar.ios);
1480 if (erofstar.ios.dumpfd >= 0)
1481 close(erofstar.ios.dumpfd);
1482 }
1483
1484 if (err) {
1485 erofs_err("\tCould not format the device : %s\n",
1486 erofs_strerror(err));
1487 return 1;
1488 }
1489 erofs_update_progressinfo("Build completed.\n");
1490 erofs_mkfs_showsummaries(nblocks);
1491 erofs_put_super(&g_sbi);
1492 return 0;
1493 }
1494