linux-erofs.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 1/5] erofs-utils: add support for the full decompressed length
@ 2021-10-28 10:57 Guo Xuenan
  2021-10-28 10:57 ` [PATCH v2 2/5] erofs-utils: dump: add feature for collecting filesystem statistics Guo Xuenan
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Guo Xuenan @ 2021-10-28 10:57 UTC (permalink / raw)
  To: xiang, linux-erofs; +Cc: daeho43, mpiglet

From: Huang Jianan <huangjianan@oppo.com>

Previously, there is no need to get the full decompressed length since
EROFS supports partial decompression. However for some other cases
such as fiemap, the full decompressed length is necessary for iomap to
make it work properly.

This patch adds a way to get the full decompressed length. Note that
it takes more metadata overhead and it'd be avoided if possible in the
performance sensitive scenario.

Signed-off-by: Huang Jianan <huangjianan@oppo.com>
Signed-off-by: Gao Xiang <xiang@kernel.org>
---
 include/erofs/internal.h |  8 +++-
 lib/data.c               |  2 +-
 lib/zmap.c               | 99 ++++++++++++++++++++++++++++++++++++----
 3 files changed, 97 insertions(+), 12 deletions(-)

diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 8b154ed..a487871 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -259,6 +259,12 @@ struct erofs_map_blocks {
 	erofs_blk_t index;
 };
 
+/*
+ * Used to get the exact decompressed length, e.g. fiemap (consider lookback
+ * approach instead if possible since it's more metadata lightweight.)
+ */
+#define EROFS_GET_BLOCKS_FIEMAP	0x0002
+
 /* super.c */
 int erofs_read_superblock(void);
 
@@ -271,7 +277,7 @@ int erofs_pread(struct erofs_inode *inode, char *buf,
 /* zmap.c */
 int z_erofs_fill_inode(struct erofs_inode *vi);
 int z_erofs_map_blocks_iter(struct erofs_inode *vi,
-			    struct erofs_map_blocks *map);
+			    struct erofs_map_blocks *map, int flags);
 
 #ifdef EUCLEAN
 #define EFSCORRUPTED	EUCLEAN		/* Filesystem is corrupted */
diff --git a/lib/data.c b/lib/data.c
index 641d840..30e7312 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -201,7 +201,7 @@ static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
 	while (end > offset) {
 		map.m_la = end - 1;
 
-		ret = z_erofs_map_blocks_iter(inode, &map);
+		ret = z_erofs_map_blocks_iter(inode, &map, 0);
 		if (ret)
 			break;
 
diff --git a/lib/zmap.c b/lib/zmap.c
index 458030b..f6a8ccf 100644
--- a/lib/zmap.c
+++ b/lib/zmap.c
@@ -159,9 +159,34 @@ static unsigned int decode_compactedbits(unsigned int lobits,
 	return lo;
 }
 
+static int get_compacted_la_distance(unsigned int lclusterbits,
+				     unsigned int encodebits,
+				     unsigned int vcnt, u8 *in, int i)
+{
+	const unsigned int lomask = (1 << lclusterbits) - 1;
+	unsigned int lo, d1 = 0;
+	u8 type;
+
+	DBG_BUGON(i >= vcnt);
+
+	do {
+		lo = decode_compactedbits(lclusterbits, lomask,
+					  in, encodebits * i, &type);
+
+		if (type != Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD)
+			return d1;
+		++d1;
+	} while (++i < vcnt);
+
+	/* vcnt - 1 (Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD) item */
+	if (!(lo & Z_EROFS_VLE_DI_D0_CBLKCNT))
+		d1 += lo - 1;
+	return d1;
+}
+
 static int unpack_compacted_index(struct z_erofs_maprecorder *m,
 				  unsigned int amortizedshift,
-				  unsigned int eofs)
+				  unsigned int eofs, bool lookahead)
 {
 	struct erofs_inode *const vi = m->inode;
 	const unsigned int lclusterbits = vi->z_logical_clusterbits;
@@ -190,6 +215,11 @@ static int unpack_compacted_index(struct z_erofs_maprecorder *m,
 	m->type = type;
 	if (type == Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD) {
 		m->clusterofs = 1 << lclusterbits;
+
+		/* figure out lookahead_distance: delta[1] if needed */
+		if (lookahead)
+			m->delta[1] = get_compacted_la_distance(lclusterbits,
+						encodebits, vcnt, in, i);
 		if (lo & Z_EROFS_VLE_DI_D0_CBLKCNT) {
 			if (!big_pcluster) {
 				DBG_BUGON(1);
@@ -260,7 +290,7 @@ static int unpack_compacted_index(struct z_erofs_maprecorder *m,
 }
 
 static int compacted_load_cluster_from_disk(struct z_erofs_maprecorder *m,
-					    unsigned long lcn)
+					    unsigned long lcn, bool lookahead)
 {
 	struct erofs_inode *const vi = m->inode;
 	const unsigned int lclusterbits = vi->z_logical_clusterbits;
@@ -310,11 +340,12 @@ out:
 	err = z_erofs_reload_indexes(m, erofs_blknr(pos));
 	if (err)
 		return err;
-	return unpack_compacted_index(m, amortizedshift, erofs_blkoff(pos));
+	return unpack_compacted_index(m, amortizedshift, erofs_blkoff(pos),
+				      lookahead);
 }
 
 static int z_erofs_load_cluster_from_disk(struct z_erofs_maprecorder *m,
-					  unsigned int lcn)
+					  unsigned int lcn, bool lookahead)
 {
 	const unsigned int datamode = m->inode->datalayout;
 
@@ -322,7 +353,7 @@ static int z_erofs_load_cluster_from_disk(struct z_erofs_maprecorder *m,
 		return legacy_load_cluster_from_disk(m, lcn);
 
 	if (datamode == EROFS_INODE_FLAT_COMPRESSION)
-		return compacted_load_cluster_from_disk(m, lcn);
+		return compacted_load_cluster_from_disk(m, lcn, lookahead);
 
 	return -EINVAL;
 }
@@ -345,7 +376,7 @@ static int z_erofs_extent_lookback(struct z_erofs_maprecorder *m,
 
 	/* load extent head logical cluster if needed */
 	lcn -= lookback_distance;
-	err = z_erofs_load_cluster_from_disk(m, lcn);
+	err = z_erofs_load_cluster_from_disk(m, lcn, false);
 	if (err)
 		return err;
 
@@ -394,7 +425,7 @@ static int z_erofs_get_extent_compressedlen(struct z_erofs_maprecorder *m,
 	if (m->compressedlcs)
 		goto out;
 
-	err = z_erofs_load_cluster_from_disk(m, lcn);
+	err = z_erofs_load_cluster_from_disk(m, lcn, false);
 	if (err)
 		return err;
 
@@ -440,8 +471,50 @@ err_bonus_cblkcnt:
 	return -EFSCORRUPTED;
 }
 
+static int z_erofs_get_extent_decompressedlen(struct z_erofs_maprecorder *m)
+{
+	struct erofs_inode *const vi = m->inode;
+	struct erofs_map_blocks *map = m->map;
+	unsigned int lclusterbits = vi->z_logical_clusterbits;
+	u64 lcn = m->lcn, headlcn = map->m_la >> lclusterbits;
+	int err;
+
+	do {
+		/* handle the last EOF pcluster (no next HEAD lcluster) */
+		if ((lcn << lclusterbits) >= vi->i_size) {
+			map->m_llen = vi->i_size - map->m_la;
+			return 0;
+		}
+
+		err = z_erofs_load_cluster_from_disk(m, lcn, true);
+		if (err)
+			return err;
+
+		if (m->type == Z_EROFS_VLE_CLUSTER_TYPE_NONHEAD) {
+			DBG_BUGON(!m->delta[1] &&
+				  m->clusterofs != 1 << lclusterbits);
+		} else if (m->type == Z_EROFS_VLE_CLUSTER_TYPE_PLAIN ||
+			   m->type == Z_EROFS_VLE_CLUSTER_TYPE_HEAD) {
+			/* go on until the next HEAD lcluster */
+			if (lcn != headlcn)
+				break;
+			m->delta[1] = 1;
+		} else {
+			erofs_err("unknown type %u @ lcn %lu of nid %llu",
+				  m->type, lcn, (unsigned long long)vi->nid);
+			DBG_BUGON(1);
+			return -EOPNOTSUPP;
+		}
+		lcn += m->delta[1];
+	} while (m->delta[1]);
+
+	map->m_llen = (lcn << lclusterbits) + m->clusterofs - map->m_la;
+	return 0;
+}
+
 int z_erofs_map_blocks_iter(struct erofs_inode *vi,
-			    struct erofs_map_blocks *map)
+			    struct erofs_map_blocks *map,
+			    int flags)
 {
 	struct z_erofs_maprecorder m = {
 		.inode = vi,
@@ -470,7 +543,7 @@ int z_erofs_map_blocks_iter(struct erofs_inode *vi,
 	initial_lcn = ofs >> lclusterbits;
 	endoff = ofs & ((1 << lclusterbits) - 1);
 
-	err = z_erofs_load_cluster_from_disk(&m, initial_lcn);
+	err = z_erofs_load_cluster_from_disk(&m, initial_lcn, false);
 	if (err)
 		goto out;
 
@@ -512,11 +585,17 @@ int z_erofs_map_blocks_iter(struct erofs_inode *vi,
 
 	map->m_llen = end - map->m_la;
 	map->m_pa = blknr_to_addr(m.pblk);
+	map->m_flags |= EROFS_MAP_MAPPED;
 
 	err = z_erofs_get_extent_compressedlen(&m, initial_lcn);
 	if (err)
 		goto out;
-	map->m_flags |= EROFS_MAP_MAPPED;
+
+	if (flags & EROFS_GET_BLOCKS_FIEMAP) {
+		err = z_erofs_get_extent_decompressedlen(&m);
+		if (!err)
+			map->m_flags |= EROFS_MAP_FULL_MAPPED;
+	}
 
 out:
 	erofs_dbg("m_la %" PRIu64 " m_pa %" PRIu64 " m_llen %" PRIu64 " m_plen %" PRIu64 " m_flags 0%o",
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 2/5] erofs-utils: dump: add feature for collecting filesystem statistics.
  2021-10-28 10:57 [PATCH v2 1/5] erofs-utils: add support for the full decompressed length Guo Xuenan
@ 2021-10-28 10:57 ` Guo Xuenan
  2021-10-28 11:12   ` Gao Xiang
  2021-10-28 10:57 ` [PATCH v2 3/5] erofs-utils: dump: add option to print specified file infomation Guo Xuenan
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 10+ messages in thread
From: Guo Xuenan @ 2021-10-28 10:57 UTC (permalink / raw)
  To: xiang, linux-erofs; +Cc: daeho43, mpiglet

