summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/cifs/cifspdu.h2
-rw-r--r--fs/cifs/connect.c348
2 files changed, 246 insertions, 104 deletions
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
index e3e92615705e..aede6a813167 100644
--- a/fs/cifs/cifspdu.h
+++ b/fs/cifs/cifspdu.h
@@ -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 )
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 419f145c80b5..a8d592bc33fe 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -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;