xref: /aosp_15_r20/external/erofs-utils/dump/main.c (revision 33b1fccf6a0fada2c2875d400ed01119b7676ee5)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2021-2022 HUAWEI, Inc.
4  *             http://www.huawei.com/
5  * Created by Wang Qi <[email protected]>
6  *            Guo Xuenan <[email protected]>
7  */
8 #define _GNU_SOURCE
9 #include <stdlib.h>
10 #include <getopt.h>
11 #include <time.h>
12 #include <sys/stat.h>
13 #include "erofs/print.h"
14 #include "erofs/inode.h"
15 #include "erofs/dir.h"
16 #include "erofs/compress.h"
17 #include "erofs/fragments.h"
18 #include "../lib/liberofs_private.h"
19 #include "../lib/liberofs_uuid.h"
20 
21 
22 struct erofsdump_cfg {
23 	unsigned int totalshow;
24 	bool show_inode;
25 	bool show_extent;
26 	bool show_superblock;
27 	bool show_statistics;
28 	bool show_subdirectories;
29 	erofs_nid_t nid;
30 	const char *inode_path;
31 };
32 static struct erofsdump_cfg dumpcfg;
33 
34 static const char chart_format[] = "%-16s	%-11d %8.2f%% |%-50s|\n";
35 static const char header_format[] = "%-16s %11s %16s |%-50s|\n";
36 static char *file_types[] = {
37 	".txt", ".so", ".xml", ".apk",
38 	".odex", ".vdex", ".oat", ".rc",
39 	".otf", "others",
40 };
41 #define OTHERFILETYPE	ARRAY_SIZE(file_types)
42 /* (1 << FILE_MAX_SIZE_BITS)KB */
43 #define	FILE_MAX_SIZE_BITS	16
44 
45 static const char * const file_category_types[] = {
46 	[EROFS_FT_UNKNOWN] = "unknown type",
47 	[EROFS_FT_REG_FILE] = "regular file",
48 	[EROFS_FT_DIR] = "directory",
49 	[EROFS_FT_CHRDEV] = "char dev",
50 	[EROFS_FT_BLKDEV] = "block dev",
51 	[EROFS_FT_FIFO] = "FIFO file",
52 	[EROFS_FT_SOCK] = "SOCK file",
53 	[EROFS_FT_SYMLINK] = "symlink file",
54 };
55 
56 struct erofs_statistics {
57 	unsigned long files;
58 	unsigned long compressed_files;
59 	unsigned long uncompressed_files;
60 	unsigned long files_total_size;
61 	unsigned long files_total_origin_size;
62 	double compress_rate;
63 
64 	/* [statistics] # of files based on inode_info->flags */
65 	unsigned long file_category_stat[EROFS_FT_MAX];
66 	/* [statistics] # of files based on file name extensions */
67 	unsigned int file_type_stat[OTHERFILETYPE];
68 	/* [statistics] # of files based on the original size of files */
69 	unsigned int file_original_size[FILE_MAX_SIZE_BITS + 1];
70 	/* [statistics] # of files based on the compressed size of files */
71 	unsigned int file_comp_size[FILE_MAX_SIZE_BITS + 1];
72 };
73 static struct erofs_statistics stats;
74 
75 static struct option long_options[] = {
76 	{"version", no_argument, NULL, 'V'},
77 	{"help", no_argument, NULL, 'h'},
78 	{"nid", required_argument, NULL, 2},
79 	{"device", required_argument, NULL, 3},
80 	{"path", required_argument, NULL, 4},
81 	{"ls", no_argument, NULL, 5},
82 	{"offset", required_argument, NULL, 6},
83 	{0, 0, 0, 0},
84 };
85 
86 struct erofsdump_feature {
87 	bool compat;
88 	u32 flag;
89 	const char *name;
90 };
91 
92 static struct erofsdump_feature feature_lists[] = {
93 	{ true, EROFS_FEATURE_COMPAT_SB_CHKSUM, "sb_csum" },
94 	{ true, EROFS_FEATURE_COMPAT_MTIME, "mtime" },
95 	{ true, EROFS_FEATURE_COMPAT_XATTR_FILTER, "xattr_filter" },
96 	{ false, EROFS_FEATURE_INCOMPAT_ZERO_PADDING, "0padding" },
97 	{ false, EROFS_FEATURE_INCOMPAT_COMPR_CFGS, "compr_cfgs" },
98 	{ false, EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER, "big_pcluster" },
99 	{ false, EROFS_FEATURE_INCOMPAT_CHUNKED_FILE, "chunked_file" },
100 	{ false, EROFS_FEATURE_INCOMPAT_DEVICE_TABLE, "device_table" },
101 	{ false, EROFS_FEATURE_INCOMPAT_ZTAILPACKING, "ztailpacking" },
102 	{ false, EROFS_FEATURE_INCOMPAT_FRAGMENTS, "fragments" },
103 	{ false, EROFS_FEATURE_INCOMPAT_DEDUPE, "dedupe" },
104 	{ false, EROFS_FEATURE_INCOMPAT_XATTR_PREFIXES, "xattr_prefixes" },
105 };
106 
107 static int erofsdump_readdir(struct erofs_dir_context *ctx);
108 
usage(int argc,char ** argv)109 static void usage(int argc, char **argv)
110 {
111 	//	"         1         2         3         4         5         6         7         8  "
112 	//	"12345678901234567890123456789012345678901234567890123456789012345678901234567890\n"
113 	printf(
114 		"Usage: %s [OPTIONS] IMAGE\n"
115 		"Dump erofs layout from IMAGE.\n"
116 		"\n"
117 		"General options:\n"
118 		" -V, --version   print the version number of dump.erofs and exit\n"
119 		" -h, --help      display this help and exit\n"
120 		"\n"
121 		" -S              show statistic information of the image\n"
122 		" -e              show extent info (INODE required)\n"
123 		" -s              show information about superblock\n"
124 		" --device=X      specify an extra device to be used together\n"
125 		" --ls            show directory contents (INODE required)\n"
126 		" --nid=#         show the target inode info of nid #\n"
127 		" --offset=#      skip # bytes at the beginning of IMAGE\n"
128 		" --path=X        show the target inode info of path X\n",
129 		argv[0]);
130 }
131 
erofsdump_print_version(void)132 static void erofsdump_print_version(void)
133 {
134 	printf("dump.erofs (erofs-utils) %s\n", cfg.c_version);
135 }
136 
erofsdump_parse_options_cfg(int argc,char ** argv)137 static int erofsdump_parse_options_cfg(int argc, char **argv)
138 {
139 	int opt, err;
140 	char *endptr;
141 
142 	while ((opt = getopt_long(argc, argv, "SVesh",
143 				  long_options, NULL)) != -1) {
144 		switch (opt) {
145 		case 'e':
146 			dumpcfg.show_extent = true;
147 			++dumpcfg.totalshow;
148 			break;
149 		case 's':
150 			dumpcfg.show_superblock = true;
151 			++dumpcfg.totalshow;
152 			break;
153 		case 'S':
154 			dumpcfg.show_statistics = true;
155 			++dumpcfg.totalshow;
156 			break;
157 		case 'V':
158 			erofsdump_print_version();
159 			exit(0);
160 		case 2:
161 			dumpcfg.show_inode = true;
162 			dumpcfg.nid = (erofs_nid_t)atoll(optarg);
163 			++dumpcfg.totalshow;
164 			break;
165 		case 'h':
166 			usage(argc, argv);
167 			exit(0);
168 		case 3:
169 			err = erofs_blob_open_ro(&g_sbi, optarg);
170 			if (err)
171 				return err;
172 			++g_sbi.extra_devices;
173 			break;
174 		case 4:
175 			dumpcfg.inode_path = optarg;
176 			dumpcfg.show_inode = true;
177 			++dumpcfg.totalshow;
178 			break;
179 		case 5:
180 			dumpcfg.show_subdirectories = true;
181 			break;
182 		case 6:
183 			g_sbi.bdev.offset = strtoull(optarg, &endptr, 0);
184 			if (*endptr != '\0') {
185 				erofs_err("invalid disk offset %s", optarg);
186 				return -EINVAL;
187 			}
188 			break;
189 		default:
190 			return -EINVAL;
191 		}
192 	}
193 
194 	if (optind >= argc) {
195 		erofs_err("missing argument: IMAGE");
196 		return -EINVAL;
197 	}
198 
199 	cfg.c_img_path = strdup(argv[optind++]);
200 	if (!cfg.c_img_path)
201 		return -ENOMEM;
202 
203 	if (optind < argc) {
204 		erofs_err("unexpected argument: %s\n", argv[optind]);
205 		return -EINVAL;
206 	}
207 	return 0;
208 }
209 
erofsdump_get_occupied_size(struct erofs_inode * inode,erofs_off_t * size)210 static int erofsdump_get_occupied_size(struct erofs_inode *inode,
211 		erofs_off_t *size)
212 {
213 	*size = 0;
214 	switch (inode->datalayout) {
215 	case EROFS_INODE_FLAT_INLINE:
216 	case EROFS_INODE_FLAT_PLAIN:
217 	case EROFS_INODE_CHUNK_BASED:
218 		stats.uncompressed_files++;
219 		*size = inode->i_size;
220 		break;
221 	case EROFS_INODE_COMPRESSED_FULL:
222 	case EROFS_INODE_COMPRESSED_COMPACT:
223 		stats.compressed_files++;
224 		*size = inode->u.i_blocks * erofs_blksiz(inode->sbi);
225 		break;
226 	default:
227 		erofs_err("unknown datalayout");
228 		return -ENOTSUP;
229 	}
230 	return 0;
231 }
232 
inc_file_extension_count(const char * dname,unsigned int len)233 static void inc_file_extension_count(const char *dname, unsigned int len)
234 {
235 	char *postfix = memrchr(dname, '.', len);
236 	int type;
237 
238 	if (!postfix) {
239 		type = OTHERFILETYPE - 1;
240 	} else {
241 		for (type = 0; type < OTHERFILETYPE - 1; ++type)
242 			if (!strncmp(postfix, file_types[type],
243 				     len - (postfix - dname)))
244 				break;
245 	}
246 	++stats.file_type_stat[type];
247 }
248 
update_file_size_statistics(erofs_off_t size,bool original)249 static void update_file_size_statistics(erofs_off_t size, bool original)
250 {
251 	unsigned int *file_size = original ? stats.file_original_size :
252 				  stats.file_comp_size;
253 	int size_mark = 0;
254 
255 	size >>= 10;
256 
257 	while (size) {
258 		size >>= 1;
259 		size_mark++;
260 	}
261 
262 	if (size_mark >= FILE_MAX_SIZE_BITS)
263 		file_size[FILE_MAX_SIZE_BITS]++;
264 	else
265 		file_size[size_mark]++;
266 }
267 
erofsdump_ls_dirent_iter(struct erofs_dir_context * ctx)268 static int erofsdump_ls_dirent_iter(struct erofs_dir_context *ctx)
269 {
270 	char fname[EROFS_NAME_LEN + 1];
271 
272 	strncpy(fname, ctx->dname, ctx->de_namelen);
273 	fname[ctx->de_namelen] = '\0';
274 	fprintf(stdout, "%10llu    %u  %s\n",  ctx->de_nid | 0ULL,
275 		ctx->de_ftype, fname);
276 	return 0;
277 }
278 
erofsdump_dirent_iter(struct erofs_dir_context * ctx)279 static int erofsdump_dirent_iter(struct erofs_dir_context *ctx)
280 {
281 	/* skip "." and ".." dentry */
282 	if (ctx->dot_dotdot)
283 		return 0;
284 
285 	return erofsdump_readdir(ctx);
286 }
287 
erofsdump_read_packed_inode(void)288 static int erofsdump_read_packed_inode(void)
289 {
290 	int err;
291 	erofs_off_t occupied_size = 0;
292 	struct erofs_inode vi = { .sbi = &g_sbi, .nid = g_sbi.packed_nid };
293 
294 	if (!(erofs_sb_has_fragments(&g_sbi) && g_sbi.packed_nid > 0))
295 		return 0;
296 
297 	err = erofs_read_inode_from_disk(&vi);
298 	if (err) {
299 		erofs_err("failed to read packed file inode from disk");
300 		return err;
301 	}
302 
303 	err = erofsdump_get_occupied_size(&vi, &occupied_size);
304 	if (err) {
305 		erofs_err("failed to get the file size of packed inode");
306 		return err;
307 	}
308 
309 	stats.files_total_size += occupied_size;
310 	update_file_size_statistics(occupied_size, false);
311 	return 0;
312 }
313 
erofsdump_readdir(struct erofs_dir_context * ctx)314 static int erofsdump_readdir(struct erofs_dir_context *ctx)
315 {
316 	int err;
317 	erofs_off_t occupied_size = 0;
318 	struct erofs_inode vi = { .sbi = &g_sbi, .nid = ctx->de_nid };
319 
320 	err = erofs_read_inode_from_disk(&vi);
321 	if (err) {
322 		erofs_err("failed to read file inode from disk");
323 		return err;
324 	}
325 	stats.files++;
326 	stats.file_category_stat[erofs_mode_to_ftype(vi.i_mode)]++;
327 
328 	err = erofsdump_get_occupied_size(&vi, &occupied_size);
329 	if (err) {
330 		erofs_err("get file size failed");
331 		return err;
332 	}
333 
334 	if (S_ISREG(vi.i_mode)) {
335 		stats.files_total_origin_size += vi.i_size;
336 		inc_file_extension_count(ctx->dname, ctx->de_namelen);
337 		stats.files_total_size += occupied_size;
338 		update_file_size_statistics(vi.i_size, true);
339 		update_file_size_statistics(occupied_size, false);
340 	}
341 
342 	/* XXXX: the dir depth should be restricted in order to avoid loops */
343 	if (S_ISDIR(vi.i_mode)) {
344 		struct erofs_dir_context nctx = {
345 			.flags = ctx->dir ? EROFS_READDIR_VALID_PNID : 0,
346 			.pnid = ctx->dir ? ctx->dir->nid : 0,
347 			.dir = &vi,
348 			.cb = erofsdump_dirent_iter,
349 		};
350 
351 		return erofs_iterate_dir(&nctx, false);
352 	}
353 	return 0;
354 }
355 
erofsdump_map_blocks(struct erofs_inode * inode,struct erofs_map_blocks * map,int flags)356 static int erofsdump_map_blocks(struct erofs_inode *inode,
357 		struct erofs_map_blocks *map, int flags)
358 {
359 	if (erofs_inode_is_data_compressed(inode->datalayout))
360 		return z_erofs_map_blocks_iter(inode, map, flags);
361 	return erofs_map_blocks(inode, map, flags);
362 }
363 
erofsdump_show_fileinfo(bool show_extent)364 static void erofsdump_show_fileinfo(bool show_extent)
365 {
366 	const char *ext_fmt[] = {
367 		"%4d: %8" PRIu64 "..%8" PRIu64 " | %7" PRIu64 " : %10" PRIu64 "..%10" PRIu64 " | %7" PRIu64 "\n",
368 		"%4d: %8" PRIu64 "..%8" PRIu64 " | %7" PRIu64 " : %10" PRIu64 "..%10" PRIu64 " | %7" PRIu64 "  # device %u\n"
369 	};
370 	int err, i;
371 	erofs_off_t size;
372 	u16 access_mode;
373 	struct erofs_inode inode = { .sbi = &g_sbi, .nid = dumpcfg.nid };
374 	char path[PATH_MAX];
375 	char access_mode_str[] = "rwxrwxrwx";
376 	char timebuf[128] = {0};
377 	unsigned int extent_count = 0;
378 	struct erofs_map_blocks map = {
379 		.index = UINT_MAX,
380 		.m_la = 0,
381 	};
382 
383 	if (dumpcfg.inode_path) {
384 		err = erofs_ilookup(dumpcfg.inode_path, &inode);
385 		if (err) {
386 			erofs_err("read inode failed @ %s", dumpcfg.inode_path);
387 			return;
388 		}
389 	} else {
390 		err = erofs_read_inode_from_disk(&inode);
391 		if (err) {
392 			erofs_err("read inode failed @ nid %llu",
393 				  inode.nid | 0ULL);
394 			return;
395 		}
396 	}
397 
398 	err = erofs_get_occupied_size(&inode, &size);
399 	if (err) {
400 		erofs_err("get file size failed @ nid %llu", inode.nid | 0ULL);
401 		return;
402 	}
403 
404 	err = erofs_get_pathname(inode.sbi, inode.nid, path, sizeof(path));
405 	if (err < 0) {
406 		strncpy(path, "(not found)", sizeof(path) - 1);
407 		path[sizeof(path) - 1] = '\0';
408 	}
409 
410 	strftime(timebuf, sizeof(timebuf),
411 		 "%Y-%m-%d %H:%M:%S", localtime((time_t *)&inode.i_mtime));
412 	access_mode = inode.i_mode & 0777;
413 	for (i = 8; i >= 0; i--)
414 		if (((access_mode >> i) & 1) == 0)
415 			access_mode_str[8 - i] = '-';
416 	fprintf(stdout, "Path : %s\n",
417 		erofs_is_packed_inode(&inode) ? "(packed file)" : path);
418 	fprintf(stdout, "Size: %" PRIu64"  On-disk size: %" PRIu64 "  %s\n",
419 		inode.i_size, size,
420 		file_category_types[erofs_mode_to_ftype(inode.i_mode)]);
421 	fprintf(stdout, "NID: %" PRIu64 "   ", inode.nid);
422 	fprintf(stdout, "Links: %u   ", inode.i_nlink);
423 	fprintf(stdout, "Layout: %d   Compression ratio: %.2f%%\n",
424 		inode.datalayout,
425 		(double)(100 * size) / (double)(inode.i_size));
426 	fprintf(stdout, "Inode size: %d   ", inode.inode_isize);
427 	fprintf(stdout,	"Xattr size: %u\n", inode.xattr_isize);
428 	fprintf(stdout, "Uid: %u   Gid: %u  ", inode.i_uid, inode.i_gid);
429 	fprintf(stdout, "Access: %04o/%s\n", access_mode, access_mode_str);
430 	fprintf(stdout, "Timestamp: %s.%09d\n", timebuf, inode.i_mtime_nsec);
431 
432 	if (dumpcfg.show_subdirectories) {
433 		struct erofs_dir_context ctx = {
434 			.flags = EROFS_READDIR_VALID_PNID,
435 			.pnid = inode.nid,
436 			.dir = &inode,
437 			.cb = erofsdump_ls_dirent_iter,
438 			.de_nid = 0,
439 			.dname = "",
440 			.de_namelen = 0,
441 		};
442 
443 		fprintf(stdout, "\n       NID TYPE  FILENAME\n");
444 		err = erofs_iterate_dir(&ctx, false);
445 		if (err) {
446 			erofs_err("failed to list directory contents");
447 			return;
448 		}
449 	}
450 
451 	if (!dumpcfg.show_extent)
452 		return;
453 
454 	fprintf(stdout, "\n Ext:   logical offset   |  length :     physical offset    |  length\n");
455 	while (map.m_la < inode.i_size) {
456 		struct erofs_map_dev mdev;
457 
458 		err = erofsdump_map_blocks(&inode, &map,
459 				EROFS_GET_BLOCKS_FIEMAP);
460 		if (err) {
461 			erofs_err("failed to get file blocks range");
462 			return;
463 		}
464 
465 		mdev = (struct erofs_map_dev) {
466 			.m_deviceid = map.m_deviceid,
467 			.m_pa = map.m_pa,
468 		};
469 		err = erofs_map_dev(inode.sbi, &mdev);
470 		if (err) {
471 			erofs_err("failed to map device");
472 			return;
473 		}
474 
475 		if (map.m_flags & EROFS_MAP_FRAGMENT)
476 			fprintf(stdout, ext_fmt[!!mdev.m_deviceid],
477 				extent_count++,
478 				map.m_la, map.m_la + map.m_llen, map.m_llen,
479 				0, 0, 0, mdev.m_deviceid);
480 		else
481 			fprintf(stdout, ext_fmt[!!mdev.m_deviceid],
482 				extent_count++,
483 				map.m_la, map.m_la + map.m_llen, map.m_llen,
484 				mdev.m_pa, mdev.m_pa + map.m_plen, map.m_plen,
485 				mdev.m_deviceid);
486 		map.m_la += map.m_llen;
487 	}
488 	fprintf(stdout, "%s: %d extents found\n",
489 		erofs_is_packed_inode(&inode) ? "(packed file)" : path, extent_count);
490 }
491 
erofsdump_filesize_distribution(const char * title,unsigned int * file_counts,unsigned int len)492 static void erofsdump_filesize_distribution(const char *title,
493 		unsigned int *file_counts, unsigned int len)
494 {
495 	char col1[30];
496 	unsigned int col2, i, lowerbound, upperbound;
497 	double col3;
498 	char col4[400];
499 
500 	lowerbound = 0;
501 	upperbound = 1;
502 	fprintf(stdout, "\n%s file size distribution:\n", title);
503 	fprintf(stdout, header_format, ">=(KB) .. <(KB) ", "count",
504 			"ratio", "distribution");
505 	for (i = 0; i < len; i++) {
506 		memset(col1, 0, sizeof(col1));
507 		memset(col4, 0, sizeof(col4));
508 		if (i == len - 1)
509 			sprintf(col1, "%6d ..", lowerbound);
510 		else if (i <= 6)
511 			sprintf(col1, "%6d .. %-6d", lowerbound, upperbound);
512 		else
513 
514 			sprintf(col1, "%6d .. %-6d", lowerbound, upperbound);
515 		col2 = file_counts[i];
516 		if (stats.file_category_stat[EROFS_FT_REG_FILE])
517 			col3 = (double)(100 * col2) /
518 				stats.file_category_stat[EROFS_FT_REG_FILE];
519 		else
520 			col3 = 0.0;
521 		memset(col4, '#', col3 / 2);
522 		fprintf(stdout, chart_format, col1, col2, col3, col4);
523 		lowerbound = upperbound;
524 		upperbound <<= 1;
525 	}
526 }
527 
erofsdump_filetype_distribution(char ** file_types,unsigned int len)528 static void erofsdump_filetype_distribution(char **file_types, unsigned int len)
529 {
530 	char col1[30];
531 	unsigned int col2, i;
532 	double col3;
533 	char col4[401];
534 
535 	fprintf(stdout, "\nFile type distribution:\n");
536 	fprintf(stdout, header_format, "type", "count", "ratio",
537 			"distribution");
538 	for (i = 0; i < len; i++) {
539 		memset(col1, 0, sizeof(col1));
540 		memset(col4, 0, sizeof(col4));
541 		sprintf(col1, "%-17s", file_types[i]);
542 		col2 = stats.file_type_stat[i];
543 		if (stats.file_category_stat[EROFS_FT_REG_FILE])
544 			col3 = (double)(100 * col2) /
545 				stats.file_category_stat[EROFS_FT_REG_FILE];
546 		else
547 			col3 = 0.0;
548 		memset(col4, '#', col3 / 2);
549 		fprintf(stdout, chart_format, col1, col2, col3, col4);
550 	}
551 }
552 
erofsdump_file_statistic(void)553 static void erofsdump_file_statistic(void)
554 {
555 	unsigned int i;
556 
557 	fprintf(stdout, "Filesystem total file count:		%lu\n",
558 			stats.files);
559 	for (i = 0; i < EROFS_FT_MAX; i++)
560 		fprintf(stdout, "Filesystem %s count:		%lu\n",
561 			file_category_types[i], stats.file_category_stat[i]);
562 
563 	stats.compress_rate = (double)(100 * stats.files_total_size) /
564 		(double)(stats.files_total_origin_size);
565 	fprintf(stdout, "Filesystem compressed files:            %lu\n",
566 			stats.compressed_files);
567 	fprintf(stdout, "Filesystem uncompressed files:          %lu\n",
568 			stats.uncompressed_files);
569 	fprintf(stdout, "Filesystem total original file size:    %lu Bytes\n",
570 			stats.files_total_origin_size);
571 	fprintf(stdout, "Filesystem total file size:             %lu Bytes\n",
572 			stats.files_total_size);
573 	fprintf(stdout, "Filesystem compress rate:               %.2f%%\n",
574 			stats.compress_rate);
575 }
576 
erofsdump_print_statistic(void)577 static void erofsdump_print_statistic(void)
578 {
579 	int err;
580 	struct erofs_dir_context ctx = {
581 		.flags = 0,
582 		.pnid = 0,
583 		.dir = NULL,
584 		.cb = erofsdump_dirent_iter,
585 		.de_nid = g_sbi.root_nid,
586 		.dname = "",
587 		.de_namelen = 0,
588 	};
589 
590 	err = erofsdump_readdir(&ctx);
591 	if (err) {
592 		erofs_err("read dir failed");
593 		return;
594 	}
595 	err = erofsdump_read_packed_inode();
596 	if (err) {
597 		erofs_err("failed to read packed inode");
598 		return;
599 	}
600 	erofsdump_file_statistic();
601 	erofsdump_filesize_distribution("Original",
602 			stats.file_original_size,
603 			ARRAY_SIZE(stats.file_original_size));
604 	erofsdump_filesize_distribution("On-disk",
605 			stats.file_comp_size,
606 			ARRAY_SIZE(stats.file_comp_size));
607 	erofsdump_filetype_distribution(file_types, OTHERFILETYPE);
608 }
609 
erofsdump_print_supported_compressors(FILE * f,unsigned int mask)610 static void erofsdump_print_supported_compressors(FILE *f, unsigned int mask)
611 {
612 	unsigned int i = 0;
613 	bool comma = false;
614 	const char *s;
615 
616 	while ((s = z_erofs_list_supported_algorithms(i++, &mask)) != NULL) {
617 		if (*s == '\0')
618 			continue;
619 		if (comma)
620 			fputs(", ", f);
621 		fputs(s, f);
622 		comma = true;
623 	}
624 	fputc('\n', f);
625 }
626 
erofsdump_show_superblock(void)627 static void erofsdump_show_superblock(void)
628 {
629 	time_t time = g_sbi.build_time;
630 	char uuid_str[37];
631 	int i = 0;
632 
633 	fprintf(stdout, "Filesystem magic number:                      0x%04X\n",
634 			EROFS_SUPER_MAGIC_V1);
635 	fprintf(stdout, "Filesystem blocksize:                         %u\n",
636 			erofs_blksiz(&g_sbi));
637 	fprintf(stdout, "Filesystem blocks:                            %llu\n",
638 			g_sbi.total_blocks | 0ULL);
639 	fprintf(stdout, "Filesystem inode metadata start block:        %u\n",
640 			g_sbi.meta_blkaddr);
641 	fprintf(stdout, "Filesystem shared xattr metadata start block: %u\n",
642 			g_sbi.xattr_blkaddr);
643 	fprintf(stdout, "Filesystem root nid:                          %llu\n",
644 			g_sbi.root_nid | 0ULL);
645 	if (erofs_sb_has_fragments(&g_sbi) && g_sbi.packed_nid > 0)
646 		fprintf(stdout, "Filesystem packed nid:                        %llu\n",
647 			g_sbi.packed_nid | 0ULL);
648 	if (erofs_sb_has_compr_cfgs(&g_sbi)) {
649 		fprintf(stdout, "Filesystem compr_algs:                        ");
650 		erofsdump_print_supported_compressors(stdout,
651 			g_sbi.available_compr_algs);
652 	} else {
653 		fprintf(stdout, "Filesystem lz4_max_distance:                  %u\n",
654 			g_sbi.lz4.max_distance | 0U);
655 	}
656 	fprintf(stdout, "Filesystem sb_size:                           %u\n",
657 			g_sbi.sb_size | 0U);
658 	fprintf(stdout, "Filesystem inode count:                       %llu\n",
659 			g_sbi.inos | 0ULL);
660 	fprintf(stdout, "Filesystem created:                           %s",
661 			ctime(&time));
662 	fprintf(stdout, "Filesystem features:                          ");
663 	for (; i < ARRAY_SIZE(feature_lists); i++) {
664 		u32 feat = le32_to_cpu(feature_lists[i].compat ?
665 				       g_sbi.feature_compat :
666 				       g_sbi.feature_incompat);
667 		if (feat & feature_lists[i].flag)
668 			fprintf(stdout, "%s ", feature_lists[i].name);
669 	}
670 	erofs_uuid_unparse_lower(g_sbi.uuid, uuid_str);
671 	fprintf(stdout, "\nFilesystem UUID:                              %s\n",
672 			uuid_str);
673 }
674 
main(int argc,char ** argv)675 int main(int argc, char **argv)
676 {
677 	int err;
678 
679 	erofs_init_configure();
680 	err = erofsdump_parse_options_cfg(argc, argv);
681 	if (err) {
682 		if (err == -EINVAL)
683 			fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
684 		goto exit;
685 	}
686 
687 	err = erofs_dev_open(&g_sbi, cfg.c_img_path, O_RDONLY | O_TRUNC);
688 	if (err) {
689 		erofs_err("failed to open image file");
690 		goto exit;
691 	}
692 
693 	err = erofs_read_superblock(&g_sbi);
694 	if (err) {
695 		erofs_err("failed to read superblock");
696 		goto exit_dev_close;
697 	}
698 
699 	if (!dumpcfg.totalshow) {
700 		dumpcfg.show_superblock = true;
701 		dumpcfg.totalshow = 1;
702 	}
703 	if (dumpcfg.show_superblock)
704 		erofsdump_show_superblock();
705 
706 	if (dumpcfg.show_statistics)
707 		erofsdump_print_statistic();
708 
709 	if (dumpcfg.show_extent && !dumpcfg.show_inode) {
710 		fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
711 		goto exit_put_super;
712 	}
713 
714 	if (dumpcfg.show_inode)
715 		erofsdump_show_fileinfo(dumpcfg.show_extent);
716 
717 exit_put_super:
718 	erofs_put_super(&g_sbi);
719 exit_dev_close:
720 	erofs_dev_close(&g_sbi);
721 exit:
722 	erofs_blob_closeall(&g_sbi);
723 	erofs_exit_configure();
724 	return err;
725 }
726