[PATCH] ocfs2: fix hung task in orphan recovery
Jiaming Zhang <[email protected]> Wed, 1 Jul 2026 17:16:13 +0800
| Newsgroups | dev.linux.lists.ocfs2-devel,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
A crafted OCFS2 image with corrupted orphan-directory extent metadata can make umount hang. During unmount, ocfs2_recovery_disable() waits for the ocfs2_complete_recovery work item to finish. The worker scans the orphan directory through ocfs2_queue_orphans() and ocfs2_dir_foreach(). If ocfs2_read_dir_block() fails on a corrupted directory block, ocfs2_dir_foreach_blk_el() skips the block and continues walking. On a badly corrupted directory this can keep orphan recovery busy for a long time, leaving umount blocked while flushing osb->ocfs2_wq. Return the read error immediately for full directory scans and propagate the error from ocfs2_dir_foreach(). When ocfs2_empty_dir() receives such an error, report the directory as non-empty so unlink/rmdir does not proceed on an unreadable directory. Closes: https://lore.kernel.org/lkml/CANypQFbWH76Y6LWHEwAvTP7aQL04uMJ=dDyL6YDmxa3fv3Tyjg@mail.gmail.com/ Assisted-by: Codex:gpt-5.5-xhigh Signed-off-by: Jiaming Zhang <[email protected]> --- fs/ocfs2/dir.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index 8e6b03238327..9bebf28f4b77 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -1866,6 +1866,7 @@ static int ocfs2_dir_foreach_blk_el(struct inode *inode, struct super_block * sb = inode->i_sb; unsigned int ra_sectors = 16; int stored = 0; + int ret = 0; bh = NULL; @@ -1873,9 +1874,13 @@ static int ocfs2_dir_foreach_blk_el(struct inode *inode, while (ctx->pos < i_size_read(inode)) { blk = ctx->pos >> sb->s_blocksize_bits; - if (ocfs2_read_dir_block(inode, blk, &bh, 0)) { + ret = ocfs2_read_dir_block(inode, blk, &bh, 0); + if (ret) { + if (persist) + return ret; /* Skip the corrupt dirblock and keep trying */ ctx->pos += sb->s_blocksize - offset; + offset = 0; continue; } @@ -1950,7 +1955,7 @@ static int ocfs2_dir_foreach_blk_el(struct inode *inode, if (!persist && stored) break; } - return 0; + return ret; } static int ocfs2_dir_foreach_blk(struct inode *inode, u64 *f_version, @@ -1969,8 +1974,7 @@ static int ocfs2_dir_foreach_blk(struct inode *inode, u64 *f_version, int ocfs2_dir_foreach(struct inode *inode, struct dir_context *ctx) { u64 version = inode_query_iversion(inode); - ocfs2_dir_foreach_blk(inode, &version, ctx, true); - return 0; + return ocfs2_dir_foreach_blk(inode, &version, ctx, true); } /* @@ -2189,8 +2193,10 @@ int ocfs2_empty_dir(struct inode *inode) } ret = ocfs2_dir_foreach(inode, &priv.ctx); - if (ret) + if (ret) { mlog_errno(ret); + return 0; + } if (!priv.seen_dot || !priv.seen_dot_dot) { mlog(ML_ERROR, "bad directory (dir #%llu) - no `.' or `..'\n", -- 2.43.0