[PATCH] erofs-utils: mkfs: fix corrupted directories with hardlinks

Gao Xiang hsiangkao at linux.alibaba.com
Wed Oct 18 01:44:20 AEDT 2023


An inode with hard links may belong to several directories. It's
invalid to update `subdirs_queued` for hard-link inodes since it
only records one of the parent directories.

References: https://github.com/NixOS/nixpkgs/issues/261394
Fixes: 21d84349e79a ("erofs-utils: rearrange on-disk metadata")
Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
 include/erofs/internal.h |  5 +++--
 lib/inode.c              | 29 ++++++++++++-----------------
 2 files changed, 15 insertions(+), 19 deletions(-)

diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index d859905..c1ff582 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -159,8 +159,9 @@ struct erofs_inode {
 	union {
 		/* (erofsfuse) runtime flags */
 		unsigned int flags;
-		/* (mkfs.erofs) queued sub-directories blocking dump */
-		u32 subdirs_queued;
+
+		/* (mkfs.erofs) next pointer for directory dumping */
+		struct erofs_inode *next_dirwrite;
 	};
 	unsigned int i_count;
 	struct erofs_sb_info *sbi;
diff --git a/lib/inode.c b/lib/inode.c
index a91578a..dd242a1 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -1229,7 +1229,6 @@ fail:
 			inode->i_parent = dir;
 			erofs_igrab(inode);
 			list_add_tail(&inode->i_subdirs, dirs);
-			++dir->subdirs_queued;
 		}
 		ftype = erofs_mode_to_ftype(inode->i_mode);
 		i_nlink += (ftype == EROFS_FT_DIR);
@@ -1254,17 +1253,10 @@ err_closedir:
 	return ret;
 }
 
-static void erofs_mkfs_dump_directory(struct erofs_inode *dir)
-{
-	erofs_write_dir_file(dir);
-	erofs_write_tail_end(dir);
-	dir->bh->op = &erofs_write_inode_bhops;
-}
-
 struct erofs_inode *erofs_mkfs_build_tree_from_path(const char *path)
 {
 	LIST_HEAD(dirs);
-	struct erofs_inode *inode, *root, *parent;
+	struct erofs_inode *inode, *root, *dumpdir;
 
 	root = erofs_iget_from_path(path, true);
 	if (IS_ERR(root))
@@ -1272,9 +1264,9 @@ struct erofs_inode *erofs_mkfs_build_tree_from_path(const char *path)
 
 	(void)erofs_igrab(root);
 	root->i_parent = root;	/* rootdir mark */
-	root->subdirs_queued = 1;
 	list_add(&root->i_subdirs, &dirs);
 
+	dumpdir = NULL;
 	do {
 		int err;
 		char *trimmed;
@@ -1294,15 +1286,18 @@ struct erofs_inode *erofs_mkfs_build_tree_from_path(const char *path)
 			root = ERR_PTR(err);
 			break;
 		}
-		parent = inode->i_parent;
 
-		DBG_BUGON(!parent->subdirs_queued);
-		if (S_ISDIR(inode->i_mode) && !inode->subdirs_queued)
-			erofs_mkfs_dump_directory(inode);
-		if (!--parent->subdirs_queued)
-			erofs_mkfs_dump_directory(parent);
-		erofs_iput(inode);
+		if (S_ISDIR(inode->i_mode)) {
+			inode->next_dirwrite = dumpdir;
+			dumpdir = inode;
+		}
 	} while (!list_empty(&dirs));
+
+	for (; dumpdir; dumpdir = dumpdir->next_dirwrite) {
+		erofs_write_dir_file(dumpdir);
+		erofs_write_tail_end(dumpdir);
+		dumpdir->bh->op = &erofs_write_inode_bhops;
+	}
 	return root;
 }
 
-- 
2.39.3



More information about the Linux-erofs mailing list