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