* [PATCH 1/2] smb: client: fix race with concurrent opens in unlink(2)
@ 2025-08-08 15:20 Paulo Alcantara
2025-08-08 15:20 ` [PATCH 2/2] smb: client: fix race with concurrent opens in rename(2) Paulo Alcantara
0 siblings, 1 reply; 2+ messages in thread
From: Paulo Alcantara @ 2025-08-08 15:20 UTC (permalink / raw)
To: smfrench
Cc: Jay Shin, Paulo Alcantara (Red Hat), David Howells, Al Viro,
linux-cifs
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
---
fs/smb/client/inode.c | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index 75be4b46bc6f..cf9060f0fc08 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -1943,15 +1943,24 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct tcon_link *tlink;
struct cifs_tcon *tcon;
- struct TCP_Server_Info *server;
- struct iattr *attrs = NULL;
__u32 dosattr = 0, origattr = 0;
+ struct TCP_Server_Info *server;
+ struct iattr *attrs = NULL;
+ bool rehash = false;
cifs_dbg(FYI, "cifs_unlink, dir=0x%p, dentry=0x%p\n", dir, dentry);
if (unlikely(cifs_forced_shutdown(cifs_sb)))
return -EIO;
+ /* Unhash dentry in advance to prevent any concurrent opens */
+ spin_lock(&dentry->d_lock);
+ if (!d_unhashed(dentry)) {
+ __d_drop(dentry);
+ rehash = true;
+ }
+ spin_unlock(&dentry->d_lock);
+
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
@@ -2003,7 +2012,8 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
cifs_drop_nlink(inode);
}
} else if (rc == -ENOENT) {
- d_drop(dentry);
+ if (simple_positive(dentry))
+ d_delete(dentry);
} else if (rc == -EBUSY) {
if (server->ops->rename_pending_delete) {
rc = server->ops->rename_pending_delete(full_path,
@@ -2056,6 +2066,8 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
kfree(attrs);
free_xid(xid);
cifs_put_tlink(tlink);
+ if (rehash)
+ d_rehash(dentry);
return rc;
}
--
2.50.1
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [PATCH 2/2] smb: client: fix race with concurrent opens in rename(2)
2025-08-08 15:20 [PATCH 1/2] smb: client: fix race with concurrent opens in unlink(2) Paulo Alcantara
@ 2025-08-08 15:20 ` Paulo Alcantara
0 siblings, 0 replies; 2+ messages in thread
From: Paulo Alcantara @ 2025-08-08 15:20 UTC (permalink / raw)
To: smfrench; +Cc: Paulo Alcantara (Red Hat), David Howells, Al Viro, linux-cifs
Besides sending the rename request to the server, the rename process
also involves closing any deferred close, waiting for outstanding I/O
to complete 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 on the target file.
Fix this by unhashing the dentry in advance to prevent any concurrent
opens on the target.
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
---
fs/smb/client/inode.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index cf9060f0fc08..0364b8882289 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -2469,11 +2469,13 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
struct dentry *source_dentry, struct inode *target_dir,
struct dentry *target_dentry, unsigned int flags)
{
+ struct inode *target_inode = d_inode(target_dentry);
const char *from_name, *to_name;
void *page1, *page2;
struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifs_tcon *tcon;
+ bool rehash = false;
unsigned int xid;
int rc, tmprc;
int retry_count = 0;
@@ -2489,6 +2491,19 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
if (unlikely(cifs_forced_shutdown(cifs_sb)))
return -EIO;
+ /*
+ * Prevent any concurrent opens on the target by unhashing the dentry.
+ * VFS already unhashes the target when renaming directories.
+ */
+ if (target_inode && !S_ISDIR(target_inode->i_mode)) {
+ spin_lock(&target_dentry->d_lock);
+ if (!d_unhashed(target_dentry)) {
+ __d_drop(target_dentry);
+ rehash = true;
+ }
+ spin_unlock(&target_dentry->d_lock);
+ }
+
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
@@ -2599,6 +2614,8 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
free_dentry_path(page1);
free_xid(xid);
cifs_put_tlink(tlink);
+ if (rehash)
+ d_rehash(target_dentry);
return rc;
}
--
2.50.1
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2025-08-08 15:20 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-08 15:20 [PATCH 1/2] smb: client: fix race with concurrent opens in unlink(2) Paulo Alcantara
2025-08-08 15:20 ` [PATCH 2/2] smb: client: fix race with concurrent opens in rename(2) Paulo Alcantara
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).