aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGao Xiang <hsiangkao@linux.alibaba.com>2023-10-17 22:44:20 +0800
committerGao Xiang <hsiangkao@linux.alibaba.com>2023-10-17 22:53:10 +0800
commit8cbc205185a18b9510f4c1fbd54957354f696321 (patch)
tree31d8fdc8fb494e3c9ffbf5bdf5f67d621f28508e
parentc0b5c3d2a85bc13c7b92a6de3be16c60c2308a0e (diff)
downloaderofs-utils-8cbc205185a18b9510f4c1fbd54957354f696321.tar.gz
erofs-utils: mkfs: fix corrupted directories with hardlinks
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@linux.alibaba.com> Link: https://lore.kernel.org/r/20231017144420.289469-1-hsiangkao@linux.alibaba.com
-rw-r--r--include/erofs/internal.h5
-rw-r--r--lib/inode.c29
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 fb062a1..71af396 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -1210,7 +1210,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);
@@ -1235,17 +1234,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))
@@ -1253,9 +1245,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;
@@ -1275,15 +1267,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;
}