Skip to content

Commit e1c8c30

Browse files
committed
smb: client: fix race with concurrent opens in unlink(2)
jira LE-4159 Rebuild_History Non-Buildable kernel-5.14.0-570.41.1.el9_6 commit-author Paulo Alcantara <[email protected]> commit 0af1561 According to some logs reported by customers, CIFS client might end up reporting unlinked files as existing in stat(2) due to concurrent opens racing with unlink(2). Besides sending the removal request to the server, the unlink process could involve closing any deferred close as well as marking all existing open handles as deleted to prevent them from deferring closes, which increases the race window for potential concurrent opens. Fix this by unhashing the dentry in cifs_unlink() to prevent any subsequent opens. Any open attempts, while we're still unlinking, will block on parent's i_rwsem. Reported-by: Jay Shin <[email protected]> Signed-off-by: Paulo Alcantara (Red Hat) <[email protected]> Reviewed-by: David Howells <[email protected]> Cc: Al Viro <[email protected]> Cc: [email protected] Signed-off-by: Steve French <[email protected]> (cherry picked from commit 0af1561) Signed-off-by: Jonathan Maple <[email protected]>
1 parent ab611f6 commit e1c8c30

File tree

1 file changed

+14
-2
lines changed

1 file changed

+14
-2
lines changed

fs/smb/client/inode.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1902,15 +1902,24 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
19021902
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
19031903
struct tcon_link *tlink;
19041904
struct cifs_tcon *tcon;
1905+
__u32 dosattr = 0, origattr = 0;
19051906
struct TCP_Server_Info *server;
19061907
struct iattr *attrs = NULL;
1907-
__u32 dosattr = 0, origattr = 0;
1908+
bool rehash = false;
19081909

19091910
cifs_dbg(FYI, "cifs_unlink, dir=0x%p, dentry=0x%p\n", dir, dentry);
19101911

19111912
if (unlikely(cifs_forced_shutdown(cifs_sb)))
19121913
return -EIO;
19131914

1915+
/* Unhash dentry in advance to prevent any concurrent opens */
1916+
spin_lock(&dentry->d_lock);
1917+
if (!d_unhashed(dentry)) {
1918+
__d_drop(dentry);
1919+
rehash = true;
1920+
}
1921+
spin_unlock(&dentry->d_lock);
1922+
19141923
tlink = cifs_sb_tlink(cifs_sb);
19151924
if (IS_ERR(tlink))
19161925
return PTR_ERR(tlink);
@@ -1961,7 +1970,8 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
19611970
cifs_drop_nlink(inode);
19621971
}
19631972
} else if (rc == -ENOENT) {
1964-
d_drop(dentry);
1973+
if (simple_positive(dentry))
1974+
d_delete(dentry);
19651975
} else if (rc == -EBUSY) {
19661976
if (server->ops->rename_pending_delete) {
19671977
rc = server->ops->rename_pending_delete(full_path,
@@ -2014,6 +2024,8 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
20142024
kfree(attrs);
20152025
free_xid(xid);
20162026
cifs_put_tlink(tlink);
2027+
if (rehash)
2028+
d_rehash(dentry);
20172029
return rc;
20182030
}
20192031

0 commit comments

Comments
 (0)