]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
smb: client: fix race with concurrent opens in rename(2)
authorPaulo Alcantara <pc@manguebit.org>
Fri, 8 Aug 2025 14:43:29 +0000 (11:43 -0300)
committerSteve French <stfrench@microsoft.com>
Sun, 10 Aug 2025 17:39:51 +0000 (12:39 -0500)
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
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/inode.c

index cf9060f0fc08e4cb1a6dcc99d2eb6e69a5159300..fe453a4b3dc83137e84557b4c0d2e40adfaf4075 100644 (file)
@@ -2474,6 +2474,7 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
        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 +2490,17 @@ 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 (d_is_positive(target_dentry) && !d_is_dir(target_dentry)) {
+               if (!d_unhashed(target_dentry)) {
+                       d_drop(target_dentry);
+                       rehash = true;
+               }
+       }
+
        tlink = cifs_sb_tlink(cifs_sb);
        if (IS_ERR(tlink))
                return PTR_ERR(tlink);
@@ -2530,6 +2542,8 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
                }
        }
 
+       if (!rc)
+               rehash = false;
        /*
         * No-replace is the natural behavior for CIFS, so skip unlink hacks.
         */
@@ -2588,12 +2602,16 @@ unlink_target:
                        goto cifs_rename_exit;
                rc = cifs_do_rename(xid, source_dentry, from_name,
                                    target_dentry, to_name);
+               if (!rc)
+                       rehash = false;
        }
 
        /* force revalidate to go get info when needed */
        CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0;
 
 cifs_rename_exit:
+       if (rehash)
+               d_rehash(target_dentry);
        kfree(info_buf_source);
        free_dentry_path(page2);
        free_dentry_path(page1);