Re: [PATCH v3] fs: let backing files inherit FMODE_NONOTIFY from the user file
From: Edvard Conrad
Date: Tue Nov 03 2026 - 08:05:29 EST
On Mon, Nov 2, 2026 at 1:34 PM Amir Goldstein <amir73il@xxxxxxxxx> wrote:
> Good catch!
> Please test the attached v2 patch and post the new test cases.
v2 fixes the merged directory. An impure directory still deadlocks the
same way: ovl_iterate_real() reads it through ovl_cache_get_impure() and
ovl_dir_read_impure(), which v2 leaves without the user file, so
ovl_dir_read() opens the upper directory with plain ovl_path_open():
fanotify_handle_event
fsnotify
fsnotify_open_perm_and_set_mode
do_dentry_open
vfs_open
dentry_open
ovl_iterate_real
ovl_iterate
iterate_dir
__x64_sys_getdents64
That takes an upper-only directory that a lower file was renamed into,
on layers that share a filesystem, so ovl_need_adjust_d_ino() sends it
to ovl_iterate_real(). Passing the file down the same way v2 does for
the merged case fixes it for me, on top of v2:
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -670,7 +670,7 @@ static bool ovl_fill_plain(struct dir_context *ctx, const char *name,
}
static int ovl_dir_read_impure(const struct path *path, struct list_head *list,
- struct rb_root *root)
+ struct rb_root *root, const struct file *user_file)
{
int err;
struct path realpath;
@@ -678,6 +678,7 @@ static int ovl_dir_read_impure(const struct path *path, struct list_head *list,
struct ovl_readdir_data rdd = {
.ctx.actor = ovl_fill_plain,
.ctx.count = INT_MAX,
+ .user_file = user_file,
.list = list,
.root = root,
};
@@ -714,9 +715,10 @@ static int ovl_dir_read_impure(const struct path *path, struct list_head *list,
return 0;
}
-static struct ovl_dir_cache *ovl_cache_get_impure(const struct path *path)
+static struct ovl_dir_cache *ovl_cache_get_impure(struct file *file)
{
int res;
+ const struct path *path = &file->f_path;
struct dentry *dentry = path->dentry;
struct inode *inode = d_inode(dentry);
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
@@ -735,7 +737,7 @@ static struct ovl_dir_cache *ovl_cache_get_impure(const struct path *path)
return ERR_PTR(-ENOMEM);
cache->is_refcounted = false;
- res = ovl_dir_read_impure(path, &cache->entries, &cache->root);
+ res = ovl_dir_read_impure(path, &cache->entries, &cache->root, file);
if (res) {
ovl_cache_free(&cache->entries);
kfree(cache);
@@ -856,7 +858,7 @@ static int ovl_iterate_real(struct file *file, struct dir_context *ctx)
dput(parent);
if (ovl_is_impure_dir(file)) {
- rdt.cache = ovl_cache_get_impure(&file->f_path);
+ rdt.cache = ovl_cache_get_impure(file);
if (IS_ERR(rdt.cache))
return PTR_ERR(rdt.cache);
}
Results in a VM on 6b4b543fa18, each kernel run once and with -i 2,
with the backing filesystem marked:
file dir merged dir impure dir
listed listed
+ c3d355f427ce PASS FAIL FAIL FAIL
+ your v1 PASS PASS FAIL FAIL
+ your v2 PASS PASS PASS FAIL
+ v2 and the diff above PASS PASS PASS PASS
The new cases are posted on top of the directory cases:
https://lore.kernel.org/ltp/P5WKBbq--F-9@xxxxxxx/
Thanks,
Edvard