Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: c9ba789dad15 ("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>

authored by

NeilBrown and committed by
Christian Brauner
cab01237 0f61b186

+16 -7
+16 -7
fs/fuse/dir.c
··· 1584 1584 { 1585 1585 int err = -ENOTDIR; 1586 1586 struct inode *parent; 1587 - struct dentry *dir; 1588 - struct dentry *entry; 1587 + struct dentry *dir = NULL; 1588 + struct dentry *entry = NULL; 1589 1589 1590 1590 parent = fuse_ilookup(fc, parent_nodeid, NULL); 1591 1591 if (!parent) ··· 1598 1598 dir = d_find_alias(parent); 1599 1599 if (!dir) 1600 1600 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 + } 1606 1614 1607 1615 fuse_dir_changed(parent); 1608 1616 if (!(flags & FUSE_EXPIRE_ONLY)) ··· 1648 1640 1649 1641 end_removing(entry); 1650 1642 put_parent: 1643 + dput(dir); 1651 1644 iput(parent); 1652 1645 return err; 1653 1646 }