[PATCH 6.18 001/122] readdir: require opt-in for d_type flags

Greg Kroah-Hartman
2026-02-04T15:28:25Z

From: Amir Goldstein <amir73il@gmail.com>

[ Upstream commit c644bce62b9c6b441143a03c910f986109c47001 ]

Commit c31f91c6af96 ("fuse: don't allow signals to interrupt getdents
copying") introduced the use of high bits in d_type as flags. However,
overlayfs was not adapted to handle this change.

In ovl_cache_entry_new(), the code checks if d_type == DT_CHR to
determine if an entry might be a whiteout. When fuse is used as the
lower layer and sets high bits in d_type, this comparison fails,
causing whiteout files to not be recognized properly and resulting in
incorrect overlayfs behavior.

Fix this by requiring callers of iterate_dir() to opt-in for getting
flag bits in d_type outside of S_DT_MASK.

Fixes: c31f91c6af96 ("fuse: don't allow signals to interrupt getdents copying")
Link: https://lore.kernel.org/all/20260107034551.439-1-luochunsheng@ustc.edu/
Link: https://github.com/containerd/stargz-snapshotter/issues/2214
Reported-by: Chunsheng Luo <luochunsheng@ustc.edu>
Reviewed-by: Chunsheng Luo <luochunsheng@ustc.edu>
Tested-by: Chunsheng Luo <luochunsheng@ustc.edu>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Link: https://patch.msgid.link/20260108074522.3400998-1-amir73il@gmail.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/readdir.c | 3 +++
include/linux/fs.h | 6 +++++-
2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/fs/readdir.c b/fs/readdir.c
index 7764b86389788..73707b6816e9a 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -316,6 +316,7 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
struct getdents_callback buf = {
.ctx.actor = filldir,
.ctx.count = count,
+ .ctx.dt_flags_mask = FILLDIR_FLAG_NOINTR,
.current_dir = dirent
};
int error;
@@ -400,6 +401,7 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd,
struct getdents_callback64 buf = {
.ctx.actor = filldir64,
.ctx.count = count,
+ .ctx.dt_flags_mask = FILLDIR_FLAG_NOINTR,
.current_dir = dirent
};
int error;
@@ -569,6 +571,7 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
struct compat_getdents_callback buf = {
.ctx.actor = compat_filldir,
.ctx.count = count,
+ .ctx.dt_flags_mask = FILLDIR_FLAG_NOINTR,
.current_dir = dirent,
};
int error;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 9b2230fb2332f..3e965c77fa1b1 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2207,6 +2207,8 @@ struct dir_context {
* INT_MAX unlimited
*/
int count;
+ /* @actor supports these flags in d_type high bits */
+ unsigned int dt_flags_mask;
};

/* If OR-ed with d_type, pending signals are not checked */
@@ -3985,7 +3987,9 @@ static inline bool dir_emit(struct dir_context *ctx,
const char *name, int namelen,
u64 ino, unsigned type)
{
- return ctx->actor(ctx, name, namelen, ctx->pos, ino, type);
+ unsigned int dt_mask = S_DT_MASK | ctx->dt_flags_mask;
+
+ return ctx->actor(ctx, name, namelen, ctx->pos, ino, type & dt_mask);
}
static inline bool dir_emit_dot(struct file *file, struct dir_context *ctx)
{
--
2.51.0