Author: Ira Cooper Date: Tue Feb 23 10:16:32 2010 -0500 Updates needed for basic SMB2 support. It also needs basic code clean up. But it is good enough to check in. diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index 3cc967f..15ac1e8 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -334,6 +334,10 @@ struct smbd_smb2_request { struct smbd_smb2_tcon *tcon; int current_idx; + int async_idx; + int cancelled; + struct iovec *async_out; + bool do_signing; struct files_struct *compat_chain_fsp; diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 9e5be40..a1175a7 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -23,6 +23,8 @@ #include "../libcli/smb/smb_common.h" #include "../lib/tsocket/tsocket.h" +static void smbd_smb2_requeue_main_eventloop(struct smbd_server_connection *); + bool smbd_is_smb2_header(const uint8_t *inbuf, size_t size) { if (size < (4 + SMB2_HDR_BODY)) { @@ -418,20 +420,25 @@ struct smbd_smb2_request_pending_state { struct iovec vector; }; +static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *); static void smbd_smb2_request_pending_writev_done(struct tevent_req *subreq); +/* This was orginally (4 + SMB2_HDR_BODY + 0x08) */ +#define PENDING_REPLY_SIZE (SMB2_HDR_BODY + 0x08 ) NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, struct tevent_req *subreq) { struct smbd_smb2_request_pending_state *state; uint8_t *outhdr; int i = req->current_idx; + int j; uint32_t flags; uint64_t message_id; uint64_t async_id; uint8_t *hdr; uint8_t *body; + DEBUG(10,("smbd_smb2_request_pending_queue: idx[%d of %d] \n",i, req->out.vector_count)); if (!tevent_req_is_in_progress(subreq)) { return NT_STATUS_OK; } @@ -439,6 +446,10 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, req->subreq = subreq; subreq = NULL; + /* No request in a compound chain can succeed after we go async. */ + req->async_idx = req->current_idx; + req->next_status = NT_STATUS_INSUFFICIENT_RESOURCES; + outhdr = (uint8_t *)req->out.vector[i].iov_base; flags = IVAL(outhdr, SMB2_HDR_FLAGS); @@ -448,19 +459,26 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, SIVAL(outhdr, SMB2_HDR_FLAGS, flags | SMB2_HDR_FLAG_ASYNC); SBVAL(outhdr, SMB2_HDR_PID, async_id); - /* TODO: add a paramter to delay this */ - state = talloc(req->sconn, struct smbd_smb2_request_pending_state); - if (state == NULL) { + /* The hdr/body now go into the main reply, the impact is pretty large. */ + + req->out.vector[i].iov_base = talloc_size(req, PENDING_REPLY_SIZE); + req->async_out = talloc_array(req, struct iovec, 3); + if(req->out.vector[i].iov_base == NULL || req->async_out == NULL) { return NT_STATUS_NO_MEMORY; } - state->sconn = req->sconn; - state->vector.iov_base = (void *)state->buf; - state->vector.iov_len = sizeof(state->buf); + for(j=0;j<3;j++) { + req->async_out[j].iov_len = req->out.vector[i+j].iov_len; + req->async_out[j].iov_base = req->out.vector[i+j].iov_base; + } - _smb2_setlen(state->buf, sizeof(state->buf) - 4); - hdr = state->buf + 4; - body = hdr + SMB2_HDR_BODY; + req->out.vector[i].iov_len = PENDING_REPLY_SIZE; + _smb2_setlen(req->out.vector[i].iov_base, PENDING_REPLY_SIZE); + req->out.vector[i+1].iov_len = 0; + req->out.vector[i+2].iov_len = 0; + + hdr = req->out.vector[i].iov_base; + body = hdr + SMB2_HDR_BODY; SIVAL(hdr, SMB2_HDR_PROTOCOL_ID, SMB2_MAGIC); SSVAL(hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY); @@ -485,7 +503,7 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, SCVAL(body, 0x02, 0); SCVAL(body, 0x03, 0); SIVAL(body, 0x04, 0); - +#if 0 subreq = tstream_writev_queue_send(state, req->sconn->smb2.event_ctx, req->sconn->smb2.stream, @@ -497,8 +515,8 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, tevent_req_set_callback(subreq, smbd_smb2_request_pending_writev_done, state); - - return NT_STATUS_OK; +#endif + return smbd_smb2_request_reply(req); } static void smbd_smb2_request_pending_writev_done(struct tevent_req *subreq) @@ -511,6 +529,7 @@ static void smbd_smb2_request_pending_writev_done(struct tevent_req *subreq) int sys_errno; ret = tstream_writev_queue_recv(subreq, &sys_errno); + DEBUG(10,(" smbd_smb2_request_pending_writev_done - %d\n",ret)); TALLOC_FREE(subreq); if (ret == -1) { NTSTATUS status = map_nt_error_from_unix(sys_errno); @@ -537,11 +556,9 @@ static NTSTATUS smbd_smb2_request_process_cancel(struct smbd_smb2_request *req) search_message_id = BVAL(inhdr, SMB2_HDR_MESSAGE_ID); search_async_id = BVAL(inhdr, SMB2_HDR_PID); - /* - * we don't need the request anymore - * cancel requests never have a response - */ - TALLOC_FREE(req); + DEBUG(10,("smbd_smb2_request_process_cancel: mid - %d aid - %d\n", + search_message_id, + search_async_id)); for (cur = sconn->smb2.requests; cur; cur = cur->next) { const uint8_t *outhdr; @@ -567,9 +584,21 @@ static NTSTATUS smbd_smb2_request_process_cancel(struct smbd_smb2_request *req) } if (cur && cur->subreq) { + DEBUG(10,("cancelled: mid - %d aid - %d\n", + search_message_id, + search_async_id)); + cur->cancelled=1; tevent_req_cancel(cur->subreq); - } + } else { + DEBUG(10,("did non cancel: mid - %d aid - %d\n", + search_message_id, + search_async_id)); + } + + TALLOC_FREE(req); + /* Respool the event loop. */ + smbd_smb2_requeue_main_eventloop(sconn); return NT_STATUS_OK; } @@ -803,8 +832,13 @@ static void smbd_smb2_request_writev_done(struct tevent_req *subreq); static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req) { struct tevent_req *subreq; + struct iovec *async_in; + struct iovec *async_out; + int j; - req->subreq = NULL; +/* req->subreq = NULL; */ + + DEBUG(10,("smbd_smb2_request_reply: idx[%d of %d] \n",req->current_idx, req->out.vector_count)); smb2_setup_nbt_length(req->out.vector, req->out.vector_count); @@ -835,6 +869,7 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req) return NT_STATUS_OK; } + DEBUG(10,("smbd_smb2_request_reply sent: idx[%d of %d] \n",req->current_idx, req->out.vector_count)); subreq = tstream_writev_queue_send(req, req->sconn->smb2.event_ctx, req->sconn->smb2.stream, @@ -842,9 +877,43 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req) req->out.vector, req->out.vector_count); if (subreq == NULL) { + DEBUG(10,("smbd_smb2_request_reply - bailed early! \n")); + req->async_idx = 0; return NT_STATUS_NO_MEMORY; } + + /* Now to setup the async if there is one. */ + + if (req->async_idx > 0) { + DEBUG(10,("smbd_smb2_request_reply - async processing\n")); + async_in = talloc_array(req, struct iovec, 4); + async_out = talloc_array(req, struct iovec, 4); + req->out.vector_count = 4; + req->in.vector_count = 4; + async_in[0].iov_base = req->in.vector[0].iov_base; + async_in[0].iov_len = req->in.vector[0].iov_len; + + async_out[0].iov_base = talloc_size(req, req->out.vector[0].iov_len); + memcpy(async_out[0].iov_base, + req->out.vector[0].iov_base, + req->out.vector[0].iov_len); + async_out[0].iov_len = req->out.vector[0].iov_len; + + for (j=0;j<3;j++) { + async_in[j+1].iov_base = req->in.vector[req->async_idx+j].iov_base; + async_in[j+1].iov_len = req->in.vector[req->async_idx+j].iov_len; + async_out[j+1].iov_base = req->async_out[j].iov_base; + async_out[j+1].iov_len = req->async_out[j].iov_len; + } + req->in.vector = async_in; + req->out.vector = async_out; + req->async_idx = -1; + req->current_idx = 1; + } + + DEBUG(10,("smbd_smb2_request_reply - async processing done\n")); tevent_req_set_callback(subreq, smbd_smb2_request_writev_done, req); + DEBUG(10,("smbd_smb2_request_reply - callback installed\n")); return NT_STATUS_OK; } @@ -877,9 +946,21 @@ static void smbd_smb2_request_writev_done(struct tevent_req *subreq) int ret; int sys_errno; + DEBUG(10,("smbd_smb2_request_writev_done - async: %d\n", req->async_idx)); + ret = tstream_writev_queue_recv(subreq, &sys_errno); TALLOC_FREE(subreq); - TALLOC_FREE(req); + if ((req->async_idx==0 ^ req->async_out!=NULL) && !req->cancelled) { + DEBUG(10,("requeued main event loop\n")); + smbd_smb2_requeue_main_eventloop(sconn); + } + + if (req->async_idx==0) { + TALLOC_FREE(req); + } else { + req->async_idx = 0; + } + if (ret == -1) { NTSTATUS status = map_nt_error_from_unix(sys_errno); smbd_server_connection_terminate(sconn, nt_errstr(status)); @@ -921,8 +1002,14 @@ NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req, /* * if a request fails, all other remaining * compounded requests should fail too + * + * But the exact status matters, if we got NT_STATUS_INSUFFICIENT_RESOURCES, we + * need to pass it on, so the compounded request fails correctly. + * */ - req->next_status = NT_STATUS_INVALID_PARAMETER; + if(NT_STATUS_V(req->next_status) != NT_STATUS_V(NT_STATUS_INSUFFICIENT_RESOURCES)) { + req->next_status = NT_STATUS_INVALID_PARAMETER; + } return smbd_smb2_request_reply(req); } @@ -1538,7 +1625,11 @@ static void smbd_smb2_request_incoming(struct tevent_req *subreq) NTSTATUS status; struct smbd_smb2_request *req = NULL; + /* This should not be needed, but... safety first. */ + tevent_req_set_callback(subreq,NULL,NULL); + status = smbd_smb2_request_read_recv(subreq, sconn, &req); + tevent_queue_stop(sconn->smb2.recv_queue); TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status)) { smbd_server_connection_terminate(sconn, nt_errstr(status)); @@ -1574,13 +1665,22 @@ static void smbd_smb2_request_incoming(struct tevent_req *subreq) smbd_server_connection_terminate(sconn, nt_errstr(status)); return; } + return; + next: /* ask for the next request (this constructs the main loop) */ + smbd_smb2_requeue_main_eventloop(sconn); +} + +static void smbd_smb2_requeue_main_eventloop(struct smbd_server_connection *sconn) { + struct tevent_req *subreq; + tevent_queue_start(sconn->smb2.recv_queue); subreq = smbd_smb2_request_read_send(sconn, sconn->smb2.event_ctx, sconn); if (subreq == NULL) { smbd_server_connection_terminate(sconn, "no memory for reading"); return; } tevent_req_set_callback(subreq, smbd_smb2_request_incoming, sconn); + return; }