summaryrefslogtreecommitdiff
path: root/fs/overlayfs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-02-16 13:00:36 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-02-16 13:00:36 -0800
commit45a43ac5acc90b8f4835eea92692f620e561a06b (patch)
tree254f816efd2f9ce1363509520921d7a88192ac56 /fs/overlayfs
parent543b9b63394ee67ecf5298fe42cbe65b21a16eac (diff)
parentdedfae78f00960d703badc500422d10e1f12b2bc (diff)
Merge tag 'vfs-7.0-rc1.misc.2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull more misc vfs updates from Christian Brauner: "Features: - Optimize close_range() from O(range size) to O(active FDs) by using find_next_bit() on the open_fds bitmap instead of linearly scanning the entire requested range. This is a significant improvement for large-range close operations on sparse file descriptor tables. - Add FS_XFLAG_VERITY file attribute for fs-verity files, retrievable via FS_IOC_FSGETXATTR and file_getattr(). The flag is read-only. Add tracepoints for fs-verity enable and verify operations, replacing the previously removed debug printk's. - Prevent nfsd from exporting special kernel filesystems like pidfs and nsfs. These filesystems have custom ->open() and ->permission() export methods that are designed for open_by_handle_at(2) only and are incompatible with nfsd. Update the exportfs documentation accordingly. Fixes: - Fix KMSAN uninit-value in ovl_fill_real() where strcmp() was used on a non-null-terminated decrypted directory entry name from fscrypt. This triggered on encrypted lower layers when the decrypted name buffer contained uninitialized tail data. The fix also adds VFS-level name_is_dot(), name_is_dotdot(), and name_is_dot_dotdot() helpers, replacing various open-coded "." and ".." checks across the tree. - Fix read-only fsflags not being reset together with xflags in vfs_fileattr_set(). Currently harmless since no read-only xflags overlap with flags, but this would cause inconsistencies for any future shared read-only flag - Return -EREMOTE instead of -ESRCH from PIDFD_GET_INFO when the target process is in a different pid namespace. This lets userspace distinguish "process exited" from "process in another namespace", matching glibc's pidfd_getpid() behavior Cleanups: - Use C-string literals in the Rust seq_file bindings, replacing the kernel::c_str!() macro (available since Rust 1.77) - Fix typo in d_walk_ret enum comment, add porting notes for the readlink_copy() calling convention change" * tag 'vfs-7.0-rc1.misc.2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: fs: add porting notes about readlink_copy() pidfs: return -EREMOTE when PIDFD_GET_INFO is called on another ns nfsd: do not allow exporting of special kernel filesystems exportfs: clarify the documentation of open()/permission() expotrfs ops fsverity: add tracepoints fs: add FS_XFLAG_VERITY for fs-verity files rust: seq_file: replace `kernel::c_str!` with C-Strings fs: dcache: fix typo in enum d_walk_ret comment 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 fs: reset read-only fsflags together with xflags fs/file: optimize close_range() complexity from O(N) to O(Sparse)
Diffstat (limited to 'fs/overlayfs')
-rw-r--r--fs/overlayfs/readdir.c41
1 files changed, 15 insertions, 26 deletions
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index 7fd415d7471e..bb09142b4e15 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -77,7 +77,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);
@@ -154,7 +155,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 */
@@ -165,7 +166,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;
@@ -561,12 +562,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;
@@ -666,8 +667,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;
@@ -756,7 +756,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;
@@ -1098,12 +1098,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;
@@ -1147,7 +1143,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)
@@ -1210,11 +1206,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);
@@ -1279,12 +1272,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);