Skip to content

Commit 3f9ee8e

Browse files
committed
Merge: CIFS: unlink(2)/rename(2) fixes for races and data corruption [rhel-9.8]
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/7410 - unlink(2)/rename(2) fixes for races and data corruption JIRA: https://issues.redhat.com/browse/RHEL-114295 Signed-off-by: Paulo Alcantara <paalcant@redhat.com> Approved-by: Jay Shin <jaeshin@redhat.com> Approved-by: Benjamin Coddington <bcodding@redhat.com> Approved-by: Scott Mayhew <smayhew@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: Patrick Talbert <ptalbert@redhat.com>
2 parents 4083df0 + cf87a2c commit 3f9ee8e

File tree

10 files changed

+413
-108
lines changed

10 files changed

+413
-108
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.
@@ -1938,9 +1938,12 @@ static inline bool is_replayable_error(int error)
19381938

19391939

19401940
/* cifs_get_writable_file() flags */
1941-
#define FIND_WR_ANY 0
1942-
#define FIND_WR_FSUID_ONLY 1
1943-
#define FIND_WR_WITH_DELETE 2
1941+
enum cifs_writable_file_flags {
1942+
FIND_WR_ANY = 0U,
1943+
FIND_WR_FSUID_ONLY = (1U << 0),
1944+
FIND_WR_WITH_DELETE = (1U << 1),
1945+
FIND_WR_NO_PENDING_DELETE = (1U << 2),
1946+
};
19441947

19451948
#define MID_FREE 0
19461949
#define MID_REQUEST_ALLOCATED 1
@@ -2374,6 +2377,8 @@ struct smb2_compound_vars {
23742377
struct kvec qi_iov;
23752378
struct kvec io_iov[SMB2_IOCTL_IOV_SIZE];
23762379
struct kvec si_iov[SMB2_SET_INFO_IOV_SIZE];
2380+
struct kvec unlink_iov[SMB2_SET_INFO_IOV_SIZE];
2381+
struct kvec rename_iov[SMB2_SET_INFO_IOV_SIZE];
23772382
struct kvec close_iov;
23782383
struct smb2_file_rename_info_hdr rename_info;
23792384
struct smb2_file_link_info_hdr link_info;

fs/smb/client/cifsproto.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,8 +297,8 @@ extern void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode);
297297

298298
extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon);
299299

300-
extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon,
301-
const char *path);
300+
void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon,
301+
struct dentry *dentry);
302302

303303
extern void cifs_mark_open_handles_for_deleted_file(struct inode *inode,
304304
const char *path);

fs/smb/client/file.c

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

682682
/* Get the cached handle as SMB2 close is deferred */
683683
if (OPEN_FMODE(file->f_flags) & FMODE_WRITE) {
684-
rc = cifs_get_writable_path(tcon, full_path, FIND_WR_FSUID_ONLY, &cfile);
684+
rc = cifs_get_writable_path(tcon, full_path,
685+
FIND_WR_FSUID_ONLY |
686+
FIND_WR_NO_PENDING_DELETE,
687+
&cfile);
685688
} else {
686689
rc = cifs_get_readable_path(tcon, full_path, &cfile);
687690
}
@@ -2286,6 +2289,9 @@ cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, int flags,
22862289
continue;
22872290
if (with_delete && !(open_file->fid.access & DELETE))
22882291
continue;
2292+
if ((flags & FIND_WR_NO_PENDING_DELETE) &&
2293+
open_file->status_file_deleted)
2294+
continue;
22892295
if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
22902296
if (!open_file->invalidHandle) {
22912297
/* found a good writable file */
@@ -2403,6 +2409,16 @@ cifs_get_readable_path(struct cifs_tcon *tcon, const char *name,
24032409
spin_unlock(&tcon->open_file_lock);
24042410
free_dentry_path(page);
24052411
*ret_file = find_readable_file(cinode, 0);
2412+
if (*ret_file) {
2413+
spin_lock(&cinode->open_file_lock);
2414+
if ((*ret_file)->status_file_deleted) {
2415+
spin_unlock(&cinode->open_file_lock);
2416+
cifsFileInfo_put(*ret_file);
2417+
*ret_file = NULL;
2418+
} else {
2419+
spin_unlock(&cinode->open_file_lock);
2420+
}
2421+
}
24062422
return *ret_file ? 0 : -ENOENT;
24072423
}
24082424

fs/smb/client/inode.c

Lines changed: 85 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1912,7 +1912,7 @@ cifs_drop_nlink(struct inode *inode)
19121912
* but will return the EACCES to the caller. Note that the VFS does not call
19131913
* unlink on negative dentries currently.
19141914
*/
1915-
int cifs_unlink(struct inode *dir, struct dentry *dentry)
1915+
static int __cifs_unlink(struct inode *dir, struct dentry *dentry, bool sillyrename)
19161916
{
19171917
int rc = 0;
19181918
unsigned int xid;
@@ -1964,7 +1964,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
19641964
goto unlink_out;
19651965
}
19661966

1967-
cifs_close_deferred_file_under_dentry(tcon, full_path);
1967+
cifs_close_deferred_file_under_dentry(tcon, dentry);
19681968
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
19691969
if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
19701970
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
@@ -1983,7 +1983,24 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
19831983
goto psx_del_no_retry;
19841984
}
19851985