From: Wang Qi <mpiglet@outlook.com>

Add -S option, for printing statistics of the overall disk, including
file type(by file extension)/size statistics and distribution, number
of compressed and uncompressed files, whole compression ratio of image.

Signed-off-by: Guo Xuenan <guoxuenan@huawei.com>
Signed-off-by: Wang Qi <mpiglet@outlook.com>
---
 dump/Makefile.am         |   2 +-
 dump/main.c              | 358 ++++++++++++++++++++++++++++++++++++++-
 include/erofs/internal.h |   1 +
 lib/namei.c              |   2 +-
 4 files changed, 360 insertions(+), 3 deletions(-)

diff --git a/dump/Makefile.am b/dump/Makefile.am
index f0246d7..4759901 100644
--- a/dump/Makefile.am
+++ b/dump/Makefile.am
@@ -6,4 +6,4 @@ bin_PROGRAMS     = dump.erofs
 AM_CPPFLAGS = ${libuuid_CFLAGS}
 dump_erofs_SOURCES = main.c
 dump_erofs_CFLAGS = -Wall -Werror -I$(top_srcdir)/include
-dump_erofs_LDADD = $(top_builddir)/lib/liberofs.la ${libselinux_LIBS} ${libuuid_LIBS}
+dump_erofs_LDADD = $(top_builddir)/lib/liberofs.la ${libselinux_LIBS} ${libuuid_LIBS} ${liblz4_LIBS}
diff --git a/dump/main.c b/dump/main.c
index 5b7ac5c..eacf02e 100644
--- a/dump/main.c
+++ b/dump/main.c
@@ -18,9 +18,51 @@
 struct erofsdump_cfg {
 	unsigned int totalshow;
 	bool show_superblock;
+	bool show_statistics;
 };
 static struct erofsdump_cfg dumpcfg;
 
