[PATCH 7/7] erofs-utils: lib: introduce directory writer

Gao Xiang hsiangkao at linux.alibaba.com
Thu Oct 16 13:48:15 AEDT 2025


Directories are now written to disk using virtual files too.

Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
 lib/inode.c | 153 ++++++++++++++++++++++++++++++----------------------
 1 file changed, 90 insertions(+), 63 deletions(-)

diff --git a/lib/inode.c b/lib/inode.c
index 264c4ae..7587248 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -377,16 +377,6 @@ static void fill_dirblock(char *buf, unsigned int size, unsigned int q,
 	memset(buf + q, 0, size - q);
 }
 
-static int write_dirblock(struct erofs_sb_info *sbi,
-			  unsigned int q, struct erofs_dentry *head,
-			  struct erofs_dentry *end, erofs_blk_t blkaddr)
-{
-	char buf[EROFS_MAX_BLOCK_SIZE];
-
-	fill_dirblock(buf, erofs_blksiz(sbi), q, head, end);
-	return erofs_blk_write(sbi, buf, blkaddr, 1);
-}
-
 erofs_nid_t erofs_lookupnid(struct erofs_inode *inode)
 {
 	struct erofs_buffer_head *const bh = inode->bh;
@@ -515,64 +505,85 @@ static int erofs_rebuild_inode_fix_pnid(struct erofs_inode *parent,
 	return -EFSCORRUPTED;
 }
 
-static int erofs_write_dir_file(struct erofs_inode *dir)
-{
-	struct erofs_dentry *head = list_first_entry(&dir->i_subdirs,
-						     struct erofs_dentry,
-						     d_child);
-	struct erofs_sb_info *sbi = dir->sbi;
-	struct erofs_dentry *d;
-	int ret;
-	unsigned int q, used, blkno;
-
-	q = used = blkno = 0;
-
-	/* allocate dir main data */
-	ret = erofs_allocate_inode_bh_data(dir, erofs_blknr(sbi, dir->i_size));
-	if (ret)
-		return ret;
-
-	list_for_each_entry(d, &dir->i_subdirs, d_child) {
-		unsigned int len = d->namelen + sizeof(struct erofs_dirent);
-
-		/* XXX: a bit hacky, but to avoid another traversal */
-		if (d->flags & EROFS_DENTRY_FLAG_FIXUP_PNID) {
-			ret = erofs_rebuild_inode_fix_pnid(dir, d->nid);
-			if (ret)
-				return ret;
-		}
-
-		erofs_d_invalidate(d);
-		if (used + len > erofs_blksiz(sbi)) {
-			ret = write_dirblock(sbi, q, head, d,
-					     dir->u.i_blkaddr + blkno);
-			if (ret)
-				return ret;
+struct erofs_dirwriter_vf {
+	struct erofs_vfile vf;
+	struct erofs_inode *dir;
+	struct list_head *head;
+	erofs_off_t offset;
+	char dirdata[];
+};
 
-			head = d;
+static ssize_t erofs_dirwriter_vfread(struct erofs_vfile *vf,
+				      void *buf, size_t len)
+{
+	struct erofs_dirwriter_vf *dwv = (struct erofs_dirwriter_vf *)vf;
+	struct erofs_inode *dir = dwv->dir;
+	unsigned int bsz = erofs_blksiz(dir->sbi);
+	size_t processed = 0;
+
+	if (len > dir->i_size - dwv->offset)
+		len = dir->i_size - dwv->offset;
+	while (processed < len) {
+		unsigned int off, dblen, count;
+
+		off = dwv->offset & (bsz - 1);
+		dblen = min_t(u64, dir->i_size - dwv->offset + off, bsz);
+		/* generate a directory block to `dwv->dirdata` */
+		if (!off) {
+			struct erofs_dentry *head, *d;
+			unsigned int q, used, len;
+			int err;
+
+			d = head = list_entry(dwv->head,
+					      struct erofs_dentry, d_child);
 			q = used = 0;
-			++blkno;
+			do {
+				/* XXX: a bit hacky, but avoids another traversal */
+				if (d->flags & EROFS_DENTRY_FLAG_FIXUP_PNID) {
+					err = erofs_rebuild_inode_fix_pnid(dir, d->nid);
+					if (err)
+						return err;
+				}
+				len = d->namelen + sizeof(struct erofs_dirent);
+				erofs_d_invalidate(d);
+				if ((used += len) > bsz)
+					break;
+				d = list_next_entry(d, d_child);
+				q += sizeof(struct erofs_dirent);
+			} while (&d->d_child != &dir->i_subdirs);
+			fill_dirblock(dwv->dirdata, dblen, q, head, d);
+			dwv->head = &d->d_child;
 		}
-		used += len;
-		q += sizeof(struct erofs_dirent);
+		count = min_t(size_t, dblen - off, len - processed);
+		memcpy(buf + processed, dwv->dirdata + off, count);
+		processed += count;
+		dwv->offset += count;
 	}
+	return processed;
+}
 
-	DBG_BUGON(used > erofs_blksiz(sbi));
-	if (used == erofs_blksiz(sbi)) {
-		DBG_BUGON(dir->i_size % erofs_blksiz(sbi));
-		DBG_BUGON(dir->idata_size);
-		return write_dirblock(sbi, q, head, d, dir->u.i_blkaddr + blkno);
-	}
-	DBG_BUGON(used != dir->i_size % erofs_blksiz(sbi));
-	if (used) {
-		/* fill tail-end dir block */
-		dir->idata = malloc(used);
-		if (!dir->idata)
-			return -ENOMEM;
-		DBG_BUGON(used != dir->idata_size);
-		fill_dirblock(dir->idata, dir->idata_size, q, head, d);
-	}
-	return 0;
+void erofs_dirwriter_vfclose(struct erofs_vfile *vf)
+{
+	free((void *)vf);
+}
+
+static struct erofs_vfops erofs_dirwriter_vfops = {
+	.read = erofs_dirwriter_vfread,
+	.close = erofs_dirwriter_vfclose,
+};
+
+static struct erofs_vfile *erofs_dirwriter_open(struct erofs_inode *dir)
+{
+	struct erofs_dirwriter_vf *dwv;
+
+	dwv = malloc(sizeof(*dwv) + erofs_blksiz(dir->sbi));
+	if (!dwv)
+		return ERR_PTR(-ENOMEM);
+	dwv->vf.ops = &erofs_dirwriter_vfops;
+	dwv->dir = dir;
+	dwv->head = dir->i_subdirs.next;
+	dwv->offset = 0;
+	return (struct erofs_vfile *)dwv;
 }
 
 int erofs_write_file_from_buffer(struct erofs_inode *inode, char *buf)
@@ -686,6 +697,22 @@ int erofs_write_unencoded_file(struct erofs_inode *inode, int fd, u64 fpos)
 			inode->datasource == EROFS_INODE_DATA_SOURCE_DISKBUF);
 }
 
+static int erofs_write_dir_file(struct erofs_inode *dir)
+{
+	unsigned int bsz = erofs_blksiz(dir->sbi);
+	struct erofs_vfile *vf;
+	int err;
+
+	DBG_BUGON(dir->idata_size != (dir->i_size & (bsz - 1)));
+	vf = erofs_dirwriter_open(dir);
+	if (IS_ERR(vf))
+		return PTR_ERR(vf);
+
+	err = erofs_write_unencoded_data(dir, vf, 0, true);
+	erofs_io_close(vf);
+	return err;
+}
+
 int erofs_iflush(struct erofs_inode *inode)
 {
 	u16 icount = EROFS_INODE_XATTR_ICOUNT(inode->xattr_isize);
-- 
2.43.5



More information about the Linux-erofs mailing list