Merge patch series "name_is_dot* cleanup"

Amir Goldstein <amir73il@gmail.com> says:

Following the syzbot ovl bug report and a fix by Qing Wang,
I decided to follow up with a small vfs cleanup of some
open coded version of checking "." and ".." name in readdir.

The fix patch is applied at the start of this cleanup series to allow
for easy backporting, but it is not an urgent fix so I don't think
there is a need to fast track it.

* patches from https://patch.msgid.link/20260128132406.23768-1-amir73il@gmail.com:
  ovl: use name_is_dot* helpers in readdir code
  fs: add helpers name_is_dot{,dot,_dotdot}
  ovl: Fix uninit-value in ovl_fill_real

Link: https://patch.msgid.link/20260128132406.23768-1-amir73il@gmail.com
Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2026-01-29 10:07:17 +01:00
commit 53e8441bfc
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
9 changed files with 35 additions and 35 deletions

View File

@ -76,7 +76,7 @@ struct fscrypt_nokey_name {
static inline bool fscrypt_is_dot_dotdot(const struct qstr *str)
{
return is_dot_dotdot(str->name, str->len);
return name_is_dot_dotdot(str->name, str->len);
}
/**

View File

@ -1904,7 +1904,7 @@ int ecryptfs_decode_and_decrypt_filename(char **plaintext_name,
if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) &&
!(mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED)) {
if (is_dot_dotdot(name, name_size)) {
if (name_is_dot_dotdot(name, name_size)) {
rc = ecryptfs_copy_filename(plaintext_name,
plaintext_name_size,
name, name_size);

View File

@ -253,7 +253,8 @@ static bool filldir_one(struct dir_context *ctx, const char *name, int len,
container_of(ctx, struct getdents_callback, ctx);
buf->sequence++;
if (buf->ino == ino && len <= NAME_MAX && !is_dot_dotdot(name, len)) {
if (buf->ino == ino && len <= NAME_MAX &&
!name_is_dot_dotdot(name, len)) {
memcpy(buf->name, name, len);
buf->name[len] = '\0';
buf->found = 1;

View File

@ -67,7 +67,7 @@ int f2fs_init_casefolded_name(const struct inode *dir,
int len;
if (IS_CASEFOLDED(dir) &&
!is_dot_dotdot(fname->usr_fname->name, fname->usr_fname->len)) {
!name_is_dot_dotdot(fname->usr_fname->name, fname->usr_fname->len)) {
buf = f2fs_kmem_cache_alloc(f2fs_cf_name_slab,
GFP_NOFS, false, F2FS_SB(sb));
if (!buf)

View File

@ -100,7 +100,7 @@ void f2fs_hash_filename(const struct inode *dir, struct f2fs_filename *fname)
WARN_ON_ONCE(!name);
if (is_dot_dotdot(name, len)) {
if (name_is_dot_dotdot(name, len)) {
fname->hash = 0;
return;
}

View File

@ -3042,7 +3042,7 @@ int lookup_noperm_common(struct qstr *qname, struct dentry *base)
if (!len)
return -EACCES;
if (is_dot_dotdot(name, len))
if (name_is_dot_dotdot(name, len))
return -EACCES;
while (len--) {

View File

@ -76,7 +76,8 @@ static int ovl_casefold(struct ovl_readdir_data *rdd, const char *str, int len,
char *cf_name;
int cf_len;
if (!IS_ENABLED(CONFIG_UNICODE) || !rdd->map || is_dot_dotdot(str, len))
if (!IS_ENABLED(CONFIG_UNICODE) || !rdd->map ||
name_is_dot_dotdot(str, len))
return 0;
cf_name = kmalloc(NAME_MAX, GFP_KERNEL);
@ -153,7 +154,7 @@ static bool ovl_calc_d_ino(struct ovl_readdir_data *rdd,
return true;
/* Always recalc d_ino for parent */
if (strcmp(p->name, "..") == 0)
if (name_is_dotdot(p->name, p->len))
return true;
/* If this is lower, then native d_ino will do */
@ -164,7 +165,7 @@ static bool ovl_calc_d_ino(struct ovl_readdir_data *rdd,
* Recalc d_ino for '.' and for all entries if dir is impure (contains
* copied up entries)
*/
if ((p->name[0] == '.' && p->len == 1) ||
if (name_is_dot(p->name, p->len) ||
ovl_test_flag(OVL_IMPURE, d_inode(rdd->dentry)))
return true;
@ -560,12 +561,12 @@ static int ovl_cache_update(const struct path *path, struct ovl_cache_entry *p,
if (!ovl_same_dev(ofs) && !p->check_xwhiteout)
goto out;
if (p->name[0] == '.') {
if (name_is_dot_dotdot(p->name, p->len)) {
if (p->len == 1) {
this = dget(dir);
goto get;
}
if (p->len == 2 && p->name[1] == '.') {
if (p->len == 2) {
/* we shall not be moved */
this = dget(dir->d_parent);
goto get;
@ -665,8 +666,7 @@ static int ovl_dir_read_impure(const struct path *path, struct list_head *list,
return err;
list_for_each_entry_safe(p, n, list, l_node) {
if (strcmp(p->name, ".") != 0 &&
strcmp(p->name, "..") != 0) {
if (!name_is_dot_dotdot(p->name, p->len)) {
err = ovl_cache_update(path, p, true);
if (err)
return err;
@ -755,7 +755,7 @@ static bool ovl_fill_real(struct dir_context *ctx, const char *name,
struct dir_context *orig_ctx = rdt->orig_ctx;
bool res;
if (rdt->parent_ino && strcmp(name, "..") == 0) {
if (rdt->parent_ino && name_is_dotdot(name, namelen)) {
ino = rdt->parent_ino;
} else if (rdt->cache) {
struct ovl_cache_entry *p;
@ -1096,12 +1096,8 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
goto del_entry;
}
if (p->name[0] == '.') {
if (p->len == 1)
goto del_entry;
if (p->len == 2 && p->name[1] == '.')
goto del_entry;
}
if (name_is_dot_dotdot(p->name, p->len))
goto del_entry;
err = -ENOTEMPTY;
break;
@ -1145,7 +1141,7 @@ static bool ovl_check_d_type(struct dir_context *ctx, const char *name,
container_of(ctx, struct ovl_readdir_data, ctx);
/* Even if d_type is not supported, DT_DIR is returned for . and .. */
if (!strncmp(name, ".", namelen) || !strncmp(name, "..", namelen))
if (name_is_dot_dotdot(name, namelen))
return true;
if (d_type != DT_UNKNOWN)
@ -1208,11 +1204,8 @@ static int ovl_workdir_cleanup_recurse(struct ovl_fs *ofs, const struct path *pa
list_for_each_entry(p, &list, l_node) {
struct dentry *dentry;
if (p->name[0] == '.') {
if (p->len == 1)
continue;
if (p->len == 2 && p->name[1] == '.')
continue;
if (name_is_dot_dotdot(p->name, p->len)) {
continue;
} else if (incompat) {
pr_err("overlay with incompat feature '%s' cannot be mounted\n",
p->name);
@ -1277,12 +1270,8 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs)
goto out;
list_for_each_entry(p, &list, l_node) {
if (p->name[0] == '.') {
if (p->len == 1)
continue;
if (p->len == 2 && p->name[1] == '.')
continue;
}
if (name_is_dot_dotdot(p->name, p->len))
continue;
index = ovl_lookup_upper_unlocked(ofs, p->name, indexdir, p->len);
if (IS_ERR(index)) {
err = PTR_ERR(index);

View File

@ -1052,7 +1052,7 @@ static bool __dir_empty(struct dir_context *ctx, const char *name, int namlen,
struct ksmbd_readdir_data *buf;
buf = container_of(ctx, struct ksmbd_readdir_data, ctx);
if (!is_dot_dotdot(name, namlen))
if (!name_is_dot_dotdot(name, namlen))
buf->dirent_count++;
return !buf->dirent_count;

View File

@ -2844,12 +2844,22 @@ u64 vfsmount_to_propagation_flags(struct vfsmount *mnt);
extern char *file_path(struct file *, char *, int);
static inline bool name_is_dot(const char *name, size_t len)
{
return unlikely(len == 1 && name[0] == '.');
}
static inline bool name_is_dotdot(const char *name, size_t len)
{
return unlikely(len == 2 && name[0] == '.' && name[1] == '.');
}
/**
* is_dot_dotdot - returns true only if @name is "." or ".."
* name_is_dot_dotdot - returns true only if @name is "." or ".."
* @name: file name to check
* @len: length of file name, in bytes
*/
static inline bool is_dot_dotdot(const char *name, size_t len)
static inline bool name_is_dot_dotdot(const char *name, size_t len)
{
return len && unlikely(name[0] == '.') &&
(len == 1 || (len == 2 && name[1] == '.'));