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: Rohith Surabattula <rohiths@microsoft.com> Cc: stable@vger.kernel.org # 5.13 Reviewed-by: Shyam Prasad N <sprasad@microsoft.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
981567bd96
commit
41535701da
@ -1625,7 +1625,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
|
||||
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 &
|
||||
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
|
||||
rc = CIFSPOSIXDelFile(xid, tcon, full_path,
|
||||
@ -2084,6 +2084,7 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
|
||||
FILE_UNIX_BASIC_INFO *info_buf_target;
|
||||
unsigned int xid;
|
||||
int rc, tmprc;
|
||||
int retry_count = 0;
|
||||
|
||||
if (flags & ~RENAME_NOREPLACE)
|
||||
return -EINVAL;
|
||||
@ -2113,10 +2114,24 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
|
||||
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,
|
||||
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.
|
||||
*/
|
||||
|
@ -723,13 +723,19 @@ void
|
||||
cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
|
||||
{
|
||||
struct cifsFileInfo *cfile = NULL;
|
||||
struct cifs_deferred_close *dclose;
|
||||
|
||||
if (cifs_inode == NULL)
|
||||
return;
|
||||
|
||||
list_for_each_entry(cfile, &cifs_inode->openFileList, flist) {
|
||||
spin_lock(&cifs_inode->deferred_lock);
|
||||
if (cifs_is_deferred_close(cfile, &dclose))
|
||||
mod_delayed_work(deferredclose_wq, &cfile->deferred, 0);
|
||||
spin_unlock(&cifs_inode->deferred_lock);
|
||||
if (delayed_work_pending(&cfile->deferred)) {
|
||||
/*
|
||||
* If there is no pending work, mod_delayed_work queues new work.
|
||||
* So, Increase the ref count to avoid use-after-free.
|
||||
*/
|
||||
if (!mod_delayed_work(deferredclose_wq, &cfile->deferred, 0))
|
||||
cifsFileInfo_get(cfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user