[PATCH] erofs-utils: lib: dynamically sized `struct erofs_dentry`

Gao Xiang hsiangkao at linux.alibaba.com
Wed Jan 15 13:27:20 AEDT 2025


 - Reduced memory footprints;
 - Optimize dname sorting and strlen(dname).

Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
 include/erofs/internal.h |  4 +++-
 lib/inode.c              | 29 +++++++++++++++++++++--------
 2 files changed, 24 insertions(+), 9 deletions(-)

diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 2edc1b4..596e363 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -314,15 +314,17 @@ static inline struct erofs_inode *erofs_parent_inode(struct erofs_inode *inode)
 
 #define IS_ROOT(x)	((x) == erofs_parent_inode(x))
 
+#define EROFS_DENTRY_NAME_ALIGNMENT	4
 struct erofs_dentry {
 	struct list_head d_child;	/* child of parent list */
 	union {
 		struct erofs_inode *inode;
 		erofs_nid_t nid;
 	};
-	char name[EROFS_NAME_LEN];
+	unsigned char namelen;
 	u8 type;
 	bool validnid;
+	char name[];
 };
 
 static inline bool is_dot_dotdot_len(const char *name, unsigned int len)
diff --git a/lib/inode.c b/lib/inode.c
index 7ee5d78..e51c0fc 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -160,14 +160,22 @@ unsigned int erofs_iput(struct erofs_inode *inode)
 struct erofs_dentry *erofs_d_alloc(struct erofs_inode *parent,
 				   const char *name)
 {
-	struct erofs_dentry *d = malloc(sizeof(*d));
+	unsigned int namelen = strlen(name);
+	unsigned int fsz = round_up(namelen + 1, EROFS_DENTRY_NAME_ALIGNMENT);
+	struct erofs_dentry *d;
 
+	if (namelen > EROFS_NAME_LEN) {
+		DBG_BUGON(1);
+		return ERR_PTR(-ENAMETOOLONG);
+	}
+	d = malloc(sizeof(*d) + fsz);
 	if (!d)
 		return ERR_PTR(-ENOMEM);
 
-	strncpy(d->name, name, EROFS_NAME_LEN - 1);
-	d->name[EROFS_NAME_LEN - 1] = '\0';
+	memcpy(d->name, name, namelen);
+	memset(d->name + namelen, 0, fsz - namelen);
 	d->inode = NULL;
+	d->namelen = namelen;
 	d->type = EROFS_FT_UNKNOWN;
 	d->validnid = false;
 	list_add_tail(&d->d_child, &parent->i_subdirs);
@@ -208,10 +216,16 @@ int erofs_allocate_inode_bh_data(struct erofs_inode *inode, erofs_blk_t nblocks)
 static int comp_subdir(const void *a, const void *b)
 {
 	const struct erofs_dentry *da, *db;
+	int commonlen, sign;
 
 	da = *((const struct erofs_dentry **)a);
 	db = *((const struct erofs_dentry **)b);
-	return strcmp(da->name, db->name);
+	commonlen = min(round_up(da->namelen, EROFS_DENTRY_NAME_ALIGNMENT),
+			round_up(db->namelen, EROFS_DENTRY_NAME_ALIGNMENT));
+	sign = memcmp(da->name, db->name, commonlen);
+	if (sign)
+		return sign;
+	return cmpsgn(da->namelen, db->namelen);
 }
 
 int erofs_init_empty_dir(struct erofs_inode *dir)
@@ -260,7 +274,7 @@ static int erofs_prepare_dir_file(struct erofs_inode *dir,
 
 	/* let's calculate dir size */
 	list_for_each_entry(d, &dir->i_subdirs, d_child) {
-		int len = strlen(d->name) + sizeof(struct erofs_dirent);
+		int len = d->namelen + sizeof(struct erofs_dirent);
 
 		if (erofs_blkoff(sbi, d_size) + len > erofs_blksiz(sbi))
 			d_size = round_up(d_size, erofs_blksiz(sbi));
@@ -283,7 +297,7 @@ static void fill_dirblock(char *buf, unsigned int size, unsigned int q,
 
 	/* write out all erofs_dirents + filenames */
 	while (head != end) {
-		const unsigned int namelen = strlen(head->name);
+		const unsigned int namelen = head->namelen;
 		struct erofs_dirent d = {
 			.nid = cpu_to_le64(head->nid),
 			.nameoff = cpu_to_le16(q),
@@ -438,8 +452,7 @@ static int erofs_write_dir_file(struct erofs_inode *dir)
 		return ret;
 
 	list_for_each_entry(d, &dir->i_subdirs, d_child) {
-		const unsigned int len = strlen(d->name) +
-			sizeof(struct erofs_dirent);
+		unsigned int len = d->namelen + sizeof(struct erofs_dirent);
 
 		/* XXX: a bit hacky, but to avoid another traversal */
 		if (d->validnid && d->type == EROFS_FT_DIR) {
-- 
2.43.5



More information about the Linux-erofs mailing list