Re: [PATCH] fuse: fix conversion of fuse_reverse_inval_entry() to start_removing()
Amir Goldstein <[email protected]>
| Newsgroups | org.kernel.vger.ecryptfs,dev.linux.lists.netfs,org.kernel.vger.linux-cifs,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-nfs,org.kernel.vger.linux-security-module,org.kernel.vger.linux-unionfs,org.kernel.vger.linux-xfs,org.kernel.vger.selinux |
|---|---|
| Message-ID | <CAOQ4uxjihcBxJzckbJis8hGcWO61QKhiqeGH+hDkTUkDhu23Ww@mail.gmail.com> |
On Sun, Nov 30, 2025 at 11:06 PM NeilBrown <[email protected]> wrote: > > > From: NeilBrown <[email protected]> > > The recent conversion of fuse_reverse_inval_entry() to use > start_removing() was wrong. > As Val Packett points out the original code did not call ->lookup > while the new code does. This can lead to a deadlock. > > Rather than using full_name_hash() and d_lookup() as the old code > did, we can use try_lookup_noperm() which combines these. Then > the result can be given to start_removing_dentry() to get the required > locks for removal. We then double check that the name hasn't > changed. > > As 'dir' needs to be used several times now, we load the dput() until > the end, and initialise to NULL so dput() is always safe. > > Reported-by: Val Packett <[email protected]> > Closes: https://lore.kernel.org/all/[email protected] > Fixes: c9ba789dad15 ("VFS: introduce start_creating_noperm() and start_removing_noperm()") > Signed-off-by: NeilBrown <[email protected]> > --- > fs/fuse/dir.c | 23 ++++++++++++++++------- > 1 file changed, 16 insertions(+), 7 deletions(-) > > diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c > index a0d5b302bcc2..8384fa96cf53 100644 > --- a/fs/fuse/dir.c > +++ b/fs/fuse/dir.c > @@ -1390,8 +1390,8 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, > { > int err = -ENOTDIR; > struct inode *parent; > - struct dentry *dir; > - struct dentry *entry; > + struct dentry *dir = NULL; > + struct dentry *entry = NULL; > > parent = fuse_ilookup(fc, parent_nodeid, NULL); > if (!parent) > @@ -1404,11 +1404,19 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, > dir = d_find_alias(parent); > if (!dir) > goto put_parent; > - > - entry = start_removing_noperm(dir, name); > - dput(dir); > - if (IS_ERR(entry)) > - goto put_parent; > + while (!entry) { > + struct dentry *child = try_lookup_noperm(name, dir); > + if (!child || IS_ERR(child)) > + goto put_parent; > + entry = start_removing_dentry(dir, child); > + dput(child); > + if (IS_ERR(entry)) > + goto put_parent; > + if (!d_same_name(entry, dir, name)) { > + end_removing(entry); > + entry = NULL; > + } > + } Can you explain why it is so important to use start_removing_dentry() around shrink_dcache_parent()? Is there a problem with reverting the change in this function instead of accomodating start_removing_dentry()? I don't think there is a point in optimizing parallel dir operations with FUSE server cache invalidation, but maybe I am missing something. Thanks, Amir.