[PATCH] Fix bug 9412 - SMB2 server doesn't support recvfile.

Stefan (metze) Metzmacher metze at samba.org
Thu Apr 4 01:56:16 MDT 2013


Hi Jeremy,

> The good news is that it got a lot smaller :-).
> I'm really pleased with how little I had to
> change to make this work. It's only a 10 element
> patch set, and most of these are 5-10 line micro
> changes.
> 
> It turns out the changes made for 4.0.x meant
> that I had to make *no* changes in the
> smbd/smb2_write.c code to make RECVFILE work
> (which is pretty amazing :-), and I think proves
> we have some good abstractions in the 4.0.x code.
> 
> This patchset has been tested by me under valgrind,
> and by the OEM on their box, and gives an 18%
> improvement in SMB2 write speed which finally makes
> SMB2 faster than SMB1 on their smaller machines.
> 
> Please review and give feedback (feel free to push
> if you're happy with it :-).

This really looks great! Thanks!

I have a few comments see below.

> diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
> index eb7059e..edd54cc 100644
> --- a/source3/smbd/smb2_server.c
> +++ b/source3/smbd/smb2_server.c
> @@ -2621,11 +2621,20 @@ NTSTATUS smbd_smb2_request_error_ex(struct smbd_smb2_request *req,
>  {
>  	DATA_BLOB body;
>  	uint8_t *outhdr = SMBD_SMB2_OUT_HDR_PTR(req);
> +	size_t unread_bytes = smbd_smb2_unread_bytes(req);
>  
>  	DEBUG(10,("smbd_smb2_request_error_ex: idx[%d] status[%s] |%s| at %s\n",
>  		  req->current_idx, nt_errstr(status), info ? " +info" : "",
>  		  location));
>  
> +	if (unread_bytes) {
> +		/* Recvfile error. Drain incoming socket. */
> +		if (drain_socket(req->sconn->sock, unread_bytes) !=
> +				unread_bytes) {
> +			smb_panic("Failed to drain SMB2 socket\n");
> +		}
> +	}
> +

Also a helper variable to avoid the function call within the if
statement would be nice.

I think it would be also better to return an error and correctly
terminate the connection
instead of using smb_panic(), it's not unlikely that the connection got
broken.
For a broken connection we should terminate gracefully and let the
client reopen
durable handles.

I think we should add some protection to req->sconn->smb2.recv_queue,
when we do a short read we should place a dummy tevent_req into the
recv_queue,
which blocks the socket, and remove it when the socket is drained or
recvfile is done.

next to smb1req->unread_bytes we could setup smb2req->recvfile_blocker;

struct smbd_smb2_recvfile_blocker {
	uint8_t dummy;
};

static void smbd_smb2_recvfile_blocker_trigger(struct tevent_req *req,
					       void *private_data)
{
	return;
}

...
	struct smbd_smb2_recvfile_blocker *recvfile_blocker;
	smb2req->recvfile_blocker = tevent_req_create(smb2req, &recvfile_blocker,
						      smbd_smb2_recvfile_blocker);
	if (smb2req->recvfile_blocker == NULL) {
		...
	}
	ok = tevent_queue_add(smb2req->sconn->smb2.recv_queue,
			      ...event_context,
			      smb2req->recvfile_blocker,
			      smbd_smb2_recvfile_blocker_trigger,
			      NULL);

smbd_smb2_request_error_ex() would then also do
TALLOC_FREE(smb2req->recvfile_blocker);
after drain_socket() and even if unread_bytes was 0.

> +		} else {
> +			/*
> +			 * Either this is a receivefile write so we've
> +			 * done a short read, or if not we have all the data.
> +			 * Either way, we're done and the caller will handle
> +			 * and short read case by looking at the
> +			 * state->doing_receivefile value.
> +			 */
> +			*_vector = NULL;
> +			*_count = 0;
> +		}

Shouldn't the caller take a look at smb1req->unread_bytes?
state->doing_receivefile is not visible to the caller.

>  	vector[0].iov_base = (void *)state->pktbuf;
> -	vector[0].iov_len = state->pktlen;
> +	if (state->min_recv_size &&
> +			state->pktlen > SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN +
> +					state->min_recv_size) {

Can you add a helper variable to get rid of the strange formatting?
Something like:

min_recvfile_size = SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN;
min_recvfile_size += state->min_recv_size;
if (state->min_recv_size == 0) {
	min_recvfile_size = UINT32_MAX;
}
if (state->pktlen > min_recvfile_size) {

> +	if (IVAL(state->pktbuf, 0) == SMB2_TF_MAGIC) {
> +		/* Transform header. Cannot recvfile. */
> +		return false;
> +	}
> +
> +	if (IVAL(state->pktbuf, 0) != SMB2_MAGIC) {
> +		/* Not SMB2. Normal error path will cope. */
> +		return false;
> +	}
> +	if (SVAL(state->pktbuf, 4) != SMB2_HDR_BODY) {
> +		/* Not SMB2. Normal error path will cope. */
> +		return false;
> +	}
> +	if (IVAL(state->pktbuf, SMB2_HDR_NEXT_COMMAND) != 0) {
> +		/* Chained. Cannot recvfile. */
> +		return false;
> +	}
> +	if (IVAL(state->pktbuf, SMB2_HDR_FLAGS) &
> +			(SMB2_HDR_FLAG_CHAINED|
> +			 SMB2_HDR_FLAG_SIGNED)) {
> +		/* Signed or chained. Cannot recvfile. */
> +		return false;
> +	}

Can you split this into 2 checks to avoid the strange formatting?

> +	if (SVAL(state->pktbuf, SMB2_HDR_OPCODE) != SMB2_OP_WRITE) {
> +		/* Needs to be a WRITE. */
> +		return false;
> +	}

Shouldn't we do this check as the first check?

metze

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 261 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20130404/a9657151/attachment.pgp>


More information about the samba-technical mailing list