1986-
rc = server->ops->unlink(xid, tcon, full_path, cifs_sb, dentry);
1986+
/* For SMB2+, if the file is open, we always perform a silly rename.
1987+
*
1988+
* We check for d_count() right after calling
1989+
* cifs_close_deferred_file_under_dentry() to make sure that the
1990+
* dentry's refcount gets dropped in case the file had any deferred
1991+
* close.
1992+
*/
1993+
if (!sillyrename && server->vals->protocol_id > SMB10_PROT_ID) {
1994+
spin_lock(&dentry->d_lock);
1995+
if (d_count(dentry) > 1)
1996+
sillyrename = true;
1997+
spin_unlock(&dentry->d_lock);
1998+
}
1999+
2000+
if (sillyrename)
2001+
rc = -EBUSY;
2002+
else
2003+
rc = server->ops->unlink(xid, tcon, full_path, cifs_sb, dentry);
19872004

19882005
psx_del_no_retry:
19892006
if (!rc) {
@@ -2051,6 +2068,11 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
20512068
return rc;
20522069
}
20532070

2071+
int cifs_unlink(struct inode *dir, struct dentry *dentry)
2072+
{
2073+
return __cifs_unlink(dir, dentry, false);
2074+
}
2075+
20542076
static int
20552077
cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
20562078
const char *full_path, struct cifs_sb_info *cifs_sb,
@@ -2338,14 +2360,16 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
23382360
rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb);
23392361
cifs_put_tlink(tlink);
23402362

2363+
cifsInode = CIFS_I(d_inode(direntry));
2364+
23412365
if (!rc) {
2366+
set_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags);
23422367
spin_lock(&d_inode(direntry)->i_lock);
23432368
i_size_write(d_inode(direntry), 0);
23442369
clear_nlink(d_inode(direntry));
23452370
spin_unlock(&d_inode(direntry)->i_lock);
23462371
}
23472372

2348-
cifsInode = CIFS_I(d_inode(direntry));
23492373
/* force revalidate to go get info when needed */
23502374
cifsInode->time = 0;
23512375

@@ -2438,8 +2462,11 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry,
24382462
}
24392463
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
24402464
do_rename_exit:
2441-
if (rc == 0)
2465+
if (rc == 0) {
24422466
d_move(from_dentry, to_dentry);
2467+
/* Force a new lookup */
2468+
d_drop(from_dentry);
2469+
}
24432470
cifs_put_tlink(tlink);
24442471
return rc;
24452472
}
@@ -2450,6 +2477,7 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
24502477
struct dentry *target_dentry, unsigned int flags)
24512478
{
24522479
const char *from_name, *to_name;
2480+
struct TCP_Server_Info *server;
24532481
void *page1, *page2;
24542482
struct cifs_sb_info *cifs_sb;
24552483
struct tcon_link *tlink;
@@ -2485,6 +2513,7 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
24852513
if (IS_ERR(tlink))
24862514
return PTR_ERR(tlink);
24872515
tcon = tlink_tcon(tlink);
2516+
server = tcon->ses->server;
24882517

24892518
page1 = alloc_dentry_path();
24902519
page2 = alloc_dentry_path();
@@ -2502,9 +2531,9 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
25022531
goto cifs_rename_exit;
25032532
}
25042533

2505-
cifs_close_deferred_file_under_dentry(tcon, from_name);
2534+
cifs_close_deferred_file_under_dentry(tcon, source_dentry);
25062535
if (d_inode(target_dentry) != NULL)
2507-
cifs_close_deferred_file_under_dentry(tcon, to_name);
2536+
cifs_close_deferred_file_under_dentry(tcon, target_dentry);
25082537

25092538
rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
25102539
to_name);
@@ -2569,19 +2598,53 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
25692598

