The Samba-Bugzilla – Attachment 8702 Details for
Bug 9412
SMB2 server doesn't support recvfile.
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
samba4-smb2-recvfile-v4.patch
samba4-smb2-recvfile-v4.patch (text/plain), 10.86 KB, created by
Jose M. Prieto
on 2013-03-30 17:55:26 UTC
(
hide
)
Description:
samba4-smb2-recvfile-v4.patch
Filename:
MIME Type:
Creator:
Jose M. Prieto
Created:
2013-03-30 17:55:26 UTC
Size:
10.86 KB
patch
obsolete
>diff --git a/source3/smbd/aio.c b/source3/smbd/aio.c >index 3f553eb..d41868c 100644 >--- a/source3/smbd/aio.c >+++ b/source3/smbd/aio.c >@@ -833,6 +833,11 @@ NTSTATUS schedule_aio_smb2_write(connection_struct *conn, > size_t min_aio_write_size = lp_aio_write_size(SNUM(conn)); > struct tevent_req *req; > >+ if (smbreq->unread_bytes) { >+ /* Can't do async with recvfile. */ >+ return NT_STATUS_RETRY; >+ } >+ > if (fsp->base_fsp != NULL) { > /* No AIO on streams yet */ > DEBUG(10, ("AIO on streams not yet supported\n")); >diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h >index 0d0ebcd..10448e5 100644 >--- a/source3/smbd/globals.h >+++ b/source3/smbd/globals.h >@@ -245,6 +245,7 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, > uint32_t defer_time); > > struct smb_request *smbd_smb2_fake_smb_request(struct smbd_smb2_request *req); >+size_t smbd_smb2_unread_bytes(struct smbd_smb2_request *req); > void remove_smb2_chained_fsp(files_struct *fsp); > > NTSTATUS smbd_smb2_request_verify_creditcharge(struct smbd_smb2_request *req, >diff --git a/source3/smbd/smb2_glue.c b/source3/smbd/smb2_glue.c >index 1b2b4dd..23d6ce5 100644 >--- a/source3/smbd/smb2_glue.c >+++ b/source3/smbd/smb2_glue.c >@@ -28,9 +28,13 @@ struct smb_request *smbd_smb2_fake_smb_request(struct smbd_smb2_request *req) > struct smb_request *smbreq; > const uint8_t *inhdr = SMBD_SMB2_IN_HDR_PTR(req); > >- smbreq = talloc_zero(req, struct smb_request); >- if (smbreq == NULL) { >- return NULL; >+ if (req->smb1req) { >+ smbreq = req->smb1req; >+ } else { >+ smbreq = talloc_zero(req, struct smb_request); >+ if (smbreq == NULL) { >+ return NULL; >+ } > } > > smbreq->request_time = req->request_time; >@@ -55,6 +59,18 @@ struct smb_request *smbd_smb2_fake_smb_request(struct smbd_smb2_request *req) > } > > /********************************************************* >+ Are there unread bytes for recvfile ? >+*********************************************************/ >+ >+size_t smbd_smb2_unread_bytes(struct smbd_smb2_request *req) >+{ >+ if (req->smb1req && req->smb1req->unread_bytes) { >+ return req->smb1req->unread_bytes; >+ } >+ return 0; >+} >+ >+/********************************************************* > Called from file_free() to remove any chained fsp pointers. > *********************************************************/ > >diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c >index d92302e..44301ac 100644 >--- a/source3/smbd/smb2_server.c >+++ b/source3/smbd/smb2_server.c >@@ -1824,6 +1824,11 @@ NTSTATUS smbd_smb2_request_verify_sizes(struct smbd_smb2_request *req, > case SMB2_OP_GETINFO: > min_dyn_size = 0; > break; >+ case SMB2_OP_WRITE: >+ if (req->smb1req && req->smb1req->unread_bytes) { >+ min_dyn_size = 0; >+ } >+ break; > } > > /* >@@ -2626,6 +2631,16 @@ NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req, > req->current_idx, nt_errstr(status), info ? " +info" : "", > location)); > >+ if (req->smb1req && req->smb1req->unread_bytes) { >+ /* Recvfile error. Drain incoming socket. */ >+ if (drain_socket(req->sconn->sock, >+ req->smb1req->unread_bytes) != >+ req->smb1req->unread_bytes) >+ { >+ smb_panic("Failed to drain SMB2 socket\n"); >+ } >+ } >+ > body.data = outhdr + SMB2_HDR_BODY; > body.length = 8; > SSVAL(body.data, 0, 9); >@@ -2823,8 +2838,116 @@ struct smbd_smb2_request_read_state { > } hdr; > size_t pktlen; > uint8_t *pktbuf; >+ bool smb2_hdr_read; >+ bool smb2_rest_read; > }; > >+static bool smbd_is_smb2_recvfile_write( >+ struct smbd_smb2_request_read_state *state, >+ size_t dyn_size) >+{ >+ int min_recv_size = lp_min_receive_file_size(); >+ const uint8_t *inhdr = state->pktbuf; >+ size_t next_command_ofs; >+ uint16_t opcode; >+ uint32_t in_flags; >+ uint32_t in_tid; >+ uint64_t in_session_id; >+ struct smbXsrv_tcon *tcon; >+ struct smbXsrv_session *session = NULL; >+ struct timeval request_time; >+ NTTIME now; >+ NTSTATUS status; >+ >+ if (min_recv_size == 0) { >+ /* recvfile not turned on. Nothing to do here. */ >+ DEBUG(10,("min receivefile size turned off\n")); >+ return false; >+ } >+ >+ if (IVAL(inhdr, SMB2_HDR_PROTOCOL_ID) != SMB2_MAGIC) { >+ /* Bad SMB2 header. Let the caller deal with it. */ >+ DEBUG(10,("bad SMB2 header\n")); >+ return false; >+ } >+ >+ opcode = SVAL(inhdr, SMB2_HDR_OPCODE); >+ >+ if (opcode != SMB2_OP_WRITE) { >+ /* Only do recvfile in SMB2_WRITE. */ >+ DEBUG(10,("not a SMB2_WRITE command [opcode=0x%02X]\n", opcode)); >+ return false; >+ } >+ >+ next_command_ofs = IVAL(inhdr, SMB2_HDR_NEXT_COMMAND); >+ >+ if (next_command_ofs != 0) { >+ /* Can't do recvfile on compound requests. */ >+ DEBUG(10,("can't do recvfile on compound requests " >+ "[next command not equal to 0]\n")); >+ return false; >+ } >+ >+ in_flags = IVAL(inhdr, SMB2_HDR_FLAGS); >+ >+ if (in_flags & SMB2_HDR_FLAG_CHAINED) { >+ /* Can't do recvfile on compound requests. */ >+ DEBUG(10,("can't do recvfile on compound requests " >+ "[flag SMB2_FLAGS_RELATED_OPERATIONS set]\n")); >+ return false; >+ } >+ >+ if (in_flags & SMB2_HDR_FLAG_SIGNED) { >+ /* Can't do recvfile on signed connection. */ >+ DEBUG(10,("can't do recvfile on signed connection\n")); >+ return false; >+ } >+ >+ if (dyn_size == 0 || dyn_size < min_recv_size) { >+ /* Bad write, or too small for us. Caller handle. */ >+ DEBUG(10,("bad write command or too small " >+ "[dyn_size=%lu, min_recv_size=%d]\n", dyn_size, min_recv_size)); >+ return false; >+ } >+ >+ /* >+ * can't do recvfile on IPC or print connections. >+ */ >+ request_time = timeval_current(); >+ now = timeval_to_nttime(&request_time); >+ in_session_id = BVAL(inhdr, SMB2_HDR_SESSION_ID); >+ status = smb2srv_session_lookup(state->sconn->conn, >+ in_session_id, now, &session); >+ if (!NT_STATUS_IS_OK(status)) { >+ DEBUG(10,("can't look up this session ID [session ID=%ld]\n", >+ in_session_id)); >+ return false; >+ } >+ if (!session || !NT_STATUS_IS_OK(session->status)) { >+ DEBUG(10,("session ID %ld is invalid\n", in_session_id)); >+ return false; >+ } >+ >+ in_tid = IVAL(inhdr, SMB2_HDR_TID); >+ status = smb2srv_tcon_lookup(session, in_tid, now, &tcon); >+ if (!NT_STATUS_IS_OK(status)) { >+ DEBUG(10,("can't look up this tree ID [tree ID=%d]\n", in_tid)); >+ return false; >+ } >+ >+ if (IS_IPC(tcon->compat) || IS_PRINT(tcon->compat)) { >+ DEBUG(10,("can't do recvfile on IPC or print connections\n")); >+ return false; >+ } >+ >+ /* >+ * OK, this is a recvfile we can handle on >+ * a file share. >+ */ >+ DEBUG(10,("SMB2_WRITE will be handled as recvfile\n")); >+ return true; >+} >+ > static int smbd_smb2_request_next_vector(struct tstream_context *stream, > void *private_data, > TALLOC_CTX *mem_ctx, >@@ -2878,9 +3001,10 @@ static int smbd_smb2_request_next_vector(struct tstream_context *stream, > talloc_get_type_abort(private_data, > struct smbd_smb2_request_read_state); > struct iovec *vector; >+ size_t pdulen; > >- if (state->pktlen > 0) { >- /* if there're no remaining bytes, we're done */ >+ if (state->smb2_rest_read && state->pktlen > 0) { >+ /* all set for this packet, we're done */ > *_vector = NULL; > *_count = 0; > return 0; >@@ -2908,27 +3032,88 @@ static int smbd_smb2_request_next_vector(struct tstream_context *stream, > /* > * Now we analyze the NBT header > */ >- state->pktlen = smb2_len(state->hdr.nbt); >- >- if (state->pktlen == 0) { >+ pdulen = smb2_len(state->hdr.nbt); >+ if (pdulen == 0) { > /* if there're no remaining bytes, we're done */ > *_vector = NULL; > *_count = 0; > return 0; > } > >- state->pktbuf = talloc_array(state->smb2_req, uint8_t, state->pktlen); >- if (state->pktbuf == NULL) { >- return -1; >- } >+ /* >+ * allocate memory for vector to be returned >+ */ > > vector = talloc_array(mem_ctx, struct iovec, 1); > if (vector == NULL) { > return -1; > } > >- vector[0].iov_base = (void *)state->pktbuf; >- vector[0].iov_len = state->pktlen; >+ if (!state->smb2_hdr_read) { >+ /* >+ * SMB2 header not read yet, so... >+ * 1. allocate buffer for whole SMB2 packet >+ * 2. mark SMB2 header as read >+ * 3. return next vector (reading SMB2 header only) >+ */ >+ state->pktlen = pdulen; >+ state->pktbuf = talloc_array(state->smb2_req, uint8_t, state->pktlen); >+ if (state->pktbuf == NULL) { >+ return -1; >+ } >+ >+ state->smb2_hdr_read = true; >+ >+ /* >+ * read whole SMB2 header plus next 2 bytes >+ * (size of SMB2 command) and store at the >+ * beginning of PDU buffer >+ */ >+ vector[0].iov_base = (void *)state->pktbuf; >+ vector[0].iov_len = SMB2_HDR_BODY + 2; >+ } else { >+ /* >+ * SMB2 header has been read already, so... >+ * 1. check whether this is a SMB2_WRITE with recvfile turned on >+ * 2. if so, adjust packet length to be read >+ * 3. if not, proceed to read rest of the packet >+ * 4. return vector (either adjusted or not) >+ */ >+ >+ /* >+ * assuming this is a SMB2_WRITE, variable buffer length will be the >+ * whole PDU packet lenght minus the SMB2 and SMB2_WRITE headers >+ * together >+ */ >+ size_t cmd_size = SVAL(state->pktbuf, SMB2_HDR_BODY) & 0xFFFE; >+ size_t dyn_size = state->pktlen - (SMB2_HDR_BODY + cmd_size); >+ >+ if (smbd_is_smb2_recvfile_write(state, dyn_size)) { >+ /* >+ * adjust packet and allocate space >+ * for fake SMB1 request >+ */ >+ state->smb2_req->smb1req = talloc_zero(state->smb2_req, >+ struct smb_request); >+ if (!state->smb2_req->smb1req) { >+ return -1; >+ } >+ >+ state->smb2_req->smb1req->unread_bytes = dyn_size; >+ state->pktlen -= dyn_size; >+ >+ state->pktbuf = talloc_realloc(state->smb2_req, state->pktbuf, >+ uint8_t, state->pktlen); >+ if (state->pktbuf == NULL) { >+ return -1; >+ } >+ } >+ >+ state->smb2_rest_read = true; >+ >+ vector[0].iov_base = (void *)(state->pktbuf + SMB2_HDR_BODY + 2); >+ vector[0].iov_len = state->pktlen - SMB2_HDR_BODY - 2; >+ } > > *_vector = vector; > *_count = 1; >diff --git a/source3/smbd/smb2_write.c b/source3/smbd/smb2_write.c >index f9cfbfc..6800070 100644 >--- a/source3/smbd/smb2_write.c >+++ b/source3/smbd/smb2_write.c >@@ -49,6 +49,7 @@ NTSTATUS smbd_smb2_request_process_write(struct smbd_smb2_request *req) > struct files_struct *in_fsp; > uint32_t in_flags; > struct tevent_req *subreq; >+ size_t unread_bytes; > > status = smbd_smb2_request_verify_sizes(req, 0x31); > if (!NT_STATUS_IS_OK(status)) { >@@ -67,7 +68,21 @@ NTSTATUS smbd_smb2_request_process_write(struct smbd_smb2_request *req) > return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); > } > >- if (in_data_length > SMBD_SMB2_IN_DYN_LEN(req)) { >+ unread_bytes = smbd_smb2_unread_bytes(req); >+ if (unread_bytes) { >+ /* >+ * RECVFILE code path. Ensure we have the >+ * correct number of bytes left in the socket >+ * buffers. >+ */ >+ if (in_data_length != unread_bytes) { >+ DEBUG(2,("smbd_smb2_request_process_write : " >+ "data length in SMB2 packet does not match " >+ ":%s:PDU=0x%08X:guessed=0x%08X\n", __location__, >+ in_data_length, (unsigned int)unread_bytes)); >+ return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); >+ } >+ } else if (in_data_length > SMBD_SMB2_IN_DYN_LEN(req)) { > return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); > } >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 9412
:
8216
| 8702 |
8706
|
8707
|
8716
|
8728
|
8737
|
8803
|
8804