smb/server: send compound prefix before async pending response

When the last request in a compound request becomes async, ksmbd sends a
STATUS_PENDING response for it. But the responses for previous requests in
the same compound request are still kept in the same response buffer.

Send these previous responses first. Clear NextCommand for the last
response in this part, sign it again if needed, and reset the iov state.
After that, the async request sends STATUS_PENDING first, and sends the
real response later. Both are separate responses.

Example:

  smbtorture //${server_ip}/export -U${username}%${password} smb2.compound_async.write_write

  Client request:

    Write Request Len:64 Off:0, File: compound_async_write_write; Write Request Len:64 Off:64

  Before this patch, STATUS_PENDING Write Response is the first of
  several responses:

    Write Response, Error: STATUS_PENDING
    Write Response, File: compound_async_write_write; Write Response

  But STATUS_PENDING Write Response should be in the middle of several
  responses, after this patch:

    Write Response, File: compound_async_write_write
    Write Response
      SMB2, STATUS_PENDING, Write Response, MessageId 7
      SMB2, Write Response, MessageId 7

Signed-off-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
ChenXiaoSong 2026-07-08 02:56:13 +00:00 committed by Namjae Jeon
parent 7c5d98b515
commit 906a622163

View File

@ -839,6 +839,48 @@ void release_async_work(struct ksmbd_work *work)
}
}
static void smb2_send_interim_compound_prefix(struct ksmbd_work *work)
{
struct smb2_hdr *req_hdr;
struct smb2_hdr *rsp_hdr;
int err;
if (!work->next_smb2_rcv_hdr_off ||
!work->next_smb2_rsp_hdr_off ||
work->curr_smb2_rsp_hdr_off == work->next_smb2_rsp_hdr_off ||
!work->iov_idx)
return;
req_hdr = ksmbd_req_buf_next(work);
/* Detach only the final async command from the completed prefix. */
if (req_hdr->NextCommand)
return;
/*
* The responses before the async command are sent as a standalone
* compound response. The last response in this prefix must terminate
* the chain.
*/
rsp_hdr = ksmbd_resp_buf_curr(work);
rsp_hdr->NextCommand = 0;
if ((rsp_hdr->Flags & SMB2_FLAGS_SIGNED) && work->sess &&
work->conn->ops->set_sign_rsp)
work->conn->ops->set_sign_rsp(work);
err = ksmbd_conn_write(work);
if (err)
ksmbd_debug(SMB, "failed to send compound interim prefix: %d\n",
err);
work->iov_idx = 0;
work->iov_cnt = 0;
work->curr_smb2_rsp_hdr_off = work->next_smb2_rsp_hdr_off;
*(__be32 *)work->response_buf = 0;
rsp_hdr = ksmbd_resp_buf_next(work);
rsp_hdr->Flags &= ~SMB2_FLAGS_RELATED_OPERATIONS;
}
void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status)
{
struct smb2_hdr *rsp_hdr;
@ -853,6 +895,9 @@ void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status)
return;
}
if (status == STATUS_PENDING)
smb2_send_interim_compound_prefix(work);
in_work->conn = work->conn;
memcpy(smb_get_msg(in_work->response_buf), ksmbd_resp_buf_next(work),
__SMB2_HEADER_STRUCTURE_SIZE);