Skip to content

Commit

Permalink
cifs: Handle race conditions during rename
Browse files Browse the repository at this point in the history
commit 4153570 upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
rohiths-msft authored and gregkh committed Aug 18, 2021
1 parent cc3a5a2 commit 3c420ec
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 7 deletions.
19 changes: 17 additions & 2 deletions fs/cifs/inode.c
Expand Up @@ -1637,7 +1637,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,
Expand Down Expand Up @@ -2096,6 +2096,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;
Expand Down Expand Up @@ -2125,10 +2126,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.
*/
Expand Down
16 changes: 11 additions & 5 deletions fs/cifs/misc.c
Expand Up @@ -735,13 +735,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);
}
}
}

Expand Down

0 comments on commit 3c420ec

Please sign in to comment.