Skip to content

Commit dddeed1

Browse files
pcacjrgregkh
authored andcommitted
smb: client: fix race with concurrent opens in unlink(2)
[ Upstream 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 <jaeshin@redhat.com> Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.org> Reviewed-by: David Howells <dhowells@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: linux-cifs@vger.kernel.org Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent b08a26b commit dddeed1

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
@@ -1943,15 +1943,24 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
19431943
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
19441944
struct tcon_link *tlink;
19451945
struct cifs_tcon *tcon;
1946+
__u32 dosattr = 0, origattr = 0;
19461947
struct TCP_Server_Info *server;
19471948
struct iattr *attrs = NULL;
1948-
__u32 dosattr = 0, origattr = 0;
1949+
bool rehash = false;
19491950

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

19521953
if (unlikely(cifs_forced_shutdown(cifs_sb)))
19531954
return -EIO;
19541955

1956+
/* Unhash dentry in advance to prevent any concurrent opens */
1957+
spin_lock(&dentry->d_lock);
1958+
if (!d_unhashed(dentry)) {
1959+
__d_drop(dentry);
1960+
rehash = true;
1961+
}
1962+
spin_unlock(&dentry->d_lock);
1963+
19551964
tlink = cifs_sb_tlink(cifs_sb);
19561965
if (IS_ERR(tlink))
19571966
return PTR_ERR(tlink);
@@ -2003,7 +2012,8 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
20032012
cifs_drop_nlink(inode);
20042013
}
20052014
} else if (rc == -ENOENT) {
2006-
d_drop(dentry);
2015+
if (simple_positive(dentry))
2016+
d_delete(dentry);
20072017
} else if (rc == -EBUSY) {
20082018
if (server->ops->rename_pending_delete) {
20092019
rc = server->ops->rename_pending_delete(full_path,
@@ -2056,6 +2066,8 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
20562066
kfree(attrs);
20572067
free_xid(xid);
20582068
cifs_put_tlink(tlink);
2069+
if (rehash)
2070+
d_rehash(dentry);
20592071
return rc;
20602072
}
20612073

0 commit comments

Comments
 (0)