6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Paulo Alcantara pc@manguebit.org
[ Upstream commit d84291fc7453df7881a970716f8256273aca5747 ]
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 Signed-off-by: Sasha Levin sashal@kernel.org --- fs/smb/client/inode.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index ffc05ebc92f43..f3ed5134ecfa9 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -2165,6 +2165,7 @@ cifs_rename2(struct user_namespace *mnt_userns, 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; @@ -2180,6 +2181,17 @@ cifs_rename2(struct user_namespace *mnt_userns, 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); @@ -2219,6 +2231,8 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir, } }
+ if (!rc) + rehash = false; /* * No-replace is the natural behavior for CIFS, so skip unlink hacks. */ @@ -2277,6 +2291,8 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir, 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 */ @@ -2286,6 +2302,8 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir, target_dir->i_mtime = current_time(source_dir);
cifs_rename_exit: + if (rehash) + d_rehash(target_dentry); kfree(info_buf_source); free_dentry_path(page2); free_dentry_path(page1);