Commit 41535701 authored by Rohith Surabattula's avatar Rohith Surabattula Committed by Steve French

cifs: Handle race conditions during rename

When rename is executed on directory which has files for which
close is deferred, then rename will fail with EACCES.

This patch will try to close all deferred files when EACCES is received
and retry rename on a directory.
Signed-off-by: default avatarRohith Surabattula <rohiths@microsoft.com>
Cc: stable@vger.kernel.org # 5.13
Reviewed-by: default avatarShyam Prasad N <sprasad@microsoft.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 981567bd
...@@ -1625,7 +1625,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1625,7 +1625,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
goto unlink_out; goto unlink_out;
} }
cifs_close_all_deferred_files(tcon); cifs_close_deferred_file(CIFS_I(inode));
if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu(tcon->fsUnixInfo.Capability))) { le64_to_cpu(tcon->fsUnixInfo.Capability))) {
rc = CIFSPOSIXDelFile(xid, tcon, full_path, rc = CIFSPOSIXDelFile(xid, tcon, full_path,
...@@ -2084,6 +2084,7 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir, ...@@ -2084,6 +2084,7 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
FILE_UNIX_BASIC_INFO *info_buf_target; FILE_UNIX_BASIC_INFO *info_buf_target;
unsigned int xid; unsigned int xid;
int rc, tmprc; int rc, tmprc;
int retry_count = 0;
if (flags & ~RENAME_NOREPLACE) if (flags & ~RENAME_NOREPLACE)
return -EINVAL; return -EINVAL;
...@@ -2113,10 +2114,24 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir, ...@@ -2113,10 +2114,24 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
goto cifs_rename_exit; goto cifs_rename_exit;
} }
cifs_close_all_deferred_files(tcon); cifs_close_deferred_file(CIFS_I(d_inode(source_dentry)));
if (d_inode(target_dentry) != NULL)
cifs_close_deferred_file(CIFS_I(d_inode(target_dentry)));
rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry, rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
to_name); to_name);
if (rc == -EACCES) {
while (retry_count < 3) {
cifs_close_all_deferred_files(tcon);
rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
to_name);
if (rc != -EACCES)
break;
retry_count++;
}
}
/* /*
* No-replace is the natural behavior for CIFS, so skip unlink hacks. * No-replace is the natural behavior for CIFS, so skip unlink hacks.
*/ */
......
...@@ -723,13 +723,19 @@ void ...@@ -723,13 +723,19 @@ void
cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
{ {
struct cifsFileInfo *cfile = NULL; struct cifsFileInfo *cfile = NULL;
struct cifs_deferred_close *dclose;
if (cifs_inode == NULL)
return;
list_for_each_entry(cfile, &cifs_inode->openFileList, flist) { list_for_each_entry(cfile, &cifs_inode->openFileList, flist) {
spin_lock(&cifs_inode->deferred_lock); if (delayed_work_pending(&cfile->deferred)) {
if (cifs_is_deferred_close(cfile, &dclose)) /*
mod_delayed_work(deferredclose_wq, &cfile->deferred, 0); * If there is no pending work, mod_delayed_work queues new work.
spin_unlock(&cifs_inode->deferred_lock); * So, Increase the ref count to avoid use-after-free.
*/
if (!mod_delayed_work(deferredclose_wq, &cfile->deferred, 0))
cifsFileInfo_get(cfile);
}
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment