diff options
| author | Christian Brauner <brauner@kernel.org> | 2026-01-29 10:07:17 +0100 |
|---|---|---|
| committer | Christian Brauner <brauner@kernel.org> | 2026-01-29 10:09:06 +0100 |
| commit | 53e8441bfcbce064dcd6e0122e271fdd5f340419 (patch) | |
| tree | 56ca56737cc09b1855e8289a63f95ddfdf90d945 | |
| parent | 9396bfdacb5aa2bcb3d2242b0de527e7d4f8a3cd (diff) | |
| parent | 9cf8ddb12a728a5b9d814dc2c12dfc0959539f9c (diff) | |
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>
| -rw-r--r-- | fs/crypto/fname.c | 2 | ||||
| -rw-r--r-- | fs/ecryptfs/crypto.c | 2 | ||||
| -rw-r--r-- | fs/exportfs/expfs.c | 3 | ||||
| -rw-r--r-- | fs/f2fs/dir.c | 2 | ||||
| -rw-r--r-- | fs/f2fs/hash.c | 2 | ||||
| -rw-r--r-- | fs/namei.c | 2 | ||||
| -rw-r--r-- | fs/overlayfs/readdir.c | 41 | ||||
| -rw-r--r-- | fs/smb/server/vfs.c | 2 | ||||
| -rw-r--r-- | include/linux/fs.h | 14 |
9 files changed, 35 insertions, 35 deletions
diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index a9a4432d12ba..629eb0d72e86 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -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); } /** diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 260f8a4938b0..3c89f06c7453 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -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); diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index d3e55de4a2a2..6c9be60a3e48 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -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; diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 48f4f98afb01..29412e6e078d 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -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) diff --git a/fs/f2fs/hash.c b/fs/f2fs/hash.c index 049ce50cec9b..14082fe5e6b2 100644 --- a/fs/f2fs/hash.c +++ b/fs/f2fs/hash.c @@ -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; } diff --git a/fs/namei.c b/fs/namei.c index 4e3a5fd370a8..aa8fbca81686 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -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--) { diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 160960bb0ad0..c665ff3546f7 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -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); diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index 98b0eb966d91..e73e968f664d 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -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; diff --git a/include/linux/fs.h b/include/linux/fs.h index 094b0adcb035..95bb9a15e109 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -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] == '.')); |
