Skip to content

Commit 4153570

Browse files
rohiths-msftsmfrench
authored andcommitted
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 <[email protected]> Cc: [email protected] # 5.13 Reviewed-by: Shyam Prasad N <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 981567b commit 4153570

File tree

2 files changed

+28
-7
lines changed

2 files changed

+28
-7
lines changed

fs/cifs/inode.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,7 +1625,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
16251625
goto unlink_out;
16261626
}
16271627

1628-
cifs_close_all_deferred_files(tcon);
1628+
cifs_close_deferred_file(CIFS_I(inode));
16291629
if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
16301630
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
16311631
rc = CIFSPOSIXDelFile(xid, tcon, full_path,
@@ -2084,6 +2084,7 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
20842084
FILE_UNIX_BASIC_INFO *info_buf_target;
20852085
unsigned int xid;
20862086
int rc, tmprc;
2087+
int retry_count = 0;
20872088

20882089
if (flags & ~RENAME_NOREPLACE)
20892090
return -EINVAL;
@@ -2113,10 +2114,24 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
21132114
goto cifs_rename_exit;
21142115
}
21152116

2116-
cifs_close_all_deferred_files(tcon);
2117+
cifs_close_deferred_file(CIFS_I(d_inode(source_dentry)));
2118+
if (d_inode(target_dentry) != NULL)
2119+
cifs_close_deferred_file(CIFS_I(d_inode(target_dentry)));
2120+
21172121
rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
21182122
to_name);
21192123

2124+
if (rc == -EACCES) {
2125+
while (retry_count < 3) {
2126+
cifs_close_all_deferred_files(tcon);
2127+
rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
2128+
to_name);
2129+
if (rc != -EACCES)
2130+
break;
2131+
retry_count++;
2132+
}
2133+
}
2134+
21202135
/*
21212136
* No-replace is the natural behavior for CIFS, so skip unlink hacks.
21222137
*/

fs/cifs/misc.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -723,13 +723,19 @@ void
723723
cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
724724
{
725725
struct cifsFileInfo *cfile = NULL;
726-
struct cifs_deferred_close *dclose;
726+
727+
if (cifs_inode == NULL)
728+
return;
727729

728730
list_for_each_entry(cfile, &cifs_inode->openFileList, flist) {
729-
spin_lock(&cifs_inode->deferred_lock);
730-
if (cifs_is_deferred_close(cfile, &dclose))
731-
mod_delayed_work(deferredclose_wq, &cfile->deferred, 0);
732-
spin_unlock(&cifs_inode->deferred_lock);
731+
if (delayed_work_pending(&cfile->deferred)) {
732+
/*
733+
* If there is no pending work, mod_delayed_work queues new work.
734+
* So, Increase the ref count to avoid use-after-free.
735+
*/
736+
if (!mod_delayed_work(deferredclose_wq, &cfile->deferred, 0))
737+
cifsFileInfo_get(cfile);
738+
}
733739
}
734740
}
735741

0 commit comments

Comments
 (0)