Skip to content

Commit cab0123

Browse files
neilbrownbrauner
authored andcommitted
fuse: fix conversion of fuse_reverse_inval_entry() to start_removing()
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 <val@packett.cool> Closes: https://lore.kernel.org/all/6713ea38-b583-4c86-b74a-bea55652851d@packett.cool Fixes: c9ba789 ("VFS: introduce start_creating_noperm() and start_removing_noperm()") Signed-off-by: NeilBrown <neil@brown.name> Link: https://patch.msgid.link/176454037897.634289.3566631742434963788@noble.neil.brown.name Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 0f61b18 commit cab0123

File tree

1 file changed

+16
-7
lines changed

1 file changed

+16
-7
lines changed

fs/fuse/dir.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,8 +1584,8 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
15841584
{
15851585
int err = -ENOTDIR;
15861586
struct inode *parent;
1587-
struct dentry *dir;
1588-
struct dentry *entry;
1587+
struct dentry *dir = NULL;
1588+
struct dentry *entry = NULL;
15891589

15901590
parent = fuse_ilookup(fc, parent_nodeid, NULL);
15911591
if (!parent)
@@ -1598,11 +1598,19 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
15981598
dir = d_find_alias(parent);
15991599
if (!dir)
16001600
goto put_parent;
1601-
1602-
entry = start_removing_noperm(dir, name);
1603-
dput(dir);
1604-
if (IS_ERR(entry))
1605-
goto put_parent;
1601+
while (!entry) {
1602+
struct dentry *child = try_lookup_noperm(name, dir);
1603+
if (!child || IS_ERR(child))
1604+
goto put_parent;
1605+
entry = start_removing_dentry(dir, child);
1606+
dput(child);
1607+
if (IS_ERR(entry))
1608+
goto put_parent;
1609+
if (!d_same_name(entry, dir, name)) {
1610+
end_removing(entry);
1611+
entry = NULL;
1612+
}
1613+
}
16061614

16071615
fuse_dir_changed(parent);
16081616
if (!(flags & FUSE_EXPIRE_ONLY))
@@ -1640,6 +1648,7 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
16401648

16411649
end_removing(entry);
16421650
put_parent:
1651+
dput(dir);
16431652
iput(parent);
16441653
return err;
16451654
}

0 commit comments

Comments
 (0)