25702599
unlink_target:
25712600
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
2572-
2573-
/* Try unlinking the target dentry if it's not negative */
2574-
if (d_really_is_positive(target_dentry) && (rc == -EACCES || rc == -EEXIST)) {
2575-
if (d_is_dir(target_dentry))
2576-
tmprc = cifs_rmdir(target_dir, target_dentry);
2577-
else
2578-
tmprc = cifs_unlink(target_dir, target_dentry);
2579-
if (tmprc)
2580-
goto cifs_rename_exit;
2581-
rc = cifs_do_rename(xid, source_dentry, from_name,
2582-
target_dentry, to_name);
2583-
if (!rc)
2584-
rehash = false;
2601+
if (d_really_is_positive(target_dentry)) {
2602+
if (!rc) {
2603+
struct inode *inode = d_inode(target_dentry);
2604+
/*
2605+
* Samba and ksmbd servers allow renaming a target
2606+
* directory that is open, so make sure to update
2607+
* ->i_nlink and then mark it as delete pending.
2608+
*/
2609+
if (S_ISDIR(inode->i_mode)) {
2610+
drop_cached_dir_by_name(xid, tcon, to_name, cifs_sb);
2611+
spin_lock(&inode->i_lock);
2612+
i_size_write(inode, 0);
2613+
clear_nlink(inode);
2614+
spin_unlock(&inode->i_lock);
2615+
set_bit(CIFS_INO_DELETE_PENDING, &CIFS_I(inode)->flags);
2616+
CIFS_I(inode)->time = 0; /* force reval */
2617+
inode_set_ctime_current(inode);
2618+
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
2619+
}
2620+
} else if (rc == -EACCES || rc == -EEXIST) {
2621+
/*
2622+
* Rename failed, possibly due to a busy target.
2623+
* Retry it by unliking the target first.
2624+
*/
2625+
if (d_is_dir(target_dentry)) {
2626+
tmprc = cifs_rmdir(target_dir, target_dentry);
2627+
} else {
2628+
tmprc = __cifs_unlink(target_dir, target_dentry,
2629+
server->vals->protocol_id > SMB10_PROT_ID);
2630+
}
2631+
if (tmprc) {
2632+
/*
2633+
* Some servers will return STATUS_ACCESS_DENIED
2634+
* or STATUS_DIRECTORY_NOT_EMPTY when failing to
2635+
* rename a non-empty directory. Make sure to
2636+
* propagate the appropriate error back to
2637+
* userspace.
2638+
*/
2639+
if (tmprc == -EEXIST || tmprc == -ENOTEMPTY)
2640+
rc = tmprc;
2641+
goto cifs_rename_exit;
2642+
}
2643+
rc = cifs_do_rename(xid, source_dentry, from_name,
2644+
target_dentry, to_name);
2645+
if (!rc)
2646+
rehash = false;
2647+
}
25852648
}
25862649

25872650
/* force revalidate to go get info when needed */
@@ -2610,6 +2673,8 @@ cifs_dentry_needs_reval(struct dentry *dentry)
26102673
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
26112674
struct cached_fid *cfid = NULL;
26122675

2676+
if (test_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags))
2677+
return false;
26132678
if (cifs_i->time == 0)
26142679
return true;
26152680

fs/smb/client/misc.c

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -832,33 +832,28 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)
832832
kfree(tmp_list);
833833
}
834834
}
835-
void
836-
cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path)
835+
836+
void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon,
837+
struct dentry *dentry)
837838
{
838-
struct cifsFileInfo *cfile;
839839
struct file_list *tmp_list, *tmp_next_list;
840-
void *page;
841-
const char *full_path;
840+
struct cifsFileInfo *cfile;
842841
LIST_HEAD(file_head);
843842

844-
page = alloc_dentry_path();
845843
spin_lock(&tcon->open_file_lock);
846844
list_for_each_entry(cfile, &tcon->openFileList, tlist) {
847-
full_path = build_path_from_dentry(cfile->dentry, page);
848-
if (strstr(full_path, path)) {
849-
if (delayed_work_pending(&cfile->deferred)) {
850-
if (cancel_delayed_work(&cfile->deferred)) {
851-
spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
852-
cifs_del_deferred_close(cfile);
853-
spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
854-
855-
tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC);
856-
if (tmp_list == NULL)
857-
break;
858-
tmp_list->cfile = cfile;
859-
list_add_tail(&tmp_list->list, &file_head);
860-
}
861-
}
845+
if ((cfile->dentry == dentry) &&
846+
delayed_work_pending(&cfile->deferred) &&
847+
cancel_delayed_work(&cfile->deferred)) {
848+
spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
849+
cifs_del_deferred_close(cfile);
850+
spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
851+
852+
tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC);
853+
if (tmp_list == NULL)
854+
break;
855+
tmp_list->cfile = cfile;
856+
list_add_tail(&tmp_list->list, &file_head);
862857
}
863858
}
864859
spin_unlock(&tcon->open_file_lock);
@@ -868,7 +863,6 @@ cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path)
868863
list_del(&tmp_list->list);
869864
kfree(tmp_list);
870865
}
871-
free_dentry_path(page);
872866
}
873867

874868
/*

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)