|
@@ -116,7 +116,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
|
|
spin_unlock(&GlobalMid_Lock);
|
|
|
server->maxBuf = 0;
|
|
|
|
|
|
- cFYI(1, ("Reconnecting tcp session "));
|
|
|
+ cFYI(1, ("Reconnecting tcp session"));
|
|
|
|
|
|
/* before reconnecting the tcp session, mark the smb session (uid)
|
|
|
and the tid bad so they are not used until reconnected */
|
|
@@ -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"));
|
|
|
+ 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,118 +465,140 @@ 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 */
|
|
|
+ reconnect = 0;
|
|
|
+
|
|
|
+ if(pdu_length > MAX_CIFS_HDR_SIZE - 4) {
|
|
|
+ isLargeBuf = TRUE;
|
|
|
+ memcpy(bigbuf, smallbuf, 4);
|
|
|
+ smb_buffer = bigbuf;
|
|
|
+ }
|
|
|
+ length = 0;
|
|
|
+ iov.iov_base = 4 + (char *)smb_buffer;
|
|
|
+ iov.iov_len = pdu_length;
|
|
|
+ for (total_read = 0; total_read < pdu_length;
|
|
|
+ total_read += length) {
|
|
|
+ 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) {
|
|
|
cifs_reconnect(server);
|
|
|
csocket = server->ssocket;
|
|
|
- wake_up(&server->response_q);
|
|
|
+ /* Reconnect wakes up rspns q */
|
|
|
+ /* Now we will reread sock */
|
|
|
+ reconnect = 1;
|
|
|
+ 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*/
|
|
|
continue;
|
|
|
- } else { /* length ok */
|
|
|
- int reconnect = 0;
|
|
|
-
|
|
|
- if(pdu_length > MAX_CIFS_HDR_SIZE - 4) {
|
|
|
- isLargeBuf = TRUE;
|
|
|
- memcpy(bigbuf, smallbuf, 4);
|
|
|
- smb_buffer = bigbuf;
|
|
|
- }
|
|
|
- length = 0;
|
|
|
- iov.iov_base = 4 + (char *)smb_buffer;
|
|
|
- iov.iov_len = pdu_length;
|
|
|
- for (total_read = 0;
|
|
|
- total_read < pdu_length;
|
|
|
- total_read += length) {
|
|
|
- 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) {
|
|
|
- cifs_reconnect(server);
|
|
|
- csocket = server->ssocket;
|
|
|
- /* Reconnect wakes up rspns q */
|
|
|
- /* Now we will reread sock */
|
|
|
- reconnect = 1;
|
|
|
- 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 */
|
|
|
- continue;
|
|
|
- } else if (length <= 0) {
|
|
|
- cERROR(1,("Received no data, expecting %d",
|
|
|
- pdu_length - total_read));
|
|
|
- cifs_reconnect(server);
|
|
|
- csocket = server->ssocket;
|
|
|
- reconnect = 1;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if(reconnect == 2)
|
|
|
- break;
|
|
|
- else if(reconnect == 1)
|
|
|
- continue;
|
|
|
-
|
|
|
- length += 4; /* account for rfc1002 hdr */
|
|
|
+ } else if (length <= 0) {
|
|
|
+ cERROR(1,("Received no data, expecting %d",
|
|
|
+ pdu_length - total_read));
|
|
|
+ cifs_reconnect(server);
|
|
|
+ csocket = server->ssocket;
|
|
|
+ reconnect = 1;
|
|
|
+ break;
|
|
|
}
|
|
|
+ }
|
|
|
+ if(reconnect == 2)
|
|
|
+ break;
|
|
|
+ else if(reconnect == 1)
|
|
|
+ continue;
|
|
|
|
|
|
- dump_smb(smb_buffer, length);
|
|
|
- if (checkSMB
|
|
|
- (smb_buffer, smb_buffer->Mid, total_read+4)) {
|
|
|
- cERROR(1, ("Bad SMB Received "));
|
|
|
- continue;
|
|
|
- }
|
|
|
+ length += 4; /* account for rfc1002 hdr */
|
|
|
+
|
|
|
|
|
|
+ dump_smb(smb_buffer, length);
|
|
|
+ if (checkSMB (smb_buffer, smb_buffer->Mid, total_read+4)) {
|
|
|
+ cERROR(1, ("Bad SMB Received "));
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- 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);
|
|
|
-
|
|
|
- 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;
|
|
|
- mid_entry->resp_buf =
|
|
|
- smb_buffer;
|
|
|
- mid_entry->midState =
|
|
|
- MID_RESPONSE_RECEIVED;
|
|
|
- if(isLargeBuf)
|
|
|
- mid_entry->largeBuf = 1;
|
|
|
- else
|
|
|
- mid_entry->largeBuf = 0;
|
|
|
- }
|
|
|
- }
|
|
|
- spin_unlock(&GlobalMid_Lock);
|
|
|
- if (task_to_wake) {
|
|
|
+
|
|
|
+ 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);
|
|
|
+
|
|
|
+ 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->largeBuf = 1;
|
|
|
+ isMultiRsp = TRUE;
|
|
|
+ bigbuf = NULL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ mid_entry->resp_buf = smb_buffer;
|
|
|
if(isLargeBuf)
|
|
|
- bigbuf = NULL;
|
|
|
+ mid_entry->largeBuf = 1;
|
|
|
else
|
|
|
- smallbuf = NULL;
|
|
|
- smb_buffer = NULL; /* will be freed by users thread after he is done */
|
|
|
- wake_up_process(task_to_wake);
|
|
|
- } else if (is_valid_oplock_break(smb_buffer) == FALSE) {
|
|
|
- cERROR(1, ("No task to wake, unknown frame rcvd!"));
|
|
|
- cifs_dump_mem("Received Data is: ",temp,sizeof(struct smb_hdr));
|
|
|
+ mid_entry->largeBuf = 0;
|
|
|
+multi_t2_fnd:
|
|
|
+ task_to_wake = mid_entry->tsk;
|
|
|
+ mid_entry->midState = MID_RESPONSE_RECEIVED;
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
+ spin_unlock(&GlobalMid_Lock);
|
|
|
+ if (task_to_wake) {
|
|
|
+ if(isLargeBuf)
|
|
|
+ bigbuf = NULL;
|
|
|
+ else
|
|
|
+ smallbuf = NULL;
|
|
|
+ /* smb buffer freed by user thread when done */
|
|
|
+ wake_up_process(task_to_wake);
|
|
|
+ } 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;
|