Skip to content

Commit ad07955

Browse files
pcacjropsiff
authored andcommitted
smb: client: fix race with concurrent opens in unlink(2)
[ Upstream commit 0af1561b2d60bab2a2b00720a5c7b292ecc549ec ] 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]> Signed-off-by: Sasha Levin <[email protected]> (cherry picked from commit bc1427a48371808378ef0c8cc1d21a27352fbeb1)
1 parent 9a03fec commit ad07955

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
@@ -1856,15 +1856,24 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
18561856
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
18571857
struct tcon_link *tlink;
18581858
struct cifs_tcon *tcon;
1859+
__u32 dosattr = 0, origattr = 0;
18591860
struct TCP_Server_Info *server;
18601861
struct iattr *attrs = NULL;
1861-
__u32 dosattr = 0, origattr = 0;
1862+
bool rehash = false;
18621863

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

18651866
if (unlikely(cifs_forced_shutdown(cifs_sb)))
18661867
return -EIO;
18671868

1869+
/* Unhash dentry in advance to prevent any concurrent opens */
1870+
spin_lock(&dentry->d_lock);
1871+
if (!d_unhashed(dentry)) {
1872+
__d_drop(dentry);
1873+
rehash = true;
1874+
}
1875+
spin_unlock(&dentry->d_lock);
1876+
18681877
tlink = cifs_sb_tlink(cifs_sb);
18691878
if (IS_ERR(tlink))
18701879
return PTR_ERR(tlink);
@@ -1915,7 +1924,8 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
19151924
cifs_drop_nlink(inode);
19161925
}
19171926
} else if (rc == -ENOENT) {
1918-
d_drop(dentry);
1927+
if (simple_positive(dentry))
1928+
d_delete(dentry);
19191929
} else if (rc == -EBUSY) {
19201930
if (server->ops->rename_pending_delete) {
19211931
rc = server->ops->rename_pending_delete(full_path,
@@ -1968,6 +1978,8 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
19681978
kfree(attrs);
19691979
free_xid(xid);
19701980
cifs_put_tlink(tlink);
1981+
if (rehash)
1982+
d_rehash(dentry);
19711983
return rc;
19721984
}
19731985

0 commit comments

Comments
 (0)