Skip to content

Commit d84291f

Browse files
pcacjrsmfrench
authored andcommitted
smb: client: fix race with concurrent opens in rename(2)
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) <[email protected]> Reviewed-by: David Howells <[email protected]> Cc: Al Viro <[email protected]> Cc: [email protected] Signed-off-by: Steve French <[email protected]>
1 parent 0af1561 commit d84291f

File tree

1 file changed

+18
-0
lines changed

1 file changed

+18
-0
lines changed

fs/smb/client/inode.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2474,6 +2474,7 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
24742474
struct cifs_sb_info *cifs_sb;
24752475
struct tcon_link *tlink;
24762476
struct cifs_tcon *tcon;
2477+
bool rehash = false;
24772478
unsigned int xid;
24782479
int rc, tmprc;
24792480
int retry_count = 0;
@@ -2489,6 +2490,17 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
24892490
if (unlikely(cifs_forced_shutdown(cifs_sb)))
24902491
return -EIO;
24912492

2493+
/*
2494+
* Prevent any concurrent opens on the target by unhashing the dentry.
2495+
* VFS already unhashes the target when renaming directories.
2496+
*/
2497+
if (d_is_positive(target_dentry) && !d_is_dir(target_dentry)) {
2498+
if (!d_unhashed(target_dentry)) {
2499+
d_drop(target_dentry);
2500+
rehash = true;
2501+
}
2502+
}
2503+
24922504
tlink = cifs_sb_tlink(cifs_sb);
24932505
if (IS_ERR(tlink))
24942506
return PTR_ERR(tlink);
@@ -2530,6 +2542,8 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
25302542
}
25312543
}
25322544

2545+
if (!rc)
2546+
rehash = false;
25332547
/*
25342548
* No-replace is the natural behavior for CIFS, so skip unlink hacks.
25352549
*/
@@ -2588,12 +2602,16 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
25882602
goto cifs_rename_exit;
25892603
rc = cifs_do_rename(xid, source_dentry, from_name,
25902604
target_dentry, to_name);
2605+
if (!rc)
2606+
rehash = false;
25912607
}
25922608

25932609
/* force revalidate to go get info when needed */
25942610
CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0;
25952611

25962612
cifs_rename_exit:
2613+
if (rehash)
2614+
d_rehash(target_dentry);
25972615
kfree(info_buf_source);
25982616
free_dentry_path(page2);
25992617
free_dentry_path(page1);

0 commit comments

Comments
 (0)