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 masking out the high bits with S_DT_MASK before checking. Fixes: c31f91c6af96 ("fuse: don't allow signals to interrupt getdents copying") Link: https://github.com/containerd/stargz-snapshotter/issues/2214 Signed-off-by: Chunsheng Luo --- fs/overlayfs/readdir.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 160960bb0ad0..a2ac47458bf9 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -246,6 +246,9 @@ static int ovl_fill_lowest(struct ovl_readdir_data *rdd, { struct ovl_cache_entry *p; + /* Mask out high bits that may be used (e.g., fuse) */ + d_type &= S_DT_MASK; + p = ovl_cache_entry_find(rdd->root, c_name, c_len); if (p) { list_move_tail(&p->l_node, &rdd->middle); @@ -316,6 +319,9 @@ static bool ovl_fill_merge(struct dir_context *ctx, const char *name, char *cf_name = NULL; int c_len = 0, ret; + /* Mask out high bits that may be used (e.g., fuse) */ + d_type &= S_DT_MASK; + if (ofs->casefold) c_len = ovl_casefold(rdd, name, namelen, &cf_name); @@ -632,6 +638,9 @@ static bool ovl_fill_plain(struct dir_context *ctx, const char *name, struct ovl_readdir_data *rdd = container_of(ctx, struct ovl_readdir_data, ctx); + /* Mask out high bits that may be used (e.g., fuse) */ + d_type &= S_DT_MASK; + rdd->count++; p = ovl_cache_entry_new(rdd, name, namelen, NULL, 0, ino, d_type); if (p == NULL) { @@ -755,6 +764,9 @@ static bool ovl_fill_real(struct dir_context *ctx, const char *name, struct dir_context *orig_ctx = rdt->orig_ctx; bool res; + /* Mask out high bits that may be used (e.g., fuse) */ + d_type &= S_DT_MASK; + if (rdt->parent_ino && strcmp(name, "..") == 0) { ino = rdt->parent_ino; } else if (rdt->cache) { @@ -1144,6 +1156,9 @@ static bool ovl_check_d_type(struct dir_context *ctx, const char *name, struct ovl_readdir_data *rdd = container_of(ctx, struct ovl_readdir_data, ctx); + /* Mask out high bits that may be used (e.g., fuse) */ + d_type &= S_DT_MASK; + /* Even if d_type is not supported, DT_DIR is returned for . and .. */ if (!strncmp(name, ".", namelen) || !strncmp(name, "..", namelen)) return true; -- 2.43.0