+static const char chart_format[] = "%-16s	%-11d %8.2f%% |%-50s|\n";
+static const char header_format[] = "%-16s %11s %16s |%-50s|\n";
+static char *file_types[] = {
+	".txt", ".so", ".xml", ".apk",
+	".odex", ".vdex", ".oat", ".rc",
+	".otf", ".txt", "others",
+};
+#define OTHERFILETYPE	ARRAY_SIZE(file_types)
+/* (1 << FILE_MAX_SIZE_BITS)KB */
+#define	FILE_MAX_SIZE_BITS	16
+
+static const char * const file_category_types[] = {
+	[EROFS_FT_UNKNOWN] = "unknown type",
+	[EROFS_FT_REG_FILE] = "regular file",
+	[EROFS_FT_DIR] = "directory",
+	[EROFS_FT_CHRDEV] = "char dev",
+	[EROFS_FT_BLKDEV] = "block dev",
+	[EROFS_FT_FIFO] = "FIFO file",
+	[EROFS_FT_SOCK] = "SOCK file",
+	[EROFS_FT_SYMLINK] = "symlink file",
+};
+
+struct erofs_statistics {
+	unsigned long files;
+	unsigned long compressed_files;
+	unsigned long uncompressed_files;
+	unsigned long files_total_size;
+	unsigned long files_total_origin_size;
+	double compress_rate;
+
+	/* [statistics] # of files based on inode_info->flags */
+	unsigned long file_category_stat[EROFS_FT_MAX];
+	/* [statistics] # of files based on file name extensions */
+	unsigned int file_type_stat[OTHERFILETYPE];
+	/* [statistics] # of files based on the original size of files */
+	unsigned int file_original_size[FILE_MAX_SIZE_BITS + 1];
+	/* [statistics] # of files based on the compressed size of files */
+	unsigned int file_comp_size[FILE_MAX_SIZE_BITS + 1];
+};
+static struct erofs_statistics stats;
+
 static struct option long_options[] = {
 	{"help", no_argument, 0, 1},
 	{0, 0, 0, 0},
@@ -39,10 +81,13 @@ static struct erofsdump_feature feature_lists[] = {
 	{ false, EROFS_FEATURE_INCOMPAT_CHUNKED_FILE, "chunked_file" },
 };
 
+static int erofs_read_dir(erofs_nid_t nid, erofs_nid_t parent_nid);
+
 static void usage(void)
 {
 	fputs("usage: [options] IMAGE\n\n"
 	      "Dump erofs layout from IMAGE, and [options] are:\n"
+	      " -S      show statistic information of the image\n"
 	      " -V      print the version number of dump.erofs and exit.\n"
 	      " -s      show information about superblock\n"
 	      " --help  display this help and exit.\n",
@@ -58,13 +103,17 @@ static int erofsdump_parse_options_cfg(int argc, char **argv)
 {
 	int opt;
 
-	while ((opt = getopt_long(argc, argv, "Vs",
+	while ((opt = getopt_long(argc, argv, "SVs",
 				  long_options, NULL)) != -1) {
 		switch (opt) {
 		case 's':
 			dumpcfg.show_superblock = true;
 			++dumpcfg.totalshow;
 			break;
+		case 'S':
+			dumpcfg.show_statistics = true;
+			++dumpcfg.totalshow;
+			break;
 		case 'V':
 			erofsdump_print_version();
 			exit(0);
@@ -90,6 +139,310 @@ static int erofsdump_parse_options_cfg(int argc, char **argv)
 	return 0;
 }
 
+static int erofs_get_occupied_size(struct erofs_inode *inode,
+		erofs_off_t *size)
+{
+	*size = 0;
+	switch (inode->datalayout) {
+	case EROFS_INODE_FLAT_INLINE:
+	case EROFS_INODE_FLAT_PLAIN:
+	case EROFS_INODE_CHUNK_BASED:
+		stats.uncompressed_files++;
+		*size = inode->i_size;
+		break;
+	case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
+	case EROFS_INODE_FLAT_COMPRESSION:
+		stats.compressed_files++;
+		*size = inode->u.i_blocks * EROFS_BLKSIZ;
+		break;
+	default:
+		erofs_err("unknown datalayout");
+		return -1;
+	}
+	return 0;
+}
+
+static int erofs_getfile_extension(const char *filename)
+{
+	char *postfix = strrchr(filename, '.');
+	int type = 0;
+
+	if (postfix == NULL)
+		return OTHERFILETYPE - 1;
+	for (type = 0; type < OTHERFILETYPE - 1; ++type) {
+		if (strcmp(postfix, file_types[type]) == 0)
+			break;
+		type++;
+	}
+	return type;
+}
+
+static void update_file_size_statatics(erofs_off_t occupied_size,
+		erofs_off_t original_size)
+{
+	int occupied_size_mark, original_size_mark;
+
+	original_size_mark = 0;
+	occupied_size_mark = 0;
+	occupied_size >>= 10;
+	original_size >>= 10;
+
+	while (occupied_size || original_size) {
+		if (occupied_size) {
+			occupied_size >>= 1;
+			occupied_size_mark++;
+		}
+		if (original_size) {
+			original_size >>= 1;
+			original_size_mark++;
+		}
+	}
+
+	if (original_size_mark >= FILE_MAX_SIZE_BITS)
+		stats.file_original_size[FILE_MAX_SIZE_BITS]++;
+	else
+		stats.file_original_size[original_size_mark]++;
+
+	if (occupied_size_mark >= FILE_MAX_SIZE_BITS)
+		stats.file_comp_size[FILE_MAX_SIZE_BITS]++;
+	else
+		stats.file_comp_size[occupied_size_mark]++;
+}
+
+static inline int erofs_checkdirent(struct erofs_dirent *de,
+		struct erofs_dirent *last_de,
+		u32 maxsize, const char *dname)
+{
+	int dname_len;
+	unsigned int nameoff = le16_to_cpu(de->nameoff);
+
+	if (nameoff < sizeof(struct erofs_dirent) ||
+			nameoff >= PAGE_SIZE) {
+		erofs_err("invalid de[0].nameoff %u @ nid %llu",
+				nameoff, de->nid | 0ULL);
+		return -EFSCORRUPTED;
+	}
+
+	dname_len = (de + 1 >= last_de) ? strnlen(dname, maxsize - nameoff) :
+				le16_to_cpu(de[1].nameoff) - nameoff;
+	/* a corrupted entry is found */
+	if (nameoff + dname_len > maxsize ||
+			dname_len > EROFS_NAME_LEN) {
+		erofs_err("bogus dirent @ nid %llu",
+				le64_to_cpu(de->nid) | 0ULL);
+		DBG_BUGON(1);
+		return -EFSCORRUPTED;
+	}
+	if (de->file_type >= EROFS_FT_MAX) {
+		erofs_err("invalid file type %llu", de->nid);
+		return -EFSCORRUPTED;
+	}
+	return dname_len;
+}
+
+static int erofs_read_dirent(struct erofs_dirent *de,
+		erofs_nid_t nid, erofs_nid_t parent_nid,
+		const char *dname)
+{
+	int err;
+	erofs_off_t occupied_size = 0;
+	struct erofs_inode inode = { .nid = de->nid };
+
+	stats.files++;
+	stats.file_category_stat[de->file_type]++;
+	err = erofs_read_inode_from_disk(&inode);
+	if (err) {
+		erofs_err("read file inode from disk failed!");
+		return err;
+	}
+
+	err = erofs_get_occupied_size(&inode, &occupied_size);
+	if (err) {
+		erofs_err("get file size failed\n");
+		return err;
+	}
+
+	if (de->file_type == EROFS_FT_REG_FILE) {
+		stats.files_total_origin_size += inode.i_size;
+		stats.file_type_stat[erofs_getfile_extension(dname)]++;
+		stats.files_total_size += occupied_size;
+		update_file_size_statatics(occupied_size, inode.i_size);
+	}
+
+	if ((de->file_type == EROFS_FT_DIR)
+			&& de->nid != nid && de->nid != parent_nid) {
+		err = erofs_read_dir(de->nid, nid);
+		if (err) {
+			erofs_err("parse dir nid %llu error occurred\n",
+					de->nid);
+			return err;
+		}
+	}
+	return 0;
+}
+
+
+static int erofs_read_dir(erofs_nid_t nid, erofs_nid_t parent_nid)
+{
+	int err;
+	erofs_off_t offset;
+	char buf[EROFS_BLKSIZ];
+	struct erofs_inode vi = { .nid = nid };
+
+	err = erofs_read_inode_from_disk(&vi);
+	if (err)
+		return err;
+
+	offset = 0;
+	while (offset < vi.i_size) {
+		erofs_off_t maxsize = min_t(erofs_off_t,
+						vi.i_size - offset, EROFS_BLKSIZ);
+		struct erofs_dirent *de = (void *)buf;
+		struct erofs_dirent *end;
+		unsigned int nameoff;
+
+		err = erofs_pread(&vi, buf, maxsize, offset);
+		if (err)
+			return err;
+
+		nameoff = le16_to_cpu(de->nameoff);
+		end = (void *)buf + nameoff;
+		while (de < end) {
+			const char *dname;
+			int ret;
+
+			/* skip "." and ".." dentry */
+			if (de->nid == nid || de->nid == parent_nid) {
+				de++;
+				continue;
+			}
+
+			dname = (char *)buf + nameoff;
+			ret = erofs_checkdirent(de, end, maxsize, dname);
+			if (ret < 0)
+				return ret;
+			ret = erofs_read_dirent(de, nid, parent_nid, dname);
+			if (ret < 0)
+				return ret;
+			++de;
+		}
+		offset += maxsize;
+	}
+	return 0;
+}
+
+static void erofsdump_print_chart_row(char *col1, unsigned int col2,
+		double col3, char *col4)
+{
+	char row[500] = {0};
+
+	sprintf(row, chart_format, col1, col2, col3, col4);
+	fprintf(stdout, row);
+}
+
+static void erofsdump_filesize_distribution(const char *title,
+		unsigned int *file_counts, unsigned int len)
+{
+	char col1[30];
+	unsigned int col2;
+	double col3;
+	char col4[400];
+	unsigned int lowerbound = 0;
+	unsigned int upperbound = 1;
+
+	fprintf(stdout, "\n%s file size distribution:\n", title);
+	fprintf(stdout, header_format, ">=(KB) .. <(KB) ", "count",
+			"ratio", "distribution");
+	for (int i = 0; i < len; i++) {
+		memset(col1, 0, sizeof(col1));
+		memset(col4, 0, sizeof(col4));
+		if (i == len - 1)
+			sprintf(col1, "%6d ..", lowerbound);
+		else if (i <= 6)
+			sprintf(col1, "%6d .. %-6d", lowerbound, upperbound);
+		else
+
+			sprintf(col1, "%6d .. %-6d", lowerbound, upperbound);
+		col2 = file_counts[i];
+		if (stats.file_category_stat[EROFS_FT_REG_FILE])
+			col3 = (double)(100 * col2) /
+				stats.file_category_stat[EROFS_FT_REG_FILE];
+		else
+			col3 = 0.0;
+		memset(col4, '#', col3 / 2);
+		erofsdump_print_chart_row(col1, col2, col3, col4);
+		lowerbound = upperbound;
+		upperbound <<= 1;
+	}
+}
+
+static void erofsdump_filetype_distribution(char **file_types, unsigned int len)
+{
+	char col1[30];
+	unsigned int col2;
+	double col3;
+	char col4[401];
+
+	fprintf(stdout, "\nFile type distribution:\n");
+	fprintf(stdout, header_format, "type", "count", "ratio",
+			"distribution");
+	for (int i = 0; i < len; i++) {
+		memset(col1, 0, sizeof(col1));
+		memset(col4, 0, sizeof(col4));
+		sprintf(col1, "%-17s", file_types[i]);
+		col2 = stats.file_type_stat[i];
+		if (stats.file_category_stat[EROFS_FT_REG_FILE])
+			col3 = (double)(100 * col2) /
+				stats.file_category_stat[EROFS_FT_REG_FILE];
+		else
+			col3 = 0.0;
+		memset(col4, '#', col3 / 2);
+		erofsdump_print_chart_row(col1, col2, col3, col4);
+	}
+}
+
+static void erofsdump_file_statistic(void)
+{
+	fprintf(stdout, "Filesystem total file count:		%lu\n",
+			stats.files);
+	for (int i = 0; i < EROFS_FT_MAX; i++)
+		fprintf(stdout, "Filesystem %s count:		%lu\n",
+			file_category_types[i], stats.file_category_stat[i]);
+
+	stats.compress_rate = (double)(100 * stats.files_total_size) /
+		(double)(stats.files_total_origin_size);
+	fprintf(stdout, "Filesystem compressed files:            %lu\n",
+			stats.compressed_files);
+	fprintf(stdout, "Filesystem uncompressed files:          %lu\n",
+			stats.uncompressed_files);
+	fprintf(stdout, "Filesystem total original file size:    %lu Bytes\n",
+			stats.files_total_origin_size);
+	fprintf(stdout, "Filesystem total file size:             %lu Bytes\n",
+			stats.files_total_size);
+	fprintf(stdout, "Filesystem compress rate:               %.2f%%\n",
+			stats.compress_rate);
+}
+
+static void erofsdump_print_statistic(void)
+{
+	int err;
+
+	err = erofs_read_dir(sbi.root_nid, sbi.root_nid);
+	if (err) {
+		erofs_err("read dir failed");
+		return;
+	}
+
+	erofsdump_file_statistic();
+	erofsdump_filesize_distribution("Original",
+			stats.file_original_size,
+			ARRAY_SIZE(stats.file_original_size));
+	erofsdump_filesize_distribution("On-disk",
+			stats.file_comp_size,
+			ARRAY_SIZE(stats.file_comp_size));
+	erofsdump_filetype_distribution(file_types, OTHERFILETYPE);
+}
+
 static void erofsdump_show_superblock(void)
 {
 	time_t time = sbi.build_time;
@@ -156,6 +509,9 @@ int main(int argc, char **argv)
 	if (dumpcfg.show_superblock)
 		erofsdump_show_superblock();
 
+	if (dumpcfg.show_statistics)
+		erofsdump_print_statistic();
+
 exit:
 	erofs_exit_configure();
 	return err;
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index a487871..139656c 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -269,6 +269,7 @@ struct erofs_map_blocks {
 int erofs_read_superblock(void);
 
 /* namei.c */
+int erofs_read_inode_from_disk(struct erofs_inode *vi);
 int erofs_ilookup(const char *path, struct erofs_inode *vi);
 
 /* data.c */
diff --git a/lib/namei.c b/lib/namei.c
index b4bdabf..56f199a 100644
--- a/lib/namei.c
+++ b/lib/namei.c
@@ -22,7 +22,7 @@ static dev_t erofs_new_decode_dev(u32 dev)
 	return makedev(major, minor);
 }
 
-static int erofs_read_inode_from_disk(struct erofs_inode *vi)
+int erofs_read_inode_from_disk(struct erofs_inode *vi)
 {
 	int ret, ifmt;
 	char buf[sizeof(struct erofs_inode_extended)];
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 3/5] erofs-utils: dump: add option to print specified file infomation.
  2021-10-28 10:57 [PATCH v2 1/5] erofs-utils: add support for the full decompressed length Guo Xuenan
  2021-10-28 10:57 ` [PATCH v2 2/5] erofs-utils: dump: add feature for collecting filesystem statistics Guo Xuenan
@ 2021-10-28 10:57 ` Guo Xuenan
  2021-10-28 11:20   ` Gao Xiang
  2021-10-28 10:57 ` [PATCH v2 4/5] erofs-utils: dump: add support for showing file extents Guo Xuenan
  2021-10-28 10:57 ` [PATCH v2 5/5] erofs-utils: manpage: add dump.erofs manpage Guo Xuenan
  3 siblings, 1 reply; 10+ messages in thread
From: Guo Xuenan @ 2021-10-28 10:57 UTC (permalink / raw)
  To: xiang, linux-erofs; +Cc: daeho43, mpiglet

From: Wang Qi <mpiglet@outlook.com>

add option --nid to print information of specific nid, including
file name/file links/file size/data layout/compression ratio etc..

Signed-off-by: Guo Xuenan <guoxuenan@huawei.com>
Signed-off-by: Wang Qi <mpiglet@outlook.com>
---
 dump/main.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 135 insertions(+), 3 deletions(-)

diff --git a/dump/main.c b/dump/main.c
index eacf02e..d1aa017 100644
--- a/dump/main.c
+++ b/dump/main.c
@@ -17,8 +17,10 @@
 
 struct erofsdump_cfg {
 	unsigned int totalshow;
+	bool show_inode;
 	bool show_superblock;
 	bool show_statistics;
+	erofs_nid_t nid;
 };
 static struct erofsdump_cfg dumpcfg;
 
@@ -64,7 +66,8 @@ struct erofs_statistics {
 static struct erofs_statistics stats;
 
 static struct option long_options[] = {
-	{"help", no_argument, 0, 1},
+	{"help", no_argument, NULL, 1},
+	{"nid", required_argument, NULL, 2},
 	{0, 0, 0, 0},
 };
 
@@ -82,14 +85,18 @@ static struct erofsdump_feature feature_lists[] = {
 };
 
 static int erofs_read_dir(erofs_nid_t nid, erofs_nid_t parent_nid);
+static inline int erofs_checkdirent(struct erofs_dirent *de,
+		struct erofs_dirent *last_de,
+		u32 maxsize, const char *dname);
 
 static void usage(void)
 {
 	fputs("usage: [options] IMAGE\n\n"
 	      "Dump erofs layout from IMAGE, and [options] are:\n"
-	      " -S      show statistic information of the image\n"
 	      " -V      print the version number of dump.erofs and exit.\n"
 	      " -s      show information about superblock\n"
+	      " -S      show statistic information of the image\n"
+	      " --nid=# show the target inode info of nid #\n"
 	      " --help  display this help and exit.\n",
 	      stderr);
 }
@@ -103,7 +110,7 @@ static int erofsdump_parse_options_cfg(int argc, char **argv)
 {
 	int opt;
 
-	while ((opt = getopt_long(argc, argv, "SVs",
+	while ((opt = getopt_long(argc, argv, "SV:s",
 				  long_options, NULL)) != -1) {
 		switch (opt) {
 		case 's':
@@ -117,6 +124,11 @@ static int erofsdump_parse_options_cfg(int argc, char **argv)
 		case 'V':
 			erofsdump_print_version();
 			exit(0);
+		case 2:
+			dumpcfg.show_inode = true;
+			dumpcfg.nid = (erofs_nid_t)atoll(optarg);
+			++dumpcfg.totalshow;
+			break;
 		case 1:
 			usage();
 			exit(0);
@@ -331,6 +343,123 @@ static int erofs_read_dir(erofs_nid_t nid, erofs_nid_t parent_nid)
 	return 0;
 }
 
+static int erofs_get_pathname(erofs_nid_t nid, erofs_nid_t parent_nid,
+		erofs_nid_t target, char *path, unsigned int pos)
+{
+	int err;
+	erofs_off_t offset;
+	char buf[EROFS_BLKSIZ];
+	struct erofs_inode inode = { .nid = nid };
+
+	path[pos++] = '/';
+	if (target == sbi.root_nid)
+		return 0;
+
+	err = erofs_read_inode_from_disk(&inode);
+	if (err) {
+		erofs_err("read inode %lu failed", nid);
+		return err;
+	}
+
+	offset = 0;
+	while (offset < inode.i_size) {
+		erofs_off_t maxsize = min_t(erofs_off_t,
+					inode.i_size - offset, EROFS_BLKSIZ);
+		struct erofs_dirent *de = (void *)buf;
+		struct erofs_dirent *end;
+		unsigned int nameoff;
+
+		err = erofs_pread(&inode, buf, maxsize, offset);
+		if (err)
+			return err;
+
+		nameoff = le16_to_cpu(de->nameoff);
+		end = (void *)buf + nameoff;
+		while (de < end) {
+			const char *dname;
+			int len;
+
+			nameoff = le16_to_cpu(de->nameoff);
+			dname = (char *)buf + nameoff;
+			len = erofs_checkdirent(de, end, maxsize, dname);
+			if (len < 0)
+				return len;
+
+			if (de->nid == target) {
+				memcpy(path + pos, dname, len);
+				return 0;
+			}
+
+			if (de->file_type == EROFS_FT_DIR &&
+					de->nid != parent_nid &&
+					de->nid != nid) {
+				memcpy(path + pos, dname, len);
+				err = erofs_get_pathname(de->nid, nid,
+						target, path, pos + len);
+				if (!err)
+					return 0;
+				memset(path + pos, 0, len);
+			}
+			++de;
+		}
+		offset += maxsize;
+	}
+	return -1;
+}
+
+static void erofsdump_show_fileinfo(void)
+{
+	int err;
+	erofs_off_t size;
+	u16 access_mode;
+	time_t t;
+	erofs_nid_t nid = dumpcfg.nid;
+	struct erofs_inode inode = { .nid = nid };
+	char path[PATH_MAX + 1] = {0};
+	char access_mode_str[] = "rwxrwxrwx";
+	char timebuf[128] = {0};
+
+	err = erofs_read_inode_from_disk(&inode);
+	if (err) {
+		erofs_err("failed to find inode %lu from disk", nid);
+		return;
+	}
+
+	err = erofs_get_occupied_size(&inode, &size);
+	if (err) {
+		erofs_err("get file size failed\n");
+		return;
+	}
+
+	err = erofs_get_pathname(sbi.root_nid, sbi.root_nid, nid, path, 0);
+	if (err < 0) {
+		fprintf(stderr, "File: Path not found.\n");
+		return;
+	}
+
+	fprintf(stdout, "File : %s\n", path);
+	fprintf(stdout, "Inode: %lu  ", inode.nid);
+	fprintf(stdout, "Links: %u  ", inode.i_nlink);
+	fprintf(stdout, "Layout: %d\n", inode.datalayout);
+	fprintf(stdout, "Inode size: %d   ", inode.inode_isize);
+	fprintf(stdout, "Extent size: %u   ", inode.extent_isize);
+	fprintf(stdout,	"Xattr size: %u\n", inode.xattr_isize);
+	fprintf(stdout, "File size: %lu  ", inode.i_size);
+	fprintf(stdout,	"On-disk size: %lu  ", size);
+	fprintf(stdout, "Compression ratio: %.2f%%\n",
+			(double)(100 * size) / (double)(inode.i_size));
+	t = inode.i_ctime;
+	strftime(timebuf, sizeof(timebuf),
+			"%Y-%m-%d %H:%M:%S", localtime(&t));
+	access_mode = inode.i_mode & 0777;
+	for (int i = 8; i >= 0; i--)
+		if (((access_mode >> i) & 1) == 0)
+			access_mode_str[8 - i] = '-';
+	fprintf(stdout, "Uid: %u   Gid: %u  ", inode.i_uid, inode.i_gid);
+	fprintf(stdout, "Access: %04o/%s\n", access_mode, access_mode_str);
+	fprintf(stdout, "Timestamp: %s\n", timebuf);
+}
+
 static void erofsdump_print_chart_row(char *col1, unsigned int col2,
 		double col3, char *col4)
 {
@@ -512,6 +641,9 @@ int main(int argc, char **argv)
 	if (dumpcfg.show_statistics)
 		erofsdump_print_statistic();
 
+	if (dumpcfg.show_inode)
+		erofsdump_show_fileinfo();
+
 exit:
 	erofs_exit_configure();
 	return err;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 4/5] erofs-utils: dump: add support for showing file extents.
  2021-10-28 10:57 [PATCH v2 1/5] erofs-utils: add support for the full decompressed length Guo Xuenan
  2021-10-28 10:57 ` [PATCH v2 2/5] erofs-utils: dump: add feature for collecting filesystem statistics Guo Xuenan
  2021-10-28 10:57 ` [PATCH v2 3/5] erofs-utils: dump: add option to print specified file infomation Guo Xuenan
@ 2021-10-28 10:57 ` Guo Xuenan
  2021-10-28 11:27   ` Gao Xiang
  2021-10-28 10:57 ` [PATCH v2 5/5] erofs-utils: manpage: add dump.erofs manpage Guo Xuenan
  3 siblings, 1 reply; 10+ messages in thread
From: Guo Xuenan @ 2021-10-28 10:57 UTC (permalink / raw)
  To: xiang, linux-erofs; +Cc: daeho43, mpiglet

Add option -e to show file extents info, this option needs
specify nid as well. (eg. dump.erofs --nid # -e erofs.img)

Signed-off-by: Guo Xuenan <guoxuenan@huawei.com>
Signed-off-by: Wang Qi <mpiglet@outlook.com>
---
 dump/main.c              | 77 ++++++++++++++++++++++++++++++++++------
 include/erofs/internal.h |  2 ++
 lib/data.c               |  4 +--
 3 files changed, 71 insertions(+), 12 deletions(-)

diff --git a/dump/main.c b/dump/main.c
index d1aa017..58ecf93 100644
--- a/dump/main.c
+++ b/dump/main.c
@@ -18,6 +18,7 @@
 struct erofsdump_cfg {
 	unsigned int totalshow;
 	bool show_inode;
+	bool show_extent;
 	bool show_superblock;
 	bool show_statistics;
 	erofs_nid_t nid;
@@ -94,6 +95,7 @@ static void usage(void)
 	fputs("usage: [options] IMAGE\n\n"
 	      "Dump erofs layout from IMAGE, and [options] are:\n"
 	      " -V      print the version number of dump.erofs and exit.\n"
+	      " -e      show extent info (require --nid #)\n"
 	      " -s      show information about superblock\n"
 	      " -S      show statistic information of the image\n"
 	      " --nid=# show the target inode info of nid #\n"
@@ -110,9 +112,13 @@ static int erofsdump_parse_options_cfg(int argc, char **argv)
 {
 	int opt;
 
-	while ((opt = getopt_long(argc, argv, "SV:s",
+	while ((opt = getopt_long(argc, argv, "SVes",
 				  long_options, NULL)) != -1) {
 		switch (opt) {
+		case 'e':
+			dumpcfg.show_extent = true;
+			++dumpcfg.totalshow;
+			break;
 		case 's':
 			dumpcfg.show_superblock = true;
 			++dumpcfg.totalshow;
@@ -407,7 +413,28 @@ static int erofs_get_pathname(erofs_nid_t nid, erofs_nid_t parent_nid,
 	return -1;
 }
 
-static void erofsdump_show_fileinfo(void)
+static int erofsdump_map_blocks_helper(struct erofs_inode *inode,
+		struct erofs_map_blocks *map, int flags)
+{
+	int err = 0;
+
+	switch (inode->datalayout) {
+	case EROFS_INODE_FLAT_PLAIN:
+	case EROFS_INODE_FLAT_INLINE:
+	case EROFS_INODE_CHUNK_BASED:
+		err = erofs_map_blocks(inode, map, flags);
+		break;
+	case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
+	case EROFS_INODE_FLAT_COMPRESSION:
+		err = z_erofs_map_blocks_iter(inode, map, flags);
+		break;
+	default:
+		break;
+	}
+	return err;
+}
+
+static void erofsdump_show_fileinfo(bool show_extent)
 {
 	int err;
 	erofs_off_t size;
@@ -418,6 +445,11 @@ static void erofsdump_show_fileinfo(void)
 	char path[PATH_MAX + 1] = {0};
 	char access_mode_str[] = "rwxrwxrwx";
 	char timebuf[128] = {0};
+	unsigned int extent_count = 0;
+	struct erofs_map_blocks map = {
+		.index = UINT_MAX,
+		.m_la = 0,
+	};
 
 	err = erofs_read_inode_from_disk(&inode);
 	if (err) {
@@ -437,6 +469,14 @@ static void erofsdump_show_fileinfo(void)
 		return;
 	}
 
+	t = inode.i_ctime;
+	strftime(timebuf, sizeof(timebuf),
+			"%Y-%m-%d %H:%M:%S", localtime(&t));
+	access_mode = inode.i_mode & 0777;
+	for (int i = 8; i >= 0; i--) {
+		if (((access_mode >> i) & 1) == 0)
+			access_mode_str[8 - i] = '-';
+	}
 	fprintf(stdout, "File : %s\n", path);
 	fprintf(stdout, "Inode: %lu  ", inode.nid);
 	fprintf(stdout, "Links: %u  ", inode.i_nlink);
@@ -448,16 +488,28 @@ static void erofsdump_show_fileinfo(void)
 	fprintf(stdout,	"On-disk size: %lu  ", size);
 	fprintf(stdout, "Compression ratio: %.2f%%\n",
 			(double)(100 * size) / (double)(inode.i_size));
-	t = inode.i_ctime;
-	strftime(timebuf, sizeof(timebuf),
-			"%Y-%m-%d %H:%M:%S", localtime(&t));
-	access_mode = inode.i_mode & 0777;
-	for (int i = 8; i >= 0; i--)
-		if (((access_mode >> i) & 1) == 0)
-			access_mode_str[8 - i] = '-';
 	fprintf(stdout, "Uid: %u   Gid: %u  ", inode.i_uid, inode.i_gid);
 	fprintf(stdout, "Access: %04o/%s\n", access_mode, access_mode_str);
 	fprintf(stdout, "Timestamp: %s\n", timebuf);
+
+	if (!dumpcfg.show_extent)
+		return;
+
+	fprintf(stdout, "\nExt:  logical_offset/length:  physical_offset/length\n");
+	while (map.m_la < inode.i_size) {
+		err = erofsdump_map_blocks_helper(&inode, &map,
+				EROFS_GET_BLOCKS_FIEMAP);
+		if (err) {
+			erofs_err("get file blocks range failed");
+			return;
+		}
+		fprintf(stdout, "%u: ", extent_count++);
+		fprintf(stdout, "%6lu..%-6lu / %-lu: ",
+				map.m_la, map.m_la + map.m_llen, map.m_llen);
+		fprintf(stdout, "%lu..%lu / %-lu\n",
+				map.m_pa, map.m_pa + map.m_plen, map.m_plen);
+		map.m_la += map.m_llen;
+	}
 }
 
 static void erofsdump_print_chart_row(char *col1, unsigned int col2,
@@ -641,8 +693,13 @@ int main(int argc, char **argv)
 	if (dumpcfg.show_statistics)
 		erofsdump_print_statistic();
 
+	if (dumpcfg.show_extent && !dumpcfg.show_inode) {
+		usage();
+		goto exit;
+	}
+
 	if (dumpcfg.show_inode)
-		erofsdump_show_fileinfo();
+		erofsdump_show_fileinfo(dumpcfg.show_extent);
 
 exit:
 	erofs_exit_configure();
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 139656c..8282f03 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -275,6 +275,8 @@ int erofs_ilookup(const char *path, struct erofs_inode *vi);
 /* data.c */
 int erofs_pread(struct erofs_inode *inode, char *buf,
 		erofs_off_t count, erofs_off_t offset);
+int erofs_map_blocks(struct erofs_inode *inode,
+		struct erofs_map_blocks *map, int flags);
 /* zmap.c */
 int z_erofs_fill_inode(struct erofs_inode *vi);
 int z_erofs_map_blocks_iter(struct erofs_inode *vi,
diff --git a/lib/data.c b/lib/data.c
index 30e7312..9048707 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -61,8 +61,8 @@ err_out:
 	return err;
 }
 
-static int erofs_map_blocks(struct erofs_inode *inode,
-			    struct erofs_map_blocks *map, int flags)
+int erofs_map_blocks(struct erofs_inode *inode,
+		struct erofs_map_blocks *map, int flags)
 {
 	struct erofs_inode *vi = inode;
 	struct erofs_inode_chunk_index *idx;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v2 5/5] erofs-utils: manpage: add dump.erofs manpage.
  2021-10-28 10:57 [PATCH v2 1/5] erofs-utils: add support for the full decompressed length Guo Xuenan
                   ` (2 preceding siblings ...)
  2021-10-28 10:57 ` [PATCH v2 4/5] erofs-utils: dump: add support for showing file extents Guo Xuenan
@ 2021-10-28 10:57 ` Guo Xuenan
  2021-10-28 11:44   ` Gao Xiang
  3 siblings, 1 reply; 10+ messages in thread
From: Guo Xuenan @ 2021-10-28 10:57 UTC (permalink / raw)
  To: xiang, linux-erofs; +Cc: daeho43, mpiglet

This patch adds dump.erofs manpage.

Signed-off-by: Guo Xuenan <guoxuenan@huawei.com>
---
 man/Makefile.am  |  2 +-
 man/dump.erofs.1 | 60 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 1 deletion(-)
 create mode 100644 man/dump.erofs.1

diff --git a/man/Makefile.am b/man/Makefile.am
index d62d6e2..769b557 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0+
 
-dist_man_MANS = mkfs.erofs.1
+dist_man_MANS = mkfs.erofs.1 dump.erofs.1
 
 EXTRA_DIST = erofsfuse.1
 if ENABLE_FUSE
diff --git a/man/dump.erofs.1 b/man/dump.erofs.1
new file mode 100644
index 0000000..d44c7b6
--- /dev/null
+++ b/man/dump.erofs.1
@@ -0,0 +1,60 @@
+.\" Copyright (c) 2021 Guo Xuenan <guoxuenan@huawei.com>
+.\"
+.TH DUMP.EROFS 1
+.SH NAME
+dump.erofs \- retrieve directory and file entries, show specific file
+or overall disk statistics information from an EROFS-formated image.
+.SH SYNOPSIS
+.B dump.erofs
+[
+.B \--nid
+.I inode number
+]
+[
+.B \-e
+]
+[
+.B \-s
+]
+[
+.B \-S
+]
+[
+.B \-V
+]
+.I DEVICE
+.SH DESCRIPTION
+.B dump.erofs
+is used to retrieve erofs metadata (usually in a disk partition).
+\fIdevice\fP is the special file corresponding to the device (e.g.
+\fI/dev/sdXX\fP).
+
+Currently, it can demonstrate 1) a file information of given inode number, 2)
+overall disk statistics, 3) file extent information,
+4) erofs superblock information.
+.SH OPTIONS
+.TP
+.BI \--nid " inode number"
+Specify an inode number to print its file information.
+.TP
+.BI \-e
+show the file extent information, the option depends on option --nid to specify nid.
+.TP
+.BI \-V
+Print the version number and exit.
+.TP
+.BI \-s
+Show superblock information of the an EROFS-formated image.
+.TP
+.BI \-S
+Show statistics of the overall disk, including file type(by file extension)/size statistics and distribution, number of compressed and uncompressed files, whole compression ratio of image etc.
+.SH AUTHOR
+Initial code was written by Wang Qi <mpiglet@outlook.com>, Guo Xuenan <guoxuenan@huawei.com>.
+.PP
+This manual page was written by Guo Xuenan <guoxuenan@huawei.com>
+.SH AVAILABILITY
+.B dump.erofs
+is part of erofs-utils package and is available from git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs-utils.git.
+.SH SEE ALSO
+.BR mkfs.erofs(1),
+.BR fsck.erofs(1)
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 2/5] erofs-utils: dump: add feature for collecting filesystem statistics.
  2021-10-28 10:57 ` [PATCH v2 2/5] erofs-utils: dump: add feature for collecting filesystem statistics Guo Xuenan
@ 2021-10-28 11:12   ` Gao Xiang
  0 siblings, 0 replies; 10+ messages in thread
From: Gao Xiang @ 2021-10-28 11:12 UTC (permalink / raw)
  To: Guo Xuenan; +Cc: daeho43, linux-erofs, mpiglet

On Thu, Oct 28, 2021 at 06:57:45PM +0800, Guo Xuenan wrote:
> From: Wang Qi <mpiglet@outlook.com>
> 
> Add -S option, for printing statistics of the overall disk, including
> file type(by file extension)/size statistics and distribution, number
> of compressed and uncompressed files, whole compression ratio of image.
> 
> Signed-off-by: Guo Xuenan <guoxuenan@huawei.com>
> Signed-off-by: Wang Qi <mpiglet@outlook.com>
> ---
>  dump/Makefile.am         |   2 +-
>  dump/main.c              | 358 ++++++++++++++++++++++++++++++++++++++-
>  include/erofs/internal.h |   1 +
>  lib/namei.c              |   2 +-
>  4 files changed, 360 insertions(+), 3 deletions(-)
> 
> diff --git a/dump/Makefile.am b/dump/Makefile.am
> index f0246d7..4759901 100644
> --- a/dump/Makefile.am
> +++ b/dump/Makefile.am
> @@ -6,4 +6,4 @@ bin_PROGRAMS     = dump.erofs
>  AM_CPPFLAGS = ${libuuid_CFLAGS}
>  dump_erofs_SOURCES = main.c
>  dump_erofs_CFLAGS = -Wall -Werror -I$(top_srcdir)/include
> -dump_erofs_LDADD = $(top_builddir)/lib/liberofs.la ${libselinux_LIBS} ${libuuid_LIBS}
> +dump_erofs_LDADD = $(top_builddir)/lib/liberofs.la ${libselinux_LIBS} ${libuuid_LIBS} ${liblz4_LIBS}
> diff --git a/dump/main.c b/dump/main.c
> index 5b7ac5c..eacf02e 100644
> --- a/dump/main.c
> +++ b/dump/main.c
> @@ -18,9 +18,51 @@
>  struct erofsdump_cfg {
>  	unsigned int totalshow;
>  	bool show_superblock;
> +	bool show_statistics;
>  };
>  static struct erofsdump_cfg dumpcfg;
>  
> +static const char chart_format[] = "%-16s	%-11d %8.2f%% |%-50s|\n";
> +static const char header_format[] = "%-16s %11s %16s |%-50s|\n";
> +static char *file_types[] = {
> +	".txt", ".so", ".xml", ".apk",
> +	".odex", ".vdex", ".oat", ".rc",
> +	".otf", ".txt", "others",
> +};

After re-thinking about it, I guess how about showing the top X extensions
and others. (X can be configurable), it makes more sense than fixed
extension as above.

But anyway, I'm fine to leave it as-is for now. we could improve it
later.

> +#define OTHERFILETYPE	ARRAY_SIZE(file_types)
> +/* (1 << FILE_MAX_SIZE_BITS)KB */
> +#define	FILE_MAX_SIZE_BITS	16
> +
> +static const char * const file_category_types[] = {
> +	[EROFS_FT_UNKNOWN] = "unknown type",
> +	[EROFS_FT_REG_FILE] = "regular file",
> +	[EROFS_FT_DIR] = "directory",
> +	[EROFS_FT_CHRDEV] = "char dev",
> +	[EROFS_FT_BLKDEV] = "block dev",
> +	[EROFS_FT_FIFO] = "FIFO file",
> +	[EROFS_FT_SOCK] = "SOCK file",
> +	[EROFS_FT_SYMLINK] = "symlink file",
> +};
> +
> +struct erofs_statistics {
> +	unsigned long files;
> +	unsigned long compressed_files;
> +	unsigned long uncompressed_files;
> +	unsigned long files_total_size;
> +	unsigned long files_total_origin_size;
> +	double compress_rate;
> +
> +	/* [statistics] # of files based on inode_info->flags */
> +	unsigned long file_category_stat[EROFS_FT_MAX];
> +	/* [statistics] # of files based on file name extensions */
> +	unsigned int file_type_stat[OTHERFILETYPE];
> +	/* [statistics] # of files based on the original size of files */
> +	unsigned int file_original_size[FILE_MAX_SIZE_BITS + 1];
> +	/* [statistics] # of files based on the compressed size of files */
> +	unsigned int file_comp_size[FILE_MAX_SIZE_BITS + 1];
> +};
> +static struct erofs_statistics stats;
> +
>  static struct option long_options[] = {
>  	{"help", no_argument, 0, 1},
>  	{0, 0, 0, 0},
> @@ -39,10 +81,13 @@ static struct erofsdump_feature feature_lists[] = {
>  	{ false, EROFS_FEATURE_INCOMPAT_CHUNKED_FILE, "chunked_file" },
>  };
>  
> +static int erofs_read_dir(erofs_nid_t nid, erofs_nid_t parent_nid);
> +
>  static void usage(void)
>  {
>  	fputs("usage: [options] IMAGE\n\n"
>  	      "Dump erofs layout from IMAGE, and [options] are:\n"
> +	      " -S      show statistic information of the image\n"
>  	      " -V      print the version number of dump.erofs and exit.\n"
>  	      " -s      show information about superblock\n"
>  	      " --help  display this help and exit.\n",
> @@ -58,13 +103,17 @@ static int erofsdump_parse_options_cfg(int argc, char **argv)
>  {
>  	int opt;
>  
> -	while ((opt = getopt_long(argc, argv, "Vs",
> +	while ((opt = getopt_long(argc, argv, "SVs",
>  				  long_options, NULL)) != -1) {
>  		switch (opt) {
>  		case 's':
>  			dumpcfg.show_superblock = true;
>  			++dumpcfg.totalshow;
>  			break;
> +		case 'S':
> +			dumpcfg.show_statistics = true;
> +			++dumpcfg.totalshow;
> +			break;
>  		case 'V':
>  			erofsdump_print_version();
>  			exit(0);
> @@ -90,6 +139,310 @@ static int erofsdump_parse_options_cfg(int argc, char **argv)
>  	return 0;
>  }
>  
> +static int erofs_get_occupied_size(struct erofs_inode *inode,
> +		erofs_off_t *size)
> +{
> +	*size = 0;
> +	switch (inode->datalayout) {
> +	case EROFS_INODE_FLAT_INLINE:
> +	case EROFS_INODE_FLAT_PLAIN:
> +	case EROFS_INODE_CHUNK_BASED:
> +		stats.uncompressed_files++;
> +		*size = inode->i_size;
> +		break;
> +	case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
> +	case EROFS_INODE_FLAT_COMPRESSION:
> +		stats.compressed_files++;
> +		*size = inode->u.i_blocks * EROFS_BLKSIZ;
> +		break;
> +	default:
> +		erofs_err("unknown datalayout");
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +static int erofs_getfile_extension(const char *filename)
> +{
> +	char *postfix = strrchr(filename, '.');
> +	int type = 0;
> +
> +	if (postfix == NULL)

	if (!postfix)

> +		return OTHERFILETYPE - 1;
> +	for (type = 0; type < OTHERFILETYPE - 1; ++type) {
> +		if (strcmp(postfix, file_types[type]) == 0)
> +			break;
> +		type++;

redundant "type++" here.

Thanks,
Gao Xiang


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 3/5] erofs-utils: dump: add option to print specified file infomation.
  2021-10-28 10:57 ` [PATCH v2 3/5] erofs-utils: dump: add option to print specified file infomation Guo Xuenan
@ 2021-10-28 11:20   ` Gao Xiang
  0 siblings, 0 replies; 10+ messages in thread
From: Gao Xiang @ 2021-10-28 11:20 UTC (permalink / raw)
  To: Guo Xuenan; +Cc: daeho43, linux-erofs, mpiglet

On Thu, Oct 28, 2021 at 06:57:46PM +0800, Guo Xuenan wrote:
> From: Wang Qi <mpiglet@outlook.com>
> 
> add option --nid to print information of specific nid, including
> file name/file links/file size/data layout/compression ratio etc..
> 
> Signed-off-by: Guo Xuenan <guoxuenan@huawei.com>
> Signed-off-by: Wang Qi <mpiglet@outlook.com>
> ---
>  dump/main.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 135 insertions(+), 3 deletions(-)
> 
> diff --git a/dump/main.c b/dump/main.c
> index eacf02e..d1aa017 100644
> --- a/dump/main.c
> +++ b/dump/main.c
> @@ -17,8 +17,10 @@
>  
>  struct erofsdump_cfg {
>  	unsigned int totalshow;
> +	bool show_inode;
>  	bool show_superblock;
>  	bool show_statistics;
> +	erofs_nid_t nid;
>  };
>  static struct erofsdump_cfg dumpcfg;
>  
> @@ -64,7 +66,8 @@ struct erofs_statistics {
>  static struct erofs_statistics stats;
>  
>  static struct option long_options[] = {
> -	{"help", no_argument, 0, 1},
> +	{"help", no_argument, NULL, 1},
> +	{"nid", required_argument, NULL, 2},
>  	{0, 0, 0, 0},
>  };
>  
> @@ -82,14 +85,18 @@ static struct erofsdump_feature feature_lists[] = {
>  };
>  
>  static int erofs_read_dir(erofs_nid_t nid, erofs_nid_t parent_nid);
> +static inline int erofs_checkdirent(struct erofs_dirent *de,
> +		struct erofs_dirent *last_de,
> +		u32 maxsize, const char *dname);
>  
>  static void usage(void)
>  {
>  	fputs("usage: [options] IMAGE\n\n"
>  	      "Dump erofs layout from IMAGE, and [options] are:\n"
> -	      " -S      show statistic information of the image\n"
>  	      " -V      print the version number of dump.erofs and exit.\n"
>  	      " -s      show information about superblock\n"
> +	      " -S      show statistic information of the image\n"
> +	      " --nid=# show the target inode info of nid #\n"
>  	      " --help  display this help and exit.\n",
>  	      stderr);
>  }
> @@ -103,7 +110,7 @@ static int erofsdump_parse_options_cfg(int argc, char **argv)
>  {
>  	int opt;
>  
> -	while ((opt = getopt_long(argc, argv, "SVs",
> +	while ((opt = getopt_long(argc, argv, "SV:s",
>  				  long_options, NULL)) != -1) {
>  		switch (opt) {
>  		case 's':
> @@ -117,6 +124,11 @@ static int erofsdump_parse_options_cfg(int argc, char **argv)
>  		case 'V':
>  			erofsdump_print_version();
>  			exit(0);
> +		case 2:
> +			dumpcfg.show_inode = true;
> +			dumpcfg.nid = (erofs_nid_t)atoll(optarg);
> +			++dumpcfg.totalshow;
> +			break;
>  		case 1:
>  			usage();
>  			exit(0);
> @@ -331,6 +343,123 @@ static int erofs_read_dir(erofs_nid_t nid, erofs_nid_t parent_nid)
>  	return 0;
>  }
>  
> +static int erofs_get_pathname(erofs_nid_t nid, erofs_nid_t parent_nid,
> +		erofs_nid_t target, char *path, unsigned int pos)
> +{
> +	int err;
> +	erofs_off_t offset;
> +	char buf[EROFS_BLKSIZ];
> +	struct erofs_inode inode = { .nid = nid };
> +
> +	path[pos++] = '/';
> +	if (target == sbi.root_nid)
> +		return 0;
> +
> +	err = erofs_read_inode_from_disk(&inode);
> +	if (err) {
> +		erofs_err("read inode %lu failed", nid);
> +		return err;
> +	}
> +
> +	offset = 0;
> +	while (offset < inode.i_size) {
> +		erofs_off_t maxsize = min_t(erofs_off_t,
> +					inode.i_size - offset, EROFS_BLKSIZ);
> +		struct erofs_dirent *de = (void *)buf;
> +		struct erofs_dirent *end;
> +		unsigned int nameoff;
> +
> +		err = erofs_pread(&inode, buf, maxsize, offset);
> +		if (err)
> +			return err;
> +
> +		nameoff = le16_to_cpu(de->nameoff);
> +		end = (void *)buf + nameoff;
> +		while (de < end) {
> +			const char *dname;
> +			int len;
> +
> +			nameoff = le16_to_cpu(de->nameoff);
> +			dname = (char *)buf + nameoff;
> +			len = erofs_checkdirent(de, end, maxsize, dname);
> +			if (len < 0)
> +				return len;
> +
> +			if (de->nid == target) {
> +				memcpy(path + pos, dname, len);
> +				return 0;
> +			}
> +
> +			if (de->file_type == EROFS_FT_DIR &&
> +					de->nid != parent_nid &&
> +					de->nid != nid) {
> +				memcpy(path + pos, dname, len);
> +				err = erofs_get_pathname(de->nid, nid,
> +						target, path, pos + len);
> +				if (!err)
> +					return 0;

can we just path[pos] = '\0'; for now?

> +				memset(path + pos, 0, len);
> +			}
> +			++de;
> +		}
> +		offset += maxsize;
> +	}
> +	return -1;
> +}
> +
> +static void erofsdump_show_fileinfo(void)
> +{
> +	int err;
> +	erofs_off_t size;
> +	u16 access_mode;
> +	time_t t;
> +	erofs_nid_t nid = dumpcfg.nid;
> +	struct erofs_inode inode = { .nid = nid };
> +	char path[PATH_MAX + 1] = {0};
> +	char access_mode_str[] = "rwxrwxrwx";
> +	char timebuf[128] = {0};
> +
> +	err = erofs_read_inode_from_disk(&inode);
> +	if (err) {
> +		erofs_err("failed to find inode %lu from disk", nid);
> +		return;
> +	}
> +
> +	err = erofs_get_occupied_size(&inode, &size);
> +	if (err) {
> +		erofs_err("get file size failed\n");
> +		return;
> +	}
> +
> +	err = erofs_get_pathname(sbi.root_nid, sbi.root_nid, nid, path, 0);
> +	if (err < 0) {
> +		fprintf(stderr, "File: Path not found.\n");
> +		return;
> +	}
> +
> +	fprintf(stdout, "File : %s\n", path);
> +	fprintf(stdout, "Inode: %lu  ", inode.nid);
> +	fprintf(stdout, "Links: %u  ", inode.i_nlink);
> +	fprintf(stdout, "Layout: %d\n", inode.datalayout);
> +	fprintf(stdout, "Inode size: %d   ", inode.inode_isize);
> +	fprintf(stdout, "Extent size: %u   ", inode.extent_isize);
> +	fprintf(stdout,	"Xattr size: %u\n", inode.xattr_isize);
> +	fprintf(stdout, "File size: %lu  ", inode.i_size);
> +	fprintf(stdout,	"On-disk size: %lu  ", size);
> +	fprintf(stdout, "Compression ratio: %.2f%%\n",
> +			(double)(100 * size) / (double)(inode.i_size));
> +	t = inode.i_ctime;
> +	strftime(timebuf, sizeof(timebuf),
> +			"%Y-%m-%d %H:%M:%S", localtime(&t));

forgot to update this part? I mean only extended inodes have such field.
Also, should we print nano-timestamp as well?

Thanks,
Gao Xiang

> +	access_mode = inode.i_mode & 0777;
> +	for (int i = 8; i >= 0; i--)
> +		if (((access_mode >> i) & 1) == 0)
> +			access_mode_str[8 - i] = '-';
> +	fprintf(stdout, "Uid: %u   Gid: %u  ", inode.i_uid, inode.i_gid);
> +	fprintf(stdout, "Access: %04o/%s\n", access_mode, access_mode_str);
> +	fprintf(stdout, "Timestamp: %s\n", timebuf);
> +}
> +
>  static void erofsdump_print_chart_row(char *col1, unsigned int col2,
>  		double col3, char *col4)
>  {
> @@ -512,6 +641,9 @@ int main(int argc, char **argv)
>  	if (dumpcfg.show_statistics)
>  		erofsdump_print_statistic();
>  
> +	if (dumpcfg.show_inode)
> +		erofsdump_show_fileinfo();
> +
>  exit:
>  	erofs_exit_configure();
>  	return err;
> -- 
> 2.31.1

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 4/5] erofs-utils: dump: add support for showing file extents.
  2021-10-28 10:57 ` [PATCH v2 4/5] erofs-utils: dump: add support for showing file extents Guo Xuenan
@ 2021-10-28 11:27   ` Gao Xiang
  2021-10-28 11:32     ` Gao Xiang
  0 siblings, 1 reply; 10+ messages in thread
From: Gao Xiang @ 2021-10-28 11:27 UTC (permalink / raw)
  To: Guo Xuenan; +Cc: daeho43, linux-erofs, mpiglet

On Thu, Oct 28, 2021 at 06:57:47PM +0800, Guo Xuenan wrote:
> Add option -e to show file extents info, this option needs
> specify nid as well. (eg. dump.erofs --nid # -e erofs.img)
> 
> Signed-off-by: Guo Xuenan <guoxuenan@huawei.com>
> Signed-off-by: Wang Qi <mpiglet@outlook.com>
> ---
>  dump/main.c              | 77 ++++++++++++++++++++++++++++++++++------
>  include/erofs/internal.h |  2 ++
>  lib/data.c               |  4 +--
>  3 files changed, 71 insertions(+), 12 deletions(-)
> 
> diff --git a/dump/main.c b/dump/main.c
> index d1aa017..58ecf93 100644
> --- a/dump/main.c
> +++ b/dump/main.c
> @@ -18,6 +18,7 @@
>  struct erofsdump_cfg {
>  	unsigned int totalshow;
>  	bool show_inode;
> +	bool show_extent;
>  	bool show_superblock;
>  	bool show_statistics;
>  	erofs_nid_t nid;
> @@ -94,6 +95,7 @@ static void usage(void)
>  	fputs("usage: [options] IMAGE\n\n"
>  	      "Dump erofs layout from IMAGE, and [options] are:\n"
>  	      " -V      print the version number of dump.erofs and exit.\n"
> +	      " -e      show extent info (require --nid #)\n"

	      " -e      show extent info (require --nid set)\n"

>  	      " -s      show information about superblock\n"
>  	      " -S      show statistic information of the image\n"
>  	      " --nid=# show the target inode info of nid #\n"
> @@ -110,9 +112,13 @@ static int erofsdump_parse_options_cfg(int argc, char **argv)
>  {
>  	int opt;
>  
> -	while ((opt = getopt_long(argc, argv, "SV:s",
> +	while ((opt = getopt_long(argc, argv, "SVes",
>  				  long_options, NULL)) != -1) {
>  		switch (opt) {
> +		case 'e':
> +			dumpcfg.show_extent = true;
> +			++dumpcfg.totalshow;
> +			break;
>  		case 's':
>  			dumpcfg.show_superblock = true;
>  			++dumpcfg.totalshow;
> @@ -407,7 +413,28 @@ static int erofs_get_pathname(erofs_nid_t nid, erofs_nid_t parent_nid,
>  	return -1;
>  }
>  
> -static void erofsdump_show_fileinfo(void)
> +static int erofsdump_map_blocks_helper(struct erofs_inode *inode,
> +		struct erofs_map_blocks *map, int flags)
> +{
> +	int err = 0;
> +

Let's add a generic entry in lib/zmap.c:

int erofs_map_blocks(struct inode *inode,
		     struct erofs_map_blocks *map, int flags)
{
	if (erofs_inode_is_data_compressed(EROFS_I(inode)->datalayout))
		return z_erofs_map_blocks_iter(inode, map, flags);
	return erofs_map_blocks_flatmode(inode, map, flags);
}

> +	switch (inode->datalayout) {
> +	case EROFS_INODE_FLAT_PLAIN:
> +	case EROFS_INODE_FLAT_INLINE:
> +	case EROFS_INODE_CHUNK_BASED:
> +		err = erofs_map_blocks(inode, map, flags);
> +		break;
> +	case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
> +	case EROFS_INODE_FLAT_COMPRESSION:
> +		err = z_erofs_map_blocks_iter(inode, map, flags);
> +		break;
> +	default:

		err = -EOPNOTSUPP;

> +		break;
> +	}
> +	return err;
> +}
> +
> +static void erofsdump_show_fileinfo(bool show_extent)
>  {
>  	int err;
>  	erofs_off_t size;
> @@ -418,6 +445,11 @@ static void erofsdump_show_fileinfo(void)
>  	char path[PATH_MAX + 1] = {0};
>  	char access_mode_str[] = "rwxrwxrwx";
>  	char timebuf[128] = {0};
> +	unsigned int extent_count = 0;
> +	struct erofs_map_blocks map = {
> +		.index = UINT_MAX,
> +		.m_la = 0,
> +	};
>  
>  	err = erofs_read_inode_from_disk(&inode);
>  	if (err) {
> @@ -437,6 +469,14 @@ static void erofsdump_show_fileinfo(void)
>  		return;
>  	}
>  
> +	t = inode.i_ctime;
> +	strftime(timebuf, sizeof(timebuf),
> +			"%Y-%m-%d %H:%M:%S", localtime(&t));
> +	access_mode = inode.i_mode & 0777;
> +	for (int i = 8; i >= 0; i--) {
> +		if (((access_mode >> i) & 1) == 0)
> +			access_mode_str[8 - i] = '-';
> +	}

Why we need to move this part?

Thanks,
Gao Xiang


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 4/5] erofs-utils: dump: add support for showing file extents.
  2021-10-28 11:27   ` Gao Xiang
@ 2021-10-28 11:32     ` Gao Xiang
  0 siblings, 0 replies; 10+ messages in thread
From: Gao Xiang @ 2021-10-28 11:32 UTC (permalink / raw)
  To: Guo Xuenan; +Cc: daeho43, linux-erofs, mpiglet

On Thu, Oct 28, 2021 at 07:27:05PM +0800, Gao Xiang wrote:
> On Thu, Oct 28, 2021 at 06:57:47PM +0800, Guo Xuenan wrote:
> > Add option -e to show file extents info, this option needs
> > specify nid as well. (eg. dump.erofs --nid # -e erofs.img)
> > 
> > Signed-off-by: Guo Xuenan <guoxuenan@huawei.com>
> > Signed-off-by: Wang Qi <mpiglet@outlook.com>
> > ---
> >  dump/main.c              | 77 ++++++++++++++++++++++++++++++++++------
> >  include/erofs/internal.h |  2 ++
> >  lib/data.c               |  4 +--
> >  3 files changed, 71 insertions(+), 12 deletions(-)
> > 
> > diff --git a/dump/main.c b/dump/main.c
> > index d1aa017..58ecf93 100644
> > --- a/dump/main.c
> > +++ b/dump/main.c
> > @@ -18,6 +18,7 @@
> >  struct erofsdump_cfg {
> >  	unsigned int totalshow;
> >  	bool show_inode;
> > +	bool show_extent;
> >  	bool show_superblock;
> >  	bool show_statistics;
> >  	erofs_nid_t nid;
> > @@ -94,6 +95,7 @@ static void usage(void)
> >  	fputs("usage: [options] IMAGE\n\n"
> >  	      "Dump erofs layout from IMAGE, and [options] are:\n"
> >  	      " -V      print the version number of dump.erofs and exit.\n"
> > +	      " -e      show extent info (require --nid #)\n"
> 
> 	      " -e      show extent info (require --nid set)\n"
> 
> >  	      " -s      show information about superblock\n"
> >  	      " -S      show statistic information of the image\n"
> >  	      " --nid=# show the target inode info of nid #\n"
> > @@ -110,9 +112,13 @@ static int erofsdump_parse_options_cfg(int argc, char **argv)
> >  {
> >  	int opt;
> >  
> > -	while ((opt = getopt_long(argc, argv, "SV:s",
> > +	while ((opt = getopt_long(argc, argv, "SVes",
> >  				  long_options, NULL)) != -1) {
> >  		switch (opt) {
> > +		case 'e':
> > +			dumpcfg.show_extent = true;
> > +			++dumpcfg.totalshow;
> > +			break;
> >  		case 's':
> >  			dumpcfg.show_superblock = true;
> >  			++dumpcfg.totalshow;
> > @@ -407,7 +413,28 @@ static int erofs_get_pathname(erofs_nid_t nid, erofs_nid_t parent_nid,
> >  	return -1;
> >  }
> >  
> > -static void erofsdump_show_fileinfo(void)
> > +static int erofsdump_map_blocks_helper(struct erofs_inode *inode,
> > +		struct erofs_map_blocks *map, int flags)
> > +{
> > +	int err = 0;
> > +
> 
> Let's add a generic entry in lib/zmap.c:

Oh, since erofs_map_blocks is already taken for now, I'm fine with the
old name erofsdump_map_blocks() for now....

Thanks,
Gao Xiang

> 
> int erofs_map_blocks(struct inode *inode,
> 		     struct erofs_map_blocks *map, int flags)
> {
> 	if (erofs_inode_is_data_compressed(EROFS_I(inode)->datalayout))
> 		return z_erofs_map_blocks_iter(inode, map, flags);
> 	return erofs_map_blocks_flatmode(inode, map, flags);
> }
> 
> > +	switch (inode->datalayout) {
> > +	case EROFS_INODE_FLAT_PLAIN:
> > +	case EROFS_INODE_FLAT_INLINE:
> > +	case EROFS_INODE_CHUNK_BASED:
> > +		err = erofs_map_blocks(inode, map, flags);
> > +		break;
> > +	case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
> > +	case EROFS_INODE_FLAT_COMPRESSION:
> > +		err = z_erofs_map_blocks_iter(inode, map, flags);
> > +		break;
> > +	default:
> 
> 		err = -EOPNOTSUPP;
> 
> > +		break;
> > +	}
> > +	return err;
> > +}
> > +
> > +static void erofsdump_show_fileinfo(bool show_extent)
> >  {
> >  	int err;
> >  	erofs_off_t size;
> > @@ -418,6 +445,11 @@ static void erofsdump_show_fileinfo(void)
> >  	char path[PATH_MAX + 1] = {0};
> >  	char access_mode_str[] = "rwxrwxrwx";
> >  	char timebuf[128] = {0};
> > +	unsigned int extent_count = 0;
> > +	struct erofs_map_blocks map = {
> > +		.index = UINT_MAX,
> > +		.m_la = 0,
> > +	};
> >  
> >  	err = erofs_read_inode_from_disk(&inode);
> >  	if (err) {
> > @@ -437,6 +469,14 @@ static void erofsdump_show_fileinfo(void)
> >  		return;
> >  	}
> >  
> > +	t = inode.i_ctime;
> > +	strftime(timebuf, sizeof(timebuf),
> > +			"%Y-%m-%d %H:%M:%S", localtime(&t));
> > +	access_mode = inode.i_mode & 0777;
> > +	for (int i = 8; i >= 0; i--) {
> > +		if (((access_mode >> i) & 1) == 0)
> > +			access_mode_str[8 - i] = '-';
> > +	}
> 
> Why we need to move this part?
> 
> Thanks,
> Gao Xiang

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v2 5/5] erofs-utils: manpage: add dump.erofs manpage.
  2021-10-28 10:57 ` [PATCH v2 5/5] erofs-utils: manpage: add dump.erofs manpage Guo Xuenan
@ 2021-10-28 11:44   ` Gao Xiang
  0 siblings, 0 replies; 10+ messages in thread
From: Gao Xiang @ 2021-10-28 11:44 UTC (permalink / raw)
  To: Guo Xuenan; +Cc: daeho43, linux-erofs, mpiglet

On Thu, Oct 28, 2021 at 06:57:48PM +0800, Guo Xuenan wrote:
> This patch adds dump.erofs manpage.
> 
> Signed-off-by: Guo Xuenan <guoxuenan@huawei.com>
> ---
>  man/Makefile.am  |  2 +-
>  man/dump.erofs.1 | 60 ++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 61 insertions(+), 1 deletion(-)
>  create mode 100644 man/dump.erofs.1
> 
> diff --git a/man/Makefile.am b/man/Makefile.am
> index d62d6e2..769b557 100644
> --- a/man/Makefile.am
> +++ b/man/Makefile.am
> @@ -1,6 +1,6 @@
>  # SPDX-License-Identifier: GPL-2.0+
>  
> -dist_man_MANS = mkfs.erofs.1
> +dist_man_MANS = mkfs.erofs.1 dump.erofs.1
>  
>  EXTRA_DIST = erofsfuse.1
>  if ENABLE_FUSE
> diff --git a/man/dump.erofs.1 b/man/dump.erofs.1
> new file mode 100644
> index 0000000..d44c7b6
> --- /dev/null
> +++ b/man/dump.erofs.1
> @@ -0,0 +1,60 @@
> +.\" Copyright (c) 2021 Guo Xuenan <guoxuenan@huawei.com>
> +.\"
> +.TH DUMP.EROFS 1
> +.SH NAME
> +dump.erofs \- retrieve directory and file entries, show specific file
> +or overall disk statistics information from an EROFS-formated image.

                                                  ^ EROFS-formatted

> +.SH SYNOPSIS
> +.B dump.erofs
> +[
> +.B \--nid
> +.I inode number
> +]
> +[
> +.B \-e
> +]
> +[
> +.B \-s
> +]
> +[
> +.B \-S
> +]
> +[
> +.B \-V
> +]
> +.I DEVICE

But from the usage, it should be 'IMAGE' here?

> +.SH DESCRIPTION
> +.B dump.erofs
> +is used to retrieve erofs metadata (usually in a disk partition).
> +\fIdevice\fP is the special file corresponding to the device (e.g.
> +\fI/dev/sdXX\fP).

is used to retrieve erofs metadata from \fIimage\fP and demonstrate....

> +
> +Currently, it can demonstrate 1) a file information of given inode number, 2)
> +overall disk statistics, 3) file extent information,
> +4) erofs superblock information.
> +.SH OPTIONS
> +.TP
> +.BI \--nid " inode number"
> +Specify an inode number to print its file information.
> +.TP
> +.BI \-e
> +show the file extent information, the option depends on option --nid to specify nid.

	.It depends on "--nid" for a given inode.

> +.TP
> +.BI \-V
> +Print the version number and exit.
> +.TP
> +.BI \-s
> +Show superblock information of the an EROFS-formated image.

formatted

> +.TP
> +.BI \-S
> +Show statistics of the overall disk, including file type(by file extension)/size statistics and distribut$ion, number of compressed and uncompressed files, whole compression ratio of image etc.

Show EROFS disk statistics, including file type/size distribution, number of (un)compressed files, compression ratio of the whole image, etc. 

Thanks,
Gao Xiang

> +.SH AUTHOR
> +Initial code was written by Wang Qi <mpiglet@outlook.com>, Guo Xuenan <guoxuenan@huawei.com>.
> +.PP
> +This manual page was written by Guo Xuenan <guoxuenan@huawei.com>
> +.SH AVAILABILITY
> +.B dump.erofs
> +is part of erofs-utils package and is available from git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs-utils.git.
> +.SH SEE ALSO
> +.BR mkfs.erofs(1),
> +.BR fsck.erofs(1)
> -- 
> 2.31.1

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2021-10-28 11:44 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-28 10:57 [PATCH v2 1/5] erofs-utils: add support for the full decompressed length Guo Xuenan
2021-10-28 10:57 ` [PATCH v2 2/5] erofs-utils: dump: add feature for collecting filesystem statistics Guo Xuenan
2021-10-28 11:12   ` Gao Xiang
2021-10-28 10:57 ` [PATCH v2 3/5] erofs-utils: dump: add option to print specified file infomation Guo Xuenan
2021-10-28 11:20   ` Gao Xiang
2021-10-28 10:57 ` [PATCH v2 4/5] erofs-utils: dump: add support for showing file extents Guo Xuenan
2021-10-28 11:27   ` Gao Xiang
2021-10-28 11:32     ` Gao Xiang
2021-10-28 10:57 ` [PATCH v2 5/5] erofs-utils: manpage: add dump.erofs manpage Guo Xuenan
2021-10-28 11:44   ` Gao Xiang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).