diff options
author | Gao Xiang <hsiangkao@linux.alibaba.com> | 2023-10-17 22:44:20 +0800 |
---|---|---|
committer | Gao Xiang <hsiangkao@linux.alibaba.com> | 2023-10-17 22:53:10 +0800 |
commit | 8cbc205185a18b9510f4c1fbd54957354f696321 (patch) | |
tree | 31d8fdc8fb494e3c9ffbf5bdf5f67d621f28508e | |
parent | c0b5c3d2a85bc13c7b92a6de3be16c60c2308a0e (diff) | |
download | erofs-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.h | 5 | ||||
-rw-r--r-- | 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 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; } |