diff options
author | Steve French <smfrench@austin.rr.com> | 2005-04-29 01:41:09 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-29 01:41:09 -0400 |
commit | e4eb295d38b57f4d4b956942a48887eb252d97c6 (patch) | |
tree | d32dddbd699516a02d197eb9df0bef775082ffcd | |
parent | 46810cbf3d951c1ce8ce3311639996ad43ca4ed1 (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>
-rw-r--r-- | fs/cifs/cifspdu.h | 2 | ||||
-rw-r--r-- | fs/cifs/connect.c | 348 |
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 | */ | ||
204 | static 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 | |||
246 | static 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 | |||
197 | static int | 312 | static int |
198 | cifs_demultiplex_thread(struct TCP_Server_Info *server) | 313 | cifs_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 */ | 581 | multi_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; |