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