xref: /aosp_15_r20/external/erofs-utils/mkfs/main.c (revision 33b1fccf6a0fada2c2875d400ed01119b7676ee5)
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