aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorSteve French <smfrench@austin.rr.com>2005-04-29 01:41:09 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-29 01:41:09 -0400
commite4eb295d38b57f4d4b956942a48887eb252d97c6 (patch)
treed32dddbd699516a02d197eb9df0bef775082ffcd /fs/cifs
parent46810cbf3d951c1ce8ce3311639996ad43ca4ed1 (diff)
[PATCH] cifs: Handle multiple response transact2 part 1 of 2
Signed-off-by: Steve French (sfrench@us.ibm.com) Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/cifs')
-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 {
330}; 330};
331/* given a pointer to an smb_hdr retrieve the value of byte count */ 331/* given a pointer to an smb_hdr retrieve the value of byte count */
332#define BCC(smb_var) ( *(__u16 *)((char *)smb_var + sizeof(struct smb_hdr) + (2* smb_var->WordCount) ) ) 332#define BCC(smb_var) ( *(__u16 *)((char *)smb_var + sizeof(struct smb_hdr) + (2* smb_var->WordCount) ) )
333 333#define BCC_LE(smb_var) ( *(__le16 *)((char *)smb_var + sizeof(struct smb_hdr) + (2* smb_var->WordCount) ) )
334/* given a pointer to an smb_hdr retrieve the pointer to the byte area */ 334/* given a pointer to an smb_hdr retrieve the pointer to the byte area */
335#define pByteArea(smb_var) ((unsigned char *)smb_var + sizeof(struct smb_hdr) + (2* smb_var->WordCount) + 2 ) 335#define pByteArea(smb_var) ((unsigned char *)smb_var + sizeof(struct smb_hdr) + (2* smb_var->WordCount) + 2 )
336 336
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)
116 spin_unlock(&GlobalMid_Lock); 116 spin_unlock(&GlobalMid_Lock);
117 server->maxBuf = 0; 117 server->maxBuf = 0;
118 118
119 cFYI(1, ("Reconnecting tcp session ")); 119 cFYI(1, ("Reconnecting tcp session"));
120 120
121 /* before reconnecting the tcp session, mark the smb session (uid) 121 /* before reconnecting the tcp session, mark the smb session (uid)
122 and the tid bad so they are not used until reconnected */ 122 and the tid bad so they are not used until reconnected */
@@ -194,6 +194,121 @@ cifs_reconnect(struct TCP_Server_Info *server)
194 return rc; 194 return rc;
195} 195}
196 196
197/*
198 return codes:
199 0 not a transact2, or all data present
200 >0 transact2 with that much data missing
201 -EINVAL = invalid transact2
202
203 */
204static int check2ndT2(struct smb_hdr * pSMB, unsigned int maxBufSize)
205{
206 struct smb_t2_rsp * pSMBt;
207 int total_data_size;
208 int data_in_this_rsp;
209 int remaining;
210
211 if(pSMB->Command != SMB_COM_TRANSACTION2)
212 return 0;
213
214 /* check for plausible wct, bcc and t2 data and parm sizes */
215 /* check for parm and data offset going beyond end of smb */
216 if(pSMB->WordCount != 10) { /* coalesce_t2 depends on this */
217 cFYI(1,("invalid transact2 word count"));
218 return -EINVAL;
219 }
220
221 pSMBt = (struct smb_t2_rsp *)pSMB;
222
223 total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount);
224 data_in_this_rsp = le16_to_cpu(pSMBt->t2_rsp.DataCount);
225
226 remaining = total_data_size - data_in_this_rsp;
227
228 if(remaining == 0)
229 return 0;
230 else if(remaining < 0) {
231 cFYI(1,("total data %d smaller than data in frame %d",
232 total_data_size, data_in_this_rsp));
233 return -EINVAL;
234 } else {
235 cFYI(1,("missing %d bytes from transact2, check next response",
236 remaining));
237 if(total_data_size > maxBufSize) {
238 cERROR(1,("TotalDataSize %d is over maximum buffer %d",
239 total_data_size,maxBufSize));
240 return -EINVAL;
241 }
242 return remaining;
243 }
244}
245
246static int coalesce_t2(struct smb_hdr * psecond, struct smb_hdr *pTargetSMB)
247{
248 struct smb_t2_rsp *pSMB2 = (struct smb_t2_rsp *)psecond;
249 struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)pTargetSMB;
250 int total_data_size;
251 int total_in_buf;
252 int remaining;
253 int total_in_buf2;
254 char * data_area_of_target;
255 char * data_area_of_buf2;
256 __u16 byte_count;
257
258 total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount);
259
260 if(total_data_size != le16_to_cpu(pSMB2->t2_rsp.TotalDataCount)) {
261 cFYI(1,("total data sizes of primary and secondary t2 differ"));
262 }
263
264 total_in_buf = le16_to_cpu(pSMBt->t2_rsp.DataCount);
265
266 remaining = total_data_size - total_in_buf;
267
268 if(remaining < 0)
269 return -EINVAL;
270
271 if(remaining == 0) /* nothing to do, ignore */
272 return 0;
273
274 total_in_buf2 = le16_to_cpu(pSMB2->t2_rsp.DataCount);
275 if(remaining < total_in_buf2) {
276 cFYI(1,("transact2 2nd response contains too much data"));
277 }
278
279 /* find end of first SMB data area */
280 data_area_of_target = (char *)&pSMBt->hdr.Protocol +
281 le16_to_cpu(pSMBt->t2_rsp.DataOffset);
282 /* validate target area */
283
284 data_area_of_buf2 = (char *) &pSMB2->hdr.Protocol +
285 le16_to_cpu(pSMB2->t2_rsp.DataOffset);
286
287 data_area_of_target += total_in_buf;
288
289 /* copy second buffer into end of first buffer */
290 memcpy(data_area_of_target,data_area_of_buf2,total_in_buf2);
291 total_in_buf += total_in_buf2;
292 pSMBt->t2_rsp.DataCount = cpu_to_le16(total_in_buf);
293 byte_count = le16_to_cpu(BCC_LE(pTargetSMB));
294 byte_count += total_in_buf2;
295 BCC_LE(pTargetSMB) = cpu_to_le16(byte_count);
296
297 byte_count = be32_to_cpu(pTargetSMB->smb_buf_length);
298 byte_count += total_in_buf2;
299
300 /* BB also add check that we are not beyond maximum buffer size */
301
302 pTargetSMB->smb_buf_length = cpu_to_be32(byte_count);
303
304 if(remaining == total_in_buf2) {
305 cFYI(1,("found the last secondary response"));
306 return 0; /* we are done */
307 } else /* more responses to go */
308 return 1;
309
310}
311
197static int 312static int
198cifs_demultiplex_thread(struct TCP_Server_Info *server) 313cifs_demultiplex_thread(struct TCP_Server_Info *server)
199{ 314{
@@ -211,6 +326,8 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
211 struct mid_q_entry *mid_entry; 326 struct mid_q_entry *mid_entry;
212 char *temp; 327 char *temp;
213 int isLargeBuf = FALSE; 328 int isLargeBuf = FALSE;
329 int isMultiRsp;
330 int reconnect;
214 331
215 daemonize("cifsd"); 332 daemonize("cifsd");
216 allow_signal(SIGKILL); 333 allow_signal(SIGKILL);
@@ -254,6 +371,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
254 memset(smallbuf, 0, sizeof (struct smb_hdr)); 371 memset(smallbuf, 0, sizeof (struct smb_hdr));
255 372
256 isLargeBuf = FALSE; 373 isLargeBuf = FALSE;
374 isMultiRsp = FALSE;
257 smb_buffer = smallbuf; 375 smb_buffer = smallbuf;
258 iov.iov_base = smb_buffer; 376 iov.iov_base = smb_buffer;
259 iov.iov_len = 4; 377 iov.iov_len = 4;
@@ -311,13 +429,15 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
311 429
312 temp = (char *) smb_buffer; 430 temp = (char *) smb_buffer;
313 if (temp[0] == (char) RFC1002_SESSION_KEEP_ALIVE) { 431 if (temp[0] == (char) RFC1002_SESSION_KEEP_ALIVE) {
314 cFYI(0,("Received 4 byte keep alive packet")); 432 continue;
315 } else if (temp[0] == (char)RFC1002_POSITIVE_SESSION_RESPONSE) { 433 } else if (temp[0] == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
316 cFYI(1,("Good RFC 1002 session rsp")); 434 cFYI(1,("Good RFC 1002 session rsp"));
435 continue;
317 } else if (temp[0] == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) { 436 } else if (temp[0] == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
318 /* we get this from Windows 98 instead of 437 /* we get this from Windows 98 instead of
319 an error on SMB negprot response */ 438 an error on SMB negprot response */
320 cFYI(1,("Negative RFC 1002 Session Response Error 0x%x)",temp[4])); 439 cFYI(1,("Negative RFC1002 Session Response Error 0x%x)",
440 temp[4]));
321 if(server->tcpStatus == CifsNew) { 441 if(server->tcpStatus == CifsNew) {
322 /* if nack on negprot (rather than 442 /* if nack on negprot (rather than
323 ret of smb negprot error) reconnecting 443 ret of smb negprot error) reconnecting
@@ -345,118 +465,140 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
345 cifs_reconnect(server); 465 cifs_reconnect(server);
346 csocket = server->ssocket; 466 csocket = server->ssocket;
347 continue; 467 continue;
348 } else { /* we have an SMB response */ 468 }
349 if((pdu_length > CIFSMaxBufSize + 469
350 MAX_CIFS_HDR_SIZE - 4) || 470 /* else we have an SMB response */
471 if((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) ||
351 (pdu_length < sizeof (struct smb_hdr) - 1 - 4)) { 472 (pdu_length < sizeof (struct smb_hdr) - 1 - 4)) {
352 cERROR(1, 473 cERROR(1, ("Invalid size SMB length %d pdu_length %d",
353 ("Invalid size SMB length %d and pdu_length %d",
354 length, pdu_length+4)); 474 length, pdu_length+4));
475 cifs_reconnect(server);
476 csocket = server->ssocket;
477 wake_up(&server->response_q);
478 continue;
479 }
480
481 /* else length ok */
482 reconnect = 0;
483
484 if(pdu_length > MAX_CIFS_HDR_SIZE - 4) {
485 isLargeBuf = TRUE;
486 memcpy(bigbuf, smallbuf, 4);
487 smb_buffer = bigbuf;
488 }
489 length = 0;
490 iov.iov_base = 4 + (char *)smb_buffer;
491 iov.iov_len = pdu_length;
492 for (total_read = 0; total_read < pdu_length;
493 total_read += length) {
494 length = kernel_recvmsg(csocket, &smb_msg, &iov, 1,
495 pdu_length - total_read, 0);
496 if((server->tcpStatus == CifsExiting) ||
497 (length == -EINTR)) {
498 /* then will exit */
499 reconnect = 2;
500 break;
501 } else if (server->tcpStatus == CifsNeedReconnect) {
355 cifs_reconnect(server); 502 cifs_reconnect(server);
356 csocket = server->ssocket; 503 csocket = server->ssocket;
357 wake_up(&server->response_q); 504 /* Reconnect wakes up rspns q */
505 /* Now we will reread sock */
506 reconnect = 1;
507 break;
508 } else if ((length == -ERESTARTSYS) ||
509 (length == -EAGAIN)) {
510 msleep(1); /* minimum sleep to prevent looping,
511 allowing socket to clear and app
512 threads to set tcpStatus
513 CifsNeedReconnect if server hung*/
358 continue; 514 continue;
359 } else { /* length ok */ 515 } else if (length <= 0) {
360 int reconnect = 0; 516 cERROR(1,("Received no data, expecting %d",
361 517 pdu_length - total_read));
362 if(pdu_length > MAX_CIFS_HDR_SIZE - 4) { 518 cifs_reconnect(server);
363 isLargeBuf = TRUE; 519 csocket = server->ssocket;
364 memcpy(bigbuf, smallbuf, 4); 520 reconnect = 1;
365 smb_buffer = bigbuf; 521 break;
366 }
367 length = 0;
368 iov.iov_base = 4 + (char *)smb_buffer;
369 iov.iov_len = pdu_length;
370 for (total_read = 0;
371 total_read < pdu_length;
372 total_read += length) {
373 length = kernel_recvmsg(csocket, &smb_msg,
374 &iov, 1,
375 pdu_length - total_read, 0);
376 if((server->tcpStatus == CifsExiting) ||
377 (length == -EINTR)) {
378 /* then will exit */
379 reconnect = 2;
380 break;
381 } else if (server->tcpStatus ==
382 CifsNeedReconnect) {
383 cifs_reconnect(server);
384 csocket = server->ssocket;
385 /* Reconnect wakes up rspns q */
386 /* Now we will reread sock */
387 reconnect = 1;
388 break;
389 } else if ((length == -ERESTARTSYS) ||
390 (length == -EAGAIN)) {
391 msleep(1); /* minimum sleep to prevent looping
392 allowing socket to clear and app threads to set
393 tcpStatus CifsNeedReconnect if server hung */
394 continue;
395 } else if (length <= 0) {
396 cERROR(1,("Received no data, expecting %d",
397 pdu_length - total_read));
398 cifs_reconnect(server);
399 csocket = server->ssocket;
400 reconnect = 1;
401 break;
402 }
403 }
404 if(reconnect == 2)
405 break;
406 else if(reconnect == 1)
407 continue;
408
409 length += 4; /* account for rfc1002 hdr */
410 } 522 }
523 }
524 if(reconnect == 2)
525 break;
526 else if(reconnect == 1)
527 continue;
411 528
412 dump_smb(smb_buffer, length); 529 length += 4; /* account for rfc1002 hdr */
413 if (checkSMB 530
414 (smb_buffer, smb_buffer->Mid, total_read+4)) {
415 cERROR(1, ("Bad SMB Received "));
416 continue;
417 }
418 531
532 dump_smb(smb_buffer, length);
533 if (checkSMB (smb_buffer, smb_buffer->Mid, total_read+4)) {
534 cERROR(1, ("Bad SMB Received "));
535 continue;
536 }
419 537
420 task_to_wake = NULL; 538
421 spin_lock(&GlobalMid_Lock); 539 task_to_wake = NULL;
422 list_for_each(tmp, &server->pending_mid_q) { 540 spin_lock(&GlobalMid_Lock);
423 mid_entry = list_entry(tmp, struct mid_q_entry, 541 list_for_each(tmp, &server->pending_mid_q) {
424 qhead); 542 mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
425 543
426 if ((mid_entry->mid == smb_buffer->Mid) 544 if ((mid_entry->mid == smb_buffer->Mid) &&
427 && (mid_entry->midState == 545 (mid_entry->midState == MID_REQUEST_SUBMITTED) &&
428 MID_REQUEST_SUBMITTED) 546 (mid_entry->command == smb_buffer->Command)) {
429 && (mid_entry->command == 547 cFYI(1,("Found Mid 0x%x wake", mid_entry->mid));
430 smb_buffer->Command)) { 548
431 cFYI(1,("Found Mid 0x%x wake up" 549 if(check2ndT2(smb_buffer,server->maxBuf) > 0) {
432 ,mid_entry->mid)); 550 /* We have a multipart transact2 resp */
433 /* BB FIXME - missing code here BB */ 551 if(mid_entry->resp_buf) {
434 /* check_2nd_t2(smb_buffer); */ 552 /* merge response - fix up 1st*/
435 task_to_wake = mid_entry->tsk; 553 if(coalesce_t2(smb_buffer,
436 mid_entry->resp_buf = 554 mid_entry->resp_buf)) {
437 smb_buffer; 555 isMultiRsp = TRUE;
438 mid_entry->midState = 556 break;
439 MID_RESPONSE_RECEIVED; 557 } else {
440 if(isLargeBuf) 558 /* all parts received */
441 mid_entry->largeBuf = 1; 559 goto multi_t2_fnd;
442 else 560 }
443 mid_entry->largeBuf = 0; 561 } else {
444 } 562 if(!isLargeBuf) {
445 } 563 cERROR(1,("1st trans2 resp needs bigbuf"));
446 spin_unlock(&GlobalMid_Lock); 564 /* BB maybe we can fix this up, switch
447 if (task_to_wake) { 565 to already allocated large buffer? */
566 } else {
567 mid_entry->resp_buf =
568 smb_buffer;
569 mid_entry->largeBuf = 1;
570 isMultiRsp = TRUE;
571 bigbuf = NULL;
572 }
573 }
574 break;
575 }
576 mid_entry->resp_buf = smb_buffer;
448 if(isLargeBuf) 577 if(isLargeBuf)
449 bigbuf = NULL; 578 mid_entry->largeBuf = 1;
450 else 579 else
451 smallbuf = NULL; 580 mid_entry->largeBuf = 0;
452 smb_buffer = NULL; /* will be freed by users thread after he is done */ 581multi_t2_fnd:
453 wake_up_process(task_to_wake); 582 task_to_wake = mid_entry->tsk;
454 } else if (is_valid_oplock_break(smb_buffer) == FALSE) { 583 mid_entry->midState = MID_RESPONSE_RECEIVED;
455 cERROR(1, ("No task to wake, unknown frame rcvd!")); 584 break;
456 cifs_dump_mem("Received Data is: ",temp,sizeof(struct smb_hdr));
457 } 585 }
458 } 586 }
459 } 587 spin_unlock(&GlobalMid_Lock);
588 if (task_to_wake) {
589 if(isLargeBuf)
590 bigbuf = NULL;
591 else
592 smallbuf = NULL;
593 /* smb buffer freed by user thread when done */
594 wake_up_process(task_to_wake);
595 } else if ((is_valid_oplock_break(smb_buffer) == FALSE)
596 && (isMultiRsp == FALSE)) {
597 cERROR(1, ("No task to wake, unknown frame rcvd!"));
598 cifs_dump_mem("Received Data is: ",temp,sizeof(struct smb_hdr));
599 }
600 } /* end while !EXITING */
601
460 spin_lock(&GlobalMid_Lock); 602 spin_lock(&GlobalMid_Lock);
461 server->tcpStatus = CifsExiting; 603 server->tcpStatus = CifsExiting;
462 server->tsk = NULL; 604 server->tsk = NULL;