Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e4eb295d authored by Steve French's avatar Steve French Committed by Linus Torvalds
Browse files

[PATCH] cifs: Handle multiple response transact2 part 1 of 2

parent 46810cbf
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -330,7 +330,7 @@ struct smb_hdr {
};
/* given a pointer to an smb_hdr retrieve the value of byte count */
#define BCC(smb_var) ( *(__u16 *)((char *)smb_var + sizeof(struct smb_hdr) + (2* smb_var->WordCount) ) )

#define BCC_LE(smb_var) ( *(__le16 *)((char *)smb_var + sizeof(struct smb_hdr) + (2* smb_var->WordCount) ) )
/* given a pointer to an smb_hdr retrieve the pointer to the byte area */
#define pByteArea(smb_var) ((unsigned char *)smb_var + sizeof(struct smb_hdr) + (2* smb_var->WordCount) + 2 )

+245 −103
Original line number Diff line number Diff line
@@ -194,6 +194,121 @@ cifs_reconnect(struct TCP_Server_Info *server)
	return rc;
}

/* 
	return codes:
		0 	not a transact2, or all data present
		>0 	transact2 with that much data missing
		-EINVAL = invalid transact2

 */
static int check2ndT2(struct smb_hdr * pSMB, unsigned int maxBufSize)
{
	struct smb_t2_rsp * pSMBt;
        int total_data_size;
	int data_in_this_rsp;
	int remaining;

	if(pSMB->Command != SMB_COM_TRANSACTION2)
		return 0;

        /* check for plausible wct, bcc and t2 data and parm sizes */
        /* check for parm and data offset going beyond end of smb */
	if(pSMB->WordCount != 10) { /* coalesce_t2 depends on this */
		cFYI(1,("invalid transact2 word count"));
		return -EINVAL;
	}

	pSMBt = (struct smb_t2_rsp *)pSMB;

	total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount);
	data_in_this_rsp = le16_to_cpu(pSMBt->t2_rsp.DataCount);

	remaining = total_data_size - data_in_this_rsp;

	if(remaining == 0)
		return 0;
	else if(remaining < 0) {
		cFYI(1,("total data %d smaller than data in frame %d",
			total_data_size, data_in_this_rsp));
		return -EINVAL;
	} else {
		cFYI(1,("missing %d bytes from transact2, check next response",
			remaining));
		if(total_data_size > maxBufSize) {
			cERROR(1,("TotalDataSize %d is over maximum buffer %d",
				total_data_size,maxBufSize));
			return -EINVAL; 
		}
		return remaining;
	}
}

static int coalesce_t2(struct smb_hdr * psecond, struct smb_hdr *pTargetSMB)
{
	struct smb_t2_rsp *pSMB2 = (struct smb_t2_rsp *)psecond;
	struct smb_t2_rsp *pSMBt  = (struct smb_t2_rsp *)pTargetSMB;
	int total_data_size;
	int total_in_buf;
	int remaining;
	int total_in_buf2;
	char * data_area_of_target;
	char * data_area_of_buf2;
	__u16 byte_count;

	total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount);

	if(total_data_size != le16_to_cpu(pSMB2->t2_rsp.TotalDataCount)) {
		cFYI(1,("total data sizes of primary and secondary t2 differ"));
	}

	total_in_buf = le16_to_cpu(pSMBt->t2_rsp.DataCount);

	remaining = total_data_size - total_in_buf;
	
	if(remaining < 0)
		return -EINVAL;

	if(remaining == 0) /* nothing to do, ignore */
		return 0;
	
	total_in_buf2 = le16_to_cpu(pSMB2->t2_rsp.DataCount);
	if(remaining < total_in_buf2) {
		cFYI(1,("transact2 2nd response contains too much data"));
	}

	/* find end of first SMB data area */
	data_area_of_target = (char *)&pSMBt->hdr.Protocol + 
				le16_to_cpu(pSMBt->t2_rsp.DataOffset);
	/* validate target area */

	data_area_of_buf2 = (char *) &pSMB2->hdr.Protocol +
                                        le16_to_cpu(pSMB2->t2_rsp.DataOffset);

	data_area_of_target += total_in_buf;

	/* copy second buffer into end of first buffer */
	memcpy(data_area_of_target,data_area_of_buf2,total_in_buf2);
	total_in_buf += total_in_buf2;
	pSMBt->t2_rsp.DataCount = cpu_to_le16(total_in_buf);
	byte_count = le16_to_cpu(BCC_LE(pTargetSMB));
	byte_count += total_in_buf2;
	BCC_LE(pTargetSMB) = cpu_to_le16(byte_count);

	byte_count = be32_to_cpu(pTargetSMB->smb_buf_length);
	byte_count += total_in_buf2;

	/* BB also add check that we are not beyond maximum buffer size */
		
	pTargetSMB->smb_buf_length = cpu_to_be32(byte_count);

	if(remaining == total_in_buf2) {
		cFYI(1,("found the last secondary response"));
		return 0; /* we are done */
	} else /* more responses to go */
		return 1;

}

