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

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

Signed-off-by: Steve French (sfrench@us.ibm.com)
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 46810cbf
...@@ -330,7 +330,7 @@ struct smb_hdr { ...@@ -330,7 +330,7 @@ struct smb_hdr {
}; };
/* given a pointer to an smb_hdr retrieve the value of byte count */ /* 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(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 */ /* 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 ) #define pByteArea(smb_var) ((unsigned char *)smb_var + sizeof(struct smb_hdr) + (2* smb_var->WordCount) + 2 )
......
...@@ -116,7 +116,7 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -116,7 +116,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
server->maxBuf = 0; server->maxBuf = 0;
cFYI(1, ("Reconnecting tcp session ")); cFYI(1, ("Reconnecting tcp session"));
/* before reconnecting the tcp session, mark the smb session (uid) /* before reconnecting the tcp session, mark the smb session (uid)
and the tid bad so they are not used until reconnected */ and the tid bad so they are not used until reconnected */
...@@ -194,6 +194,121 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -194,6 +194,121 @@ cifs_reconnect(struct TCP_Server_Info *server)
return rc; 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 static int
cifs_demultiplex_thread(struct TCP_Server_Info *server) cifs_demultiplex_thread(struct TCP_Server_Info *server)
{ {
...@@ -211,6 +326,8 @@ 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; struct mid_q_entry *mid_entry;
char *temp; char *temp;
int isLargeBuf = FALSE; int isLargeBuf = FALSE;
int isMultiRsp;
int reconnect;
daemonize("cifsd"); daemonize("cifsd");
allow_signal(SIGKILL); allow_signal(SIGKILL);
...@@ -254,6 +371,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -254,6 +371,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
memset(smallbuf, 0, sizeof (struct smb_hdr)); memset(smallbuf, 0, sizeof (struct smb_hdr));
isLargeBuf = FALSE; isLargeBuf = FALSE;
isMultiRsp = FALSE;
smb_buffer = smallbuf; smb_buffer = smallbuf;
iov.iov_base = smb_buffer; iov.iov_base = smb_buffer;
iov.iov_len = 4; iov.iov_len = 4;
...@@ -311,13 +429,15 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -311,13 +429,15 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
temp = (char *) smb_buffer; temp = (char *) smb_buffer;
if (temp[0] == (char) RFC1002_SESSION_KEEP_ALIVE) { 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) { } else if (temp[0] == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
cFYI(1,("Good RFC 1002 session rsp")); cFYI(1,("Good RFC 1002 session rsp"));
continue;
} else if (temp[0] == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) { } else if (temp[0] == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
/* we get this from Windows 98 instead of /* we get this from Windows 98 instead of
an error on SMB negprot response */ 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(server->tcpStatus == CifsNew) {
/* if nack on negprot (rather than /* if nack on negprot (rather than
ret of smb negprot error) reconnecting ret of smb negprot error) reconnecting
...@@ -345,19 +465,21 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -345,19 +465,21 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
cifs_reconnect(server); cifs_reconnect(server);
csocket = server->ssocket; csocket = server->ssocket;
continue; 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)) { (pdu_length < sizeof (struct smb_hdr) - 1 - 4)) {
cERROR(1, cERROR(1, ("Invalid size SMB length %d pdu_length %d",
("Invalid size SMB length %d and pdu_length %d",
length, pdu_length+4)); length, pdu_length+4));
cifs_reconnect(server); cifs_reconnect(server);
csocket = server->ssocket; csocket = server->ssocket;
wake_up(&server->response_q); wake_up(&server->response_q);
continue; continue;
} else { /* length ok */ }
int reconnect = 0;
/* else length ok */
reconnect = 0;
if(pdu_length > MAX_CIFS_HDR_SIZE - 4) { if(pdu_length > MAX_CIFS_HDR_SIZE - 4) {
isLargeBuf = TRUE; isLargeBuf = TRUE;
...@@ -367,19 +489,16 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -367,19 +489,16 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
length = 0; length = 0;
iov.iov_base = 4 + (char *)smb_buffer; iov.iov_base = 4 + (char *)smb_buffer;
iov.iov_len = pdu_length; iov.iov_len = pdu_length;
for (total_read = 0; for (total_read = 0; total_read < pdu_length;
total_read < pdu_length;
total_read += length) { total_read += length) {
length = kernel_recvmsg(csocket, &smb_msg, length = kernel_recvmsg(csocket, &smb_msg, &iov, 1,
&iov, 1,
pdu_length - total_read, 0); pdu_length - total_read, 0);
if((server->tcpStatus == CifsExiting) || if((server->tcpStatus == CifsExiting) ||
(length == -EINTR)) { (length == -EINTR)) {
/* then will exit */ /* then will exit */
reconnect = 2; reconnect = 2;
break; break;
} else if (server->tcpStatus == } else if (server->tcpStatus == CifsNeedReconnect) {
CifsNeedReconnect) {
cifs_reconnect(server); cifs_reconnect(server);
csocket = server->ssocket; csocket = server->ssocket;
/* Reconnect wakes up rspns q */ /* Reconnect wakes up rspns q */
...@@ -388,9 +507,10 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -388,9 +507,10 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
break; break;
} else if ((length == -ERESTARTSYS) || } else if ((length == -ERESTARTSYS) ||
(length == -EAGAIN)) { (length == -EAGAIN)) {
msleep(1); /* minimum sleep to prevent looping msleep(1); /* minimum sleep to prevent looping,
allowing socket to clear and app threads to set allowing socket to clear and app
tcpStatus CifsNeedReconnect if server hung */ threads to set tcpStatus
CifsNeedReconnect if server hung*/
continue; continue;
} else if (length <= 0) { } else if (length <= 0) {
cERROR(1,("Received no data, expecting %d", cERROR(1,("Received no data, expecting %d",
...@@ -407,11 +527,10 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -407,11 +527,10 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
continue; continue;
length += 4; /* account for rfc1002 hdr */ length += 4; /* account for rfc1002 hdr */
}
dump_smb(smb_buffer, length); dump_smb(smb_buffer, length);
if (checkSMB if (checkSMB (smb_buffer, smb_buffer->Mid, total_read+4)) {
(smb_buffer, smb_buffer->Mid, total_read+4)) {
cERROR(1, ("Bad SMB Received ")); cERROR(1, ("Bad SMB Received "));
continue; continue;
} }
...@@ -420,27 +539,49 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -420,27 +539,49 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
task_to_wake = NULL; task_to_wake = NULL;
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
list_for_each(tmp, &server->pending_mid_q) { list_for_each(tmp, &server->pending_mid_q) {
mid_entry = list_entry(tmp, struct mid_q_entry, mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
qhead);
if ((mid_entry->mid == smb_buffer->Mid) if ((mid_entry->mid == smb_buffer->Mid) &&
&& (mid_entry->midState == (mid_entry->midState == MID_REQUEST_SUBMITTED) &&
MID_REQUEST_SUBMITTED) (mid_entry->command == smb_buffer->Command)) {
&& (mid_entry->command == cFYI(1,("Found Mid 0x%x wake", mid_entry->mid));
smb_buffer->Command)) {
cFYI(1,("Found Mid 0x%x wake up" if(check2ndT2(smb_buffer,server->maxBuf) > 0) {
,mid_entry->mid)); /* We have a multipart transact2 resp */
/* BB FIXME - missing code here BB */ if(mid_entry->resp_buf) {
/* check_2nd_t2(smb_buffer); */ /* merge response - fix up 1st*/
task_to_wake = mid_entry->tsk; 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 = mid_entry->resp_buf =
smb_buffer; smb_buffer;
mid_entry->midState = mid_entry->largeBuf = 1;
MID_RESPONSE_RECEIVED; isMultiRsp = TRUE;
bigbuf = NULL;
}
}
break;
}
mid_entry->resp_buf = smb_buffer;
if(isLargeBuf) if(isLargeBuf)
mid_entry->largeBuf = 1; mid_entry->largeBuf = 1;
else else
mid_entry->largeBuf = 0; mid_entry->largeBuf = 0;
multi_t2_fnd:
task_to_wake = mid_entry->tsk;
mid_entry->midState = MID_RESPONSE_RECEIVED;
break;
} }
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
...@@ -449,14 +590,15 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -449,14 +590,15 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
bigbuf = NULL; bigbuf = NULL;
else else
smallbuf = NULL; 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); 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!")); cERROR(1, ("No task to wake, unknown frame rcvd!"));
cifs_dump_mem("Received Data is: ",temp,sizeof(struct smb_hdr)); cifs_dump_mem("Received Data is: ",temp,sizeof(struct smb_hdr));
} }
} } /* end while !EXITING */
}
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
server->tcpStatus = CifsExiting; server->tcpStatus = CifsExiting;
server->tsk = NULL; server->tsk = NULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment