Skip to content

Commit c326cb0

Browse files
committed
Merge: CIFS: unlink(2)/rename(2) fixes for races and data corruption
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-10/-/merge_requests/1437 - unlink(2)/rename(2) fixes for races and data corruption JIRA: https://issues.redhat.com/browse/RHEL-108683 Signed-off-by: Paulo Alcantara <paalcant@redhat.com> Approved-by: Scott Mayhew <smayhew@redhat.com> Approved-by: Benjamin Coddington <bcodding@redhat.com> Approved-by: David Howells <dhowells@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: Scott Weaver <scweaver@redhat.com>
2 parents f177ea4 + 476497f commit c326cb0

File tree

8 files changed

+336
-75
lines changed

8 files changed

+336
-75
lines changed

fs/smb/client/cifsglob.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
#define SMB_INTERFACE_POLL_INTERVAL 600
8888

8989
/* maximum number of PDUs in one compound */
90-
#define MAX_COMPOUND 7
90+
#define MAX_COMPOUND 10
9191

9292
/*
9393
* Default number of credits to keep available for SMB3.
@@ -1881,9 +1881,12 @@ static inline bool is_replayable_error(int error)
18811881

18821882

18831883
/* cifs_get_writable_file() flags */
1884-
#define FIND_WR_ANY 0
1885-
#define FIND_WR_FSUID_ONLY 1
1886-
#define FIND_WR_WITH_DELETE 2
1884+
enum cifs_writable_file_flags {
1885+
FIND_WR_ANY = 0U,
1886+
FIND_WR_FSUID_ONLY = (1U << 0),
1887+
FIND_WR_WITH_DELETE = (1U << 1),
1888+
FIND_WR_NO_PENDING_DELETE = (1U << 2),
1889+
};
18871890

18881891
#define MID_FREE 0
18891892
#define MID_REQUEST_ALLOCATED 1
@@ -2339,6 +2342,8 @@ struct smb2_compound_vars {
23392342
struct kvec qi_iov;
23402343
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
23412344
struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE];
2345+
struct kvec unlink_iov[SMB2_SET_INFO_IOV_SIZE];
2346+
struct kvec rename_iov[SMB2_SET_INFO_IOV_SIZE];
23422347
struct kvec close_iov;
23432348
struct smb2_file_rename_info_hdr rename_info;
23442349
struct smb2_file_link_info_hdr link_info;

fs/smb/client/file.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,10 @@ int cifs_open(struct inode *inode, struct file *file)
998998

999999
/* Get the cached handle as SMB2 close is deferred */
10001000
if (OPEN_FMODE(file->f_flags) & FMODE_WRITE) {
1001-
rc = cifs_get_writable_path(tcon, full_path, FIND_WR_FSUID_ONLY, &cfile);
1001+
rc = cifs_get_writable_path(tcon, full_path,
1002+
FIND_WR_FSUID_ONLY |
1003+
FIND_WR_NO_PENDING_DELETE,
1004+
&cfile);
10021005
} else {
10031006
rc = cifs_get_readable_path(tcon, full_path, &cfile);
10041007
}
@@ -2530,6 +2533,9 @@ cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, int flags,
25302533
continue;
25312534
if (with_delete && !(open_file->fid.access & DELETE))
25322535
continue;
2536+
if ((flags & FIND_WR_NO_PENDING_DELETE) &&
2537+
open_file->status_file_deleted)
2538+
continue;
25332539
if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
25342540
if (!open_file->invalidHandle) {
25352541
/* found a good writable file */
@@ -2647,6 +2653,16 @@ cifs_get_readable_path(struct cifs_tcon *tcon, const char *name,
26472653
spin_unlock(&tcon->open_file_lock);
26482654
free_dentry_path(page);
26492655
*ret_file = find_readable_file(cinode, 0);
2656+
if (*ret_file) {
2657+
spin_lock(&cinode->open_file_lock);
2658+
if ((*ret_file)->status_file_deleted) {
2659+
spin_unlock(&cinode->open_file_lock);
2660+
cifsFileInfo_put(*ret_file);
2661+
*ret_file = NULL;
2662+
} else {
2663+
spin_unlock(&cinode->open_file_lock);
2664+
}
2665+
}
26502666
return *ret_file ? 0 : -ENOENT;
26512667
}
26522668

fs/smb/client/inode.c

Lines changed: 112 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,7 +1931,7 @@ cifs_drop_nlink(struct inode *inode)
19311931
* but will return the EACCES to the caller. Note that the VFS does not call
19321932
* unlink on negative dentries currently.
19331933
*/
1934-
int cifs_unlink(struct inode *dir, struct dentry *dentry)
1934+
static int __cifs_unlink(struct inode *dir, struct dentry *dentry, bool sillyrename)
19351935
{
19361936
int rc = 0;
19371937
unsigned int xid;
@@ -1943,15 +1943,24 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
19431943
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
19441944
struct tcon_link *tlink;
19451945
struct cifs_tcon *tcon;
1946+
__u32 dosattr = 0, origattr = 0;
19461947
struct TCP_Server_Info *server;
19471948
struct iattr *attrs = NULL;
1948-
__u32 dosattr = 0, origattr = 0;
1949+
bool rehash = false;
19491950

19501951
cifs_dbg(FYI, "cifs_unlink, dir=0x%p, dentry=0x%p\n", dir, dentry);
19511952

19521953
if (unlikely(cifs_forced_shutdown(cifs_sb)))
19531954
return -EIO;
19541955

1956+
/* Unhash dentry in advance to prevent any concurrent opens */
1957+
spin_lock(&dentry->d_lock);
1958+
if (!d_unhashed(dentry)) {
1959+
__d_drop(dentry);
1960+
rehash = true;
1961+
}
1962+
spin_unlock(&dentry->d_lock);
1963+
19551964
tlink = cifs_sb_tlink(cifs_sb);
19561965
if (IS_ERR(tlink))
19571966
return PTR_ERR(tlink);
@@ -1994,7 +2003,24 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
19942003
goto psx_del_no_retry;
19952004
}
19962005

1997-
rc = server->ops->unlink(xid, tcon, full_path, cifs_sb, dentry);
2006+
/* For SMB2+, if the file is open, we always perform a silly rename.
2007+
*
2008+
* We check for d_count() right after calling
2009+
* cifs_close_deferred_file_under_dentry() to make sure that the
2010+
* dentry's refcount gets dropped in case the file had any deferred
2011+
* close.
2012+
*/
2013+
if (!sillyrename && server->vals->protocol_id > SMB10_PROT_ID) {
2014+
spin_lock(&dentry->d_lock);
2015+
if (d_count(dentry) > 1)
2016+
sillyrename = true;
2017+
spin_unlock(&dentry->d_lock);
2018+
}
2019+
2020+
if (sillyrename)
2021+
rc = -EBUSY;
2022+
else
2023+
rc = server->ops->unlink(xid, tcon, full_path, cifs_sb, dentry);
19982024

19992025
psx_del_no_retry:
20002026
if (!rc) {
@@ -2003,7 +2029,8 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
20032029
cifs_drop_nlink(inode);
20042030
}
20052031
} else if (rc == -ENOENT) {
2006-
d_drop(dentry);
2032+
if (simple_positive(dentry))
2033+
d_delete(dentry);
20072034
} else if (rc == -EBUSY) {
20082035
if (server->ops->rename_pending_delete) {
20092036
rc = server->ops->rename_pending_delete(full_path,
@@ -2056,9 +2083,16 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
20562083
kfree(attrs);
20572084
free_xid(xid);
20582085
cifs_put_tlink(tlink);
2086+
if (rehash)
2087+
d_rehash(dentry);
20592088
return rc;
20602089
}
20612090

2091+
int cifs_unlink(struct inode *dir, struct dentry *dentry)
2092+
{
2093+
return __cifs_unlink(dir, dentry, false);
2094+
}
2095+
20622096
static int
20632097
cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
20642098
const char *full_path, struct cifs_sb_info *cifs_sb,
@@ -2346,14 +2380,16 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
23462380
rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb);
23472381
cifs_put_tlink(tlink);
23482382

2383+
cifsInode = CIFS_I(d_inode(direntry));
2384+
23492385
if (!rc) {
2386+
set_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags);
23502387
spin_lock(&d_inode(direntry)->i_lock);
23512388
i_size_write(d_inode(direntry), 0);
23522389
clear_nlink(d_inode(direntry));
23532390
spin_unlock(&d_inode(direntry)->i_lock);
23542391
}
23552392

2356-
cifsInode = CIFS_I(d_inode(direntry));
23572393
/* force revalidate to go get info when needed */
23582394
cifsInode->time = 0;
23592395

@@ -2446,8 +2482,11 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry,
24462482
}
24472483
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
24482484
do_rename_exit:
2449-
if (rc == 0)
2485+
if (rc == 0) {
24502486
d_move(from_dentry, to_dentry);
2487+
/* Force a new lookup */
2488+
d_drop(from_dentry);
2489+
}
24512490
cifs_put_tlink(tlink);
24522491
return rc;
24532492
}
@@ -2458,10 +2497,12 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
24582497
struct dentry *target_dentry, unsigned int flags)
24592498
{
24602499
const char *from_name, *to_name;
2500+
struct TCP_Server_Info *server;
24612501
void *page1, *page2;
24622502
struct cifs_sb_info *cifs_sb;
24632503
struct tcon_link *tlink;
24642504
struct cifs_tcon *tcon;
2505+
bool rehash = false;
24652506
unsigned int xid;
24662507
int rc, tmprc;
24672508
int retry_count = 0;
@@ -2477,10 +2518,22 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
24772518
if (unlikely(cifs_forced_shutdown(cifs_sb)))
24782519
return -EIO;
24792520

2521+
/*
2522+
* Prevent any concurrent opens on the target by unhashing the dentry.
2523+
* VFS already unhashes the target when renaming directories.
2524+
*/
2525+
if (d_is_positive(target_dentry) && !d_is_dir(target_dentry)) {
2526+
if (!d_unhashed(target_dentry)) {
2527+
d_drop(target_dentry);
2528+
rehash = true;
2529+
}
2530+
}
2531+
24802532
tlink = cifs_sb_tlink(cifs_sb);
24812533
if (IS_ERR(tlink))
24822534
return PTR_ERR(tlink);
24832535
tcon = tlink_tcon(tlink);
2536+
server = tcon->ses->server;
24842537

24852538
page1 = alloc_dentry_path();
24862539
page2 = alloc_dentry_path();
@@ -2518,6 +2571,8 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
25182571
}
25192572
}
25202573

2574+
if (!rc)
2575+
rehash = false;
25212576
/*
25222577
* No-replace is the natural behavior for CIFS, so skip unlink hacks.
25232578
*/
@@ -2565,23 +2620,61 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
25652620

25662621
unlink_target:
25672622
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
2568-
2569-
/* Try unlinking the target dentry if it's not negative */
2570-
if (d_really_is_positive(target_dentry) && (rc == -EACCES || rc == -EEXIST)) {
2571-
if (d_is_dir(target_dentry))
2572-
tmprc = cifs_rmdir(target_dir, target_dentry);
2573-
else
2574-
tmprc = cifs_unlink(target_dir, target_dentry);
2575-
if (tmprc)
2576-
goto cifs_rename_exit;
2577-
rc = cifs_do_rename(xid, source_dentry, from_name,
2578-
target_dentry, to_name);
2623+
if (d_really_is_positive(target_dentry)) {
2624+
if (!rc) {
2625+
struct inode *inode = d_inode(target_dentry);
2626+
/*
2627+
* Samba and ksmbd servers allow renaming a target
2628+
* directory that is open, so make sure to update
2629+
* ->i_nlink and then mark it as delete pending.
2630+
*/
2631+
if (S_ISDIR(inode->i_mode)) {
2632+
drop_cached_dir_by_name(xid, tcon, to_name, cifs_sb);
2633+
spin_lock(&inode->i_lock);
2634+
i_size_write(inode, 0);
2635+
clear_nlink(inode);
2636+
spin_unlock(&inode->i_lock);
2637+
set_bit(CIFS_INO_DELETE_PENDING, &CIFS_I(inode)->flags);
2638+
CIFS_I(inode)->time = 0; /* force reval */
2639+
inode_set_ctime_current(inode);
2640+
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
2641+
}
2642+
} else if (rc == -EACCES || rc == -EEXIST) {
2643+
/*
2644+
* Rename failed, possibly due to a busy target.
2645+
* Retry it by unliking the target first.
2646+
*/
2647+
if (d_is_dir(target_dentry)) {
2648+
tmprc = cifs_rmdir(target_dir, target_dentry);
2649+
} else {
2650+
tmprc = __cifs_unlink(target_dir, target_dentry,
2651+
server->vals->protocol_id > SMB10_PROT_ID);
2652+
}
2653+
if (tmprc) {
2654+
/*
2655+
* Some servers will return STATUS_ACCESS_DENIED
2656+
* or STATUS_DIRECTORY_NOT_EMPTY when failing to
2657+
* rename a non-empty directory. Make sure to
2658+
* propagate the appropriate error back to
2659+
* userspace.
2660+
*/
2661+
if (tmprc == -EEXIST || tmprc == -ENOTEMPTY)
2662+
rc = tmprc;
2663+
goto cifs_rename_exit;
2664+
}
2665+
rc = cifs_do_rename(xid, source_dentry, from_name,
2666+
target_dentry, to_name);
2667+
if (!rc)
2668+
rehash = false;
2669+
}
25792670
}
25802671

25812672
/* force revalidate to go get info when needed */
25822673
CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0;
25832674

25842675
cifs_rename_exit:
2676+
if (rehash)
2677+
d_rehash(target_dentry);
25852678
kfree(info_buf_source);
25862679
free_dentry_path(page2);
25872680
free_dentry_path(page1);
@@ -2599,6 +2692,8 @@ cifs_dentry_needs_reval(struct dentry *dentry)
25992692
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
26002693
struct cached_fid *cfid = NULL;
26012694

2695+
if (test_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags))
2696+
return false;
26022697
if (cifs_i->time == 0)
26032698
return true;
26042699

fs/smb/client/smb2glob.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,9 @@ enum smb2_compound_ops {
3030
SMB2_OP_QUERY_DIR,
3131
SMB2_OP_MKDIR,
3232
SMB2_OP_RENAME,
33-
SMB2_OP_DELETE,
3433
SMB2_OP_HARDLINK,
3534
SMB2_OP_SET_EOF,
36-
SMB2_OP_RMDIR,
35+
SMB2_OP_UNLINK,
3736
SMB2_OP_POSIX_QUERY_INFO,
3837
SMB2_OP_SET_REPARSE,
3938
SMB2_OP_GET_REPARSE,

0 commit comments

Comments
 (0)