Merge tag '6.17-rc6-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6
Pull smb client fixes from Steve French: - Two unlink fixes: one for rename and one for deferred close - Four smbdirect/RDMA fixes: fix buffer leak in negotiate, two fixes for races in smbd_destroy, fix offset and length checks in recv_done * tag '6.17-rc6-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6: smb: client: fix smbdirect_recv_io leak in smbd_negotiate() error path smb: client: fix file open check in __cifs_unlink() smb: client: let smbd_destroy() call disable_work_sync(&info->post_send_credits_work) smb: client: use disable[_delayed]_work_sync in smbdirect.c smb: client: fix filename matching of deferred files smb: client: let recv_done verify data_offset, data_length and remaining_data_length
This commit is contained in:
@@ -312,8 +312,8 @@ extern void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode);
|
||||
|
||||
extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon);
|
||||
|
||||
extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon,
|
||||
const char *path);
|
||||
void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon,
|
||||
struct dentry *dentry);
|
||||
|
||||
extern void cifs_mark_open_handles_for_deleted_file(struct inode *inode,
|
||||
const char *path);
|
||||
|
||||
@@ -1984,7 +1984,7 @@ static int __cifs_unlink(struct inode *dir, struct dentry *dentry, bool sillyren
|
||||
}
|
||||
|
||||
netfs_wait_for_outstanding_io(inode);
|
||||
cifs_close_deferred_file_under_dentry(tcon, full_path);
|
||||
cifs_close_deferred_file_under_dentry(tcon, dentry);
|
||||
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
|
||||
if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
|
||||
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
|
||||
@@ -2003,8 +2003,21 @@ retry_std_delete:
|
||||
goto psx_del_no_retry;
|
||||
}
|
||||
|
||||
if (sillyrename || (server->vals->protocol_id > SMB10_PROT_ID &&
|
||||
d_is_positive(dentry) && d_count(dentry) > 2))
|
||||
/* For SMB2+, if the file is open, we always perform a silly rename.
|
||||
*
|
||||
* We check for d_count() right after calling
|
||||
* cifs_close_deferred_file_under_dentry() to make sure that the
|
||||
* dentry's refcount gets dropped in case the file had any deferred
|
||||
* close.
|
||||
*/
|
||||
if (!sillyrename && server->vals->protocol_id > SMB10_PROT_ID) {
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (d_count(dentry) > 1)
|
||||
sillyrename = true;
|
||||
spin_unlock(&dentry->d_lock);
|
||||
}
|
||||
|
||||
if (sillyrename)
|
||||
rc = -EBUSY;
|
||||
else
|
||||
rc = server->ops->unlink(xid, tcon, full_path, cifs_sb, dentry);
|
||||
@@ -2538,10 +2551,10 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
|
||||
goto cifs_rename_exit;
|
||||
}
|
||||
|
||||
cifs_close_deferred_file_under_dentry(tcon, from_name);
|
||||
cifs_close_deferred_file_under_dentry(tcon, source_dentry);
|
||||
if (d_inode(target_dentry) != NULL) {
|
||||
netfs_wait_for_outstanding_io(d_inode(target_dentry));
|
||||
cifs_close_deferred_file_under_dentry(tcon, to_name);
|
||||
cifs_close_deferred_file_under_dentry(tcon, target_dentry);
|
||||
}
|
||||
|
||||
rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
|
||||
|
||||
@@ -832,33 +832,28 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)
|
||||
kfree(tmp_list);
|
||||
}
|
||||
}
|
||||
void
|
||||
cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path)
|
||||
|
||||
void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon,
|
||||
struct dentry *dentry)
|
||||
{
|
||||
struct cifsFileInfo *cfile;
|
||||
struct file_list *tmp_list, *tmp_next_list;
|
||||
void *page;
|
||||
const char *full_path;
|
||||
struct cifsFileInfo *cfile;
|
||||
LIST_HEAD(file_head);
|
||||
|
||||
page = alloc_dentry_path();
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
list_for_each_entry(cfile, &tcon->openFileList, tlist) {
|
||||
full_path = build_path_from_dentry(cfile->dentry, page);
|
||||
if (strstr(full_path, path)) {
|
||||
if (delayed_work_pending(&cfile->deferred)) {
|
||||
if (cancel_delayed_work(&cfile->deferred)) {
|
||||
spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
|
||||
cifs_del_deferred_close(cfile);
|
||||
spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
|
||||
if ((cfile->dentry == dentry) &&
|
||||
delayed_work_pending(&cfile->deferred) &&
|
||||
cancel_delayed_work(&cfile->deferred)) {
|
||||
spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
|
||||
cifs_del_deferred_close(cfile);
|
||||
spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
|
||||
|
||||
tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC);
|
||||
if (tmp_list == NULL)
|
||||
break;
|
||||
tmp_list->cfile = cfile;
|
||||
list_add_tail(&tmp_list->list, &file_head);
|
||||
}
|
||||
}
|
||||
tmp_list = kmalloc(sizeof(struct file_list), GFP_ATOMIC);
|
||||
if (tmp_list == NULL)
|
||||
break;
|
||||
tmp_list->cfile = cfile;
|
||||
list_add_tail(&tmp_list->list, &file_head);
|
||||
}
|
||||
}
|
||||
spin_unlock(&tcon->open_file_lock);
|
||||
@@ -868,7 +863,6 @@ cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path)
|
||||
list_del(&tmp_list->list);
|
||||
kfree(tmp_list);
|
||||
}
|
||||
free_dentry_path(page);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -453,9 +453,12 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
|
||||
struct smbdirect_recv_io *response =
|
||||
container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe);
|
||||
struct smbdirect_socket *sc = response->socket;
|
||||
struct smbdirect_socket_parameters *sp = &sc->parameters;
|
||||
struct smbd_connection *info =
|
||||
container_of(sc, struct smbd_connection, socket);
|
||||
int data_length = 0;
|
||||
u32 data_offset = 0;
|
||||
u32 data_length = 0;
|
||||
u32 remaining_data_length = 0;
|
||||
|
||||
log_rdma_recv(INFO, "response=0x%p type=%d wc status=%d wc opcode %d byte_len=%d pkey_index=%u\n",
|
||||
response, sc->recv_io.expected, wc->status, wc->opcode,
|
||||
@@ -487,7 +490,22 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
|
||||
/* SMBD data transfer packet */
|
||||
case SMBDIRECT_EXPECT_DATA_TRANSFER:
|
||||
data_transfer = smbdirect_recv_io_payload(response);
|
||||
|
||||
if (wc->byte_len <
|
||||
offsetof(struct smbdirect_data_transfer, padding))
|
||||
goto error;
|
||||
|
||||
remaining_data_length = le32_to_cpu(data_transfer->remaining_data_length);
|
||||
data_offset = le32_to_cpu(data_transfer->data_offset);
|
||||
data_length = le32_to_cpu(data_transfer->data_length);
|
||||
if (wc->byte_len < data_offset ||
|
||||
(u64)wc->byte_len < (u64)data_offset + data_length)
|
||||
goto error;
|
||||
|
||||
if (remaining_data_length > sp->max_fragmented_recv_size ||
|
||||
data_length > sp->max_fragmented_recv_size ||
|
||||
(u64)remaining_data_length + (u64)data_length > (u64)sp->max_fragmented_recv_size)
|
||||
goto error;
|
||||
|
||||
if (data_length) {
|
||||
if (sc->recv_io.reassembly.full_packet_received)
|
||||
@@ -1090,8 +1108,10 @@ static int smbd_negotiate(struct smbd_connection *info)
|
||||
log_rdma_event(INFO, "smbd_post_recv rc=%d iov.addr=0x%llx iov.length=%u iov.lkey=0x%x\n",
|
||||
rc, response->sge.addr,
|
||||
response->sge.length, response->sge.lkey);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
put_receive_buffer(info, response);
|
||||
return rc;
|
||||
}
|
||||
|
||||
init_completion(&info->negotiate_completion);
|
||||
info->negotiate_done = false;
|
||||
@@ -1329,13 +1349,16 @@ void smbd_destroy(struct TCP_Server_Info *server)
|
||||
sc->status == SMBDIRECT_SOCKET_DISCONNECTED);
|
||||
}
|
||||
|
||||
log_rdma_event(INFO, "cancelling post_send_credits_work\n");
|
||||
disable_work_sync(&info->post_send_credits_work);
|
||||
|
||||
log_rdma_event(INFO, "destroying qp\n");
|
||||
ib_drain_qp(sc->ib.qp);
|
||||
rdma_destroy_qp(sc->rdma.cm_id);
|
||||
sc->ib.qp = NULL;
|
||||
|
||||
log_rdma_event(INFO, "cancelling idle timer\n");
|
||||
cancel_delayed_work_sync(&info->idle_timer_work);
|
||||
disable_delayed_work_sync(&info->idle_timer_work);
|
||||
|
||||
/* It's not possible for upper layer to get to reassembly */
|
||||
log_rdma_event(INFO, "drain the reassembly queue\n");
|
||||
@@ -1708,7 +1731,7 @@ allocate_mr_failed:
|
||||
return NULL;
|
||||
|
||||
negotiation_failed:
|
||||
cancel_delayed_work_sync(&info->idle_timer_work);
|
||||
disable_delayed_work_sync(&info->idle_timer_work);
|
||||
destroy_caches_and_workqueue(info);
|
||||
sc->status = SMBDIRECT_SOCKET_NEGOTIATE_FAILED;
|
||||
rdma_disconnect(sc->rdma.cm_id);
|
||||
@@ -2067,7 +2090,7 @@ static void destroy_mr_list(struct smbd_connection *info)
|
||||
struct smbdirect_socket *sc = &info->socket;
|
||||
struct smbd_mr *mr, *tmp;
|
||||
|
||||
cancel_work_sync(&info->mr_recovery_work);
|
||||
disable_work_sync(&info->mr_recovery_work);
|
||||
list_for_each_entry_safe(mr, tmp, &info->mr_list, list) {
|
||||
if (mr->state == MR_INVALIDATED)
|
||||
ib_dma_unmap_sg(sc->ib.dev, mr->sgt.sgl,
|
||||
|
||||
Reference in New Issue
Block a user