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