static int
cifs_demultiplex_thread(struct TCP_Server_Info *server)
{
@@ -211,6 +326,8 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
	struct mid_q_entry *mid_entry;
	char *temp;
	int isLargeBuf = FALSE;
	int isMultiRsp;
	int reconnect;

	daemonize("cifsd");
	allow_signal(SIGKILL);
@@ -254,6 +371,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
			memset(smallbuf, 0, sizeof (struct smb_hdr));

		isLargeBuf = FALSE;
		isMultiRsp = FALSE;
		smb_buffer = smallbuf;
		iov.iov_base = smb_buffer;
		iov.iov_len = 4;
@@ -311,13 +429,15 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)

		temp = (char *) smb_buffer;
		if (temp[0] == (char) RFC1002_SESSION_KEEP_ALIVE) {
			cFYI(0,("Received 4 byte keep alive packet"));
			continue; 
		} else if (temp[0] == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
			cFYI(1,("Good RFC 1002 session rsp"));
			continue;
		} else if (temp[0] == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
			/* we get this from Windows 98 instead of 
			   an error on SMB negprot response */
			cFYI(1,("Negative RFC 1002 Session Response Error 0x%x)",temp[4]));
			cFYI(1,("Negative RFC1002 Session Response Error 0x%x)",
				temp[4]));
			if(server->tcpStatus == CifsNew) {
				/* if nack on negprot (rather than 
				ret of smb negprot error) reconnecting
@@ -345,19 +465,21 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
			cifs_reconnect(server);
			csocket = server->ssocket;
			continue;
		} else { /* we have an SMB response */
			if((pdu_length > CIFSMaxBufSize + 
				MAX_CIFS_HDR_SIZE - 4) ||
		}

		/* else we have an SMB response */
		if((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) ||
			    (pdu_length < sizeof (struct smb_hdr) - 1 - 4)) {
				cERROR(1,
				    ("Invalid size SMB length %d and pdu_length %d",
			cERROR(1, ("Invalid size SMB length %d pdu_length %d",
					length, pdu_length+4));
			cifs_reconnect(server);
			csocket = server->ssocket;
			wake_up(&server->response_q);
			continue;
			} else { /* length ok */
				int reconnect = 0;
		} 

		/* else length ok */
		reconnect = 0;

		if(pdu_length > MAX_CIFS_HDR_SIZE - 4) {
			isLargeBuf = TRUE;
@@ -367,19 +489,16 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
		length = 0;
		iov.iov_base = 4 + (char *)smb_buffer;
		iov.iov_len = pdu_length;
				for (total_read = 0;
				     total_read < pdu_length;
		for (total_read = 0; total_read < pdu_length; 
		     total_read += length) {
					length = kernel_recvmsg(csocket, &smb_msg,
						&iov, 1,
			length = kernel_recvmsg(csocket, &smb_msg, &iov, 1,
						pdu_length - total_read, 0);
			if((server->tcpStatus == CifsExiting) ||
			    (length == -EINTR)) {
				/* then will exit */
				reconnect = 2;
				break;
					} else if (server->tcpStatus ==
						    CifsNeedReconnect) {
			} else if (server->tcpStatus == CifsNeedReconnect) {
				cifs_reconnect(server);
				csocket = server->ssocket;
			        /* Reconnect wakes up rspns q */
@@ -388,9 +507,10 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
				break;
			} else if ((length == -ERESTARTSYS) || 
				   (length == -EAGAIN)) {
						msleep(1); /* minimum sleep to prevent looping
                                                              allowing socket to clear and app threads to set
                                                              tcpStatus CifsNeedReconnect if server hung */
				msleep(1); /* minimum sleep to prevent looping,
                                              allowing socket to clear and app 
					      threads to set tcpStatus
					      CifsNeedReconnect if server hung*/
				continue;
			} else if (length <= 0) {
				cERROR(1,("Received no data, expecting %d",
@@ -407,11 +527,10 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
			continue;

		length += 4; /* account for rfc1002 hdr */
			}
	

		dump_smb(smb_buffer, length);
			if (checkSMB
			    (smb_buffer, smb_buffer->Mid, total_read+4)) {
		if (checkSMB (smb_buffer, smb_buffer->Mid, total_read+4)) {
			cERROR(1, ("Bad SMB Received "));
			continue;
		}
@@ -420,27 +539,49 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
		task_to_wake = NULL;
		spin_lock(&GlobalMid_Lock);
		list_for_each(tmp, &server->pending_mid_q) {
				mid_entry = list_entry(tmp, struct mid_q_entry,
							qhead);
			mid_entry = list_entry(tmp, struct mid_q_entry, qhead);

				if ((mid_entry->mid == smb_buffer->Mid)
					&& (mid_entry->midState == 
						MID_REQUEST_SUBMITTED) 
					&& (mid_entry->command == 
						smb_buffer->Command)) {
					cFYI(1,("Found Mid 0x%x wake up"
						,mid_entry->mid));
					/* BB FIXME - missing code here BB */
					/* check_2nd_t2(smb_buffer); */
					task_to_wake = mid_entry->tsk;
			if ((mid_entry->mid == smb_buffer->Mid) && 
			    (mid_entry->midState == MID_REQUEST_SUBMITTED) &&
			    (mid_entry->command == smb_buffer->Command)) {
				cFYI(1,("Found Mid 0x%x wake", mid_entry->mid));
					
				if(check2ndT2(smb_buffer,server->maxBuf) > 0) {
					/* We have a multipart transact2 resp */
					if(mid_entry->resp_buf) {
						/* merge response - fix up 1st*/
						if(coalesce_t2(smb_buffer, 
							mid_entry->resp_buf)) {
							isMultiRsp = TRUE;
							break;
						} else {
							/* all parts received */
							goto multi_t2_fnd; 
						}
					} else {
						if(!isLargeBuf) {
							cERROR(1,("1st trans2 resp needs bigbuf"));
					/* BB maybe we can fix this up,  switch
				   	   to already allocated large buffer? */
						} else {
							mid_entry->resp_buf =
								 smb_buffer;
					mid_entry->midState =
					    MID_RESPONSE_RECEIVED;
							mid_entry->largeBuf = 1;
							isMultiRsp = TRUE;
							bigbuf = NULL;
						}
					}
					break;
				} 
				mid_entry->resp_buf = smb_buffer;
				if(isLargeBuf)
					mid_entry->largeBuf = 1;
				else
					mid_entry->largeBuf = 0;
multi_t2_fnd:
				task_to_wake = mid_entry->tsk;
				mid_entry->midState = MID_RESPONSE_RECEIVED;
				break;
			}
		}
		spin_unlock(&GlobalMid_Lock);
@@ -449,14 +590,15 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
				bigbuf = NULL;
			else
				smallbuf = NULL;
				smb_buffer = NULL; /* will be freed by users thread after he is done */
			/* smb buffer freed by user thread when done */
			wake_up_process(task_to_wake);
			} else if (is_valid_oplock_break(smb_buffer) == FALSE) {                          
		} else if ((is_valid_oplock_break(smb_buffer) == FALSE) 
		    && (isMultiRsp == FALSE)) {                          
			cERROR(1, ("No task to wake, unknown frame rcvd!"));
			cifs_dump_mem("Received Data is: ",temp,sizeof(struct smb_hdr));
		}
		}
	}
	} /* end while !EXITING */

	spin_lock(&GlobalMid_Lock);
	server->tcpStatus = CifsExiting;
	server->tsk = NULL;