ovl: check the casefold flag of directories only

On an overlay over casefolded (case-insensitive) directories, every
lookup of a file that is not a directory fails with EREMOTE, and the
first file create triggers a kernel warning. No privilege is needed: an
unprivileged user can cause both in a user namespace, with the layers on
a tmpfs that they have mounted with the casefold option.

Some filesystems support casefolding, which is case-insensitive name
lookup. It is enabled per directory, and the inode of a casefolded
directory has the S_CASEFOLD flag.

overlayfs accepts layers on a filesystem that supports casefolding as
long as casefolding is enabled on every directory of the layers, or on
none. The mount records which of the two applies in ofs->casefold.
ovl_lookup_single() compares the S_CASEFOLD flag of each file that it
finds against ofs->casefold and returns -EREMOTE on a mismatch.

Filesystems only set S_CASEFOLD on directories, so with casefolded
layers a regular file, a symlink or a special file never matches and its
lookup fails.

ovl_get_inode() does the same comparison in a WARN_ON_ONCE() for each
new overlay inode. A failed lookup never gets that far, so it is the
first file create that trips the warning.

Reproduced on an overlay over casefolded tmpfs or ext4 directories, with
stat() of a regular file, a symlink and a FIFO, and with a file create:
each stat() failed with EREMOTE. For an unprivileged user in a user
namespace, on tmpfs, stat() of a regular file failed in the same way,
and the file create triggered the warning.

Fix this by limiting both checks to directories.

Fixes: 16754d61dc ("ovl: Support mounting case-insensitive enabled layers")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason <mason@kernel.org>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
This commit is contained in:
Chris Mason 2026-10-16 21:17:17 +00:00 committed by Amir Goldstein
parent a65eb18081
commit 1d27da7de3
2 changed files with 3 additions and 2 deletions

View File

@ -1287,7 +1287,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
}
ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev);
ovl_inode_init(inode, oip, ino, fsid);
WARN_ON_ONCE(!!IS_CASEFOLDED(inode) != ofs->casefold);
WARN_ON_ONCE(is_dir && !!IS_CASEFOLDED(inode) != ofs->casefold);
if (upperdentry && ovl_is_impuredir(sb, upperdentry))
ovl_set_flag(OVL_IMPURE, inode);

View File

@ -266,7 +266,8 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
goto out_err;
}
if (ofs->casefold != ovl_dentry_casefolded(this)) {
/* Filesystems set the casefold flag only on directories */
if (d_is_dir(this) && ofs->casefold != ovl_dentry_casefolded(this)) {
warn = "child wrong casefold";
err = -EREMOTE;
goto out_warn;