diff options
author | Pavel Shilovsky <pshilovsky@samba.org> | 2012-05-23 06:31:03 -0400 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2012-07-24 01:32:25 -0400 |
commit | 316cf94a910f6f93d43cc574359d163ccae098a3 (patch) | |
tree | 8a3b54eab2154f843a7040ac40920d4c17600415 /fs | |
parent | 7659624ffb550d69c87f9af9ae63e717daa874bd (diff) |
CIFS: Move trans2 processing to ops struct
Reviewed-by: Jeff Layton <jlayton@samba.org>
Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org>
Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cifs/cifsglob.h | 3 | ||||
-rw-r--r-- | fs/cifs/connect.c | 161 | ||||
-rw-r--r-- | fs/cifs/smb1ops.c | 170 |
3 files changed, 175 insertions, 159 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 6df0cbe1cbc9..2aac4e5fb334 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -187,6 +187,9 @@ struct smb_version_operations { | |||
187 | /* verify the message */ | 187 | /* verify the message */ |
188 | int (*check_message)(char *, unsigned int); | 188 | int (*check_message)(char *, unsigned int); |
189 | bool (*is_oplock_break)(char *, struct TCP_Server_Info *); | 189 | bool (*is_oplock_break)(char *, struct TCP_Server_Info *); |
190 | /* process transaction2 response */ | ||
191 | bool (*check_trans2)(struct mid_q_entry *, struct TCP_Server_Info *, | ||
192 | char *, int); | ||
190 | }; | 193 | }; |
191 | 194 | ||
192 | struct smb_version_values { | 195 | struct smb_version_values { |
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index a675b7f47d63..6d846e7624d0 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -395,143 +395,6 @@ cifs_reconnect(struct TCP_Server_Info *server) | |||
395 | return rc; | 395 | return rc; |
396 | } | 396 | } |
397 | 397 | ||
398 | /* | ||
399 | return codes: | ||
400 | 0 not a transact2, or all data present | ||
401 | >0 transact2 with that much data missing | ||
402 | -EINVAL = invalid transact2 | ||
403 | |||
404 | */ | ||
405 | static int check2ndT2(char *buf) | ||
406 | { | ||
407 | struct smb_hdr *pSMB = (struct smb_hdr *)buf; | ||
408 | struct smb_t2_rsp *pSMBt; | ||
409 | int remaining; | ||
410 | __u16 total_data_size, data_in_this_rsp; | ||
411 | |||
412 | if (pSMB->Command != SMB_COM_TRANSACTION2) | ||
413 | return 0; | ||
414 | |||
415 | /* check for plausible wct, bcc and t2 data and parm sizes */ | ||
416 | /* check for parm and data offset going beyond end of smb */ | ||
417 | if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */ | ||
418 | cFYI(1, "invalid transact2 word count"); | ||
419 | return -EINVAL; | ||
420 | } | ||
421 | |||
422 | pSMBt = (struct smb_t2_rsp *)pSMB; | ||
423 | |||
424 | total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); | ||
425 | data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); | ||
426 | |||
427 | if (total_data_size == data_in_this_rsp) | ||
428 | return 0; | ||
429 | else if (total_data_size < data_in_this_rsp) { | ||
430 | cFYI(1, "total data %d smaller than data in frame %d", | ||
431 | total_data_size, data_in_this_rsp); | ||
432 | return -EINVAL; | ||
433 | } | ||
434 | |||
435 | remaining = total_data_size - data_in_this_rsp; | ||
436 | |||
437 | cFYI(1, "missing %d bytes from transact2, check next response", | ||
438 | remaining); | ||
439 | if (total_data_size > CIFSMaxBufSize) { | ||
440 | cERROR(1, "TotalDataSize %d is over maximum buffer %d", | ||
441 | total_data_size, CIFSMaxBufSize); | ||
442 | return -EINVAL; | ||
443 | } | ||
444 | return remaining; | ||
445 | } | ||
446 | |||
447 | static int coalesce_t2(char *second_buf, struct smb_hdr *target_hdr) | ||
448 | { | ||
449 | struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf; | ||
450 | struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr; | ||
451 | char *data_area_of_tgt; | ||
452 | char *data_area_of_src; | ||
453 | int remaining; | ||
454 | unsigned int byte_count, total_in_tgt; | ||
455 | __u16 tgt_total_cnt, src_total_cnt, total_in_src; | ||
456 | |||
457 | src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount); | ||
458 | tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); | ||
459 | |||
460 | if (tgt_total_cnt != src_total_cnt) | ||
461 | cFYI(1, "total data count of primary and secondary t2 differ " | ||
462 | "source=%hu target=%hu", src_total_cnt, tgt_total_cnt); | ||
463 | |||
464 | total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); | ||
465 | |||
466 | remaining = tgt_total_cnt - total_in_tgt; | ||
467 | |||
468 | if (remaining < 0) { | ||
469 | cFYI(1, "Server sent too much data. tgt_total_cnt=%hu " | ||
470 | "total_in_tgt=%hu", tgt_total_cnt, total_in_tgt); | ||
471 | return -EPROTO; | ||
472 | } | ||
473 | |||
474 | if (remaining == 0) { | ||
475 | /* nothing to do, ignore */ | ||
476 | cFYI(1, "no more data remains"); | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount); | ||
481 | if (remaining < total_in_src) | ||
482 | cFYI(1, "transact2 2nd response contains too much data"); | ||
483 | |||
484 | /* find end of first SMB data area */ | ||
485 | data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + | ||
486 | get_unaligned_le16(&pSMBt->t2_rsp.DataOffset); | ||
487 | |||
488 | /* validate target area */ | ||
489 | data_area_of_src = (char *)&pSMBs->hdr.Protocol + | ||
490 | get_unaligned_le16(&pSMBs->t2_rsp.DataOffset); | ||
491 | |||
492 | data_area_of_tgt += total_in_tgt; | ||
493 | |||
494 | total_in_tgt += total_in_src; | ||
495 | /* is the result too big for the field? */ | ||
496 | if (total_in_tgt > USHRT_MAX) { | ||
497 | cFYI(1, "coalesced DataCount too large (%u)", total_in_tgt); | ||
498 | return -EPROTO; | ||
499 | } | ||
500 | put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount); | ||
501 | |||
502 | /* fix up the BCC */ | ||
503 | byte_count = get_bcc(target_hdr); | ||
504 | byte_count += total_in_src; | ||
505 | /* is the result too big for the field? */ | ||
506 | if (byte_count > USHRT_MAX) { | ||
507 | cFYI(1, "coalesced BCC too large (%u)", byte_count); | ||
508 | return -EPROTO; | ||
509 | } | ||
510 | put_bcc(byte_count, target_hdr); | ||
511 | |||
512 | byte_count = be32_to_cpu(target_hdr->smb_buf_length); | ||
513 | byte_count += total_in_src; | ||
514 | /* don't allow buffer to overflow */ | ||
515 | if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { | ||
516 | cFYI(1, "coalesced BCC exceeds buffer size (%u)", byte_count); | ||
517 | return -ENOBUFS; | ||
518 | } | ||
519 | target_hdr->smb_buf_length = cpu_to_be32(byte_count); | ||
520 | |||
521 | /* copy second buffer into end of first buffer */ | ||
522 | memcpy(data_area_of_tgt, data_area_of_src, total_in_src); | ||
523 | |||
524 | if (remaining != total_in_src) { | ||
525 | /* more responses to go */ | ||
526 | cFYI(1, "waiting for more secondary responses"); | ||
527 | return 1; | ||
528 | } | ||
529 | |||
530 | /* we are done */ | ||
531 | cFYI(1, "found the last secondary response"); | ||
532 | return 0; | ||
533 | } | ||
534 | |||
535 | static void | 398 | static void |
536 | cifs_echo_request(struct work_struct *work) | 399 | cifs_echo_request(struct work_struct *work) |
537 | { | 400 | { |
@@ -804,29 +667,9 @@ static void | |||
804 | handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server, | 667 | handle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server, |
805 | char *buf, int malformed) | 668 | char *buf, int malformed) |
806 | { | 669 | { |
807 | if (malformed == 0 && check2ndT2(buf) > 0) { | 670 | if (server->ops->check_trans2 && |
808 | mid->multiRsp = true; | 671 | server->ops->check_trans2(mid, server, buf, malformed)) |
809 | if (mid->resp_buf) { | ||
810 | /* merge response - fix up 1st*/ | ||
811 | malformed = coalesce_t2(buf, mid->resp_buf); | ||
812 | if (malformed > 0) | ||
813 | return; | ||
814 | |||
815 | /* All parts received or packet is malformed. */ | ||
816 | mid->multiEnd = true; | ||
817 | return dequeue_mid(mid, malformed); | ||
818 | } | ||
819 | if (!server->large_buf) { | ||
820 | /*FIXME: switch to already allocated largebuf?*/ | ||
821 | cERROR(1, "1st trans2 resp needs bigbuf"); | ||
822 | } else { | ||
823 | /* Have first buffer */ | ||
824 | mid->resp_buf = buf; | ||
825 | mid->large_buf = true; | ||
826 | server->bigbuf = NULL; | ||
827 | } | ||
828 | return; | 672 | return; |
829 | } | ||
830 | mid->resp_buf = buf; | 673 | mid->resp_buf = buf; |
831 | mid->large_buf = server->large_buf; | 674 | mid->large_buf = server->large_buf; |
832 | /* Was previous buf put in mpx struct for multi-rsp? */ | 675 | /* Was previous buf put in mpx struct for multi-rsp? */ |
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 6dec38f5522d..28359e789fff 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c | |||
@@ -213,6 +213,175 @@ cifs_get_next_mid(struct TCP_Server_Info *server) | |||
213 | return mid; | 213 | return mid; |
214 | } | 214 | } |
215 | 215 | ||
216 | /* | ||
217 | return codes: | ||
218 | 0 not a transact2, or all data present | ||
219 | >0 transact2 with that much data missing | ||
220 | -EINVAL invalid transact2 | ||
221 | */ | ||
222 | static int | ||
223 | check2ndT2(char *buf) | ||
224 | { | ||
225 | struct smb_hdr *pSMB = (struct smb_hdr *)buf; | ||
226 | struct smb_t2_rsp *pSMBt; | ||
227 | int remaining; | ||
228 | __u16 total_data_size, data_in_this_rsp; | ||
229 | |||
230 | if (pSMB->Command != SMB_COM_TRANSACTION2) | ||
231 | return 0; | ||
232 | |||
233 | /* check for plausible wct, bcc and t2 data and parm sizes */ | ||
234 | /* check for parm and data offset going beyond end of smb */ | ||
235 | if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */ | ||
236 | cFYI(1, "invalid transact2 word count"); | ||
237 | return -EINVAL; | ||
238 | } | ||
239 | |||
240 | pSMBt = (struct smb_t2_rsp *)pSMB; | ||
241 | |||
242 | total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); | ||
243 | data_in_this_rsp = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); | ||
244 | |||
245 | if (total_data_size == data_in_this_rsp) | ||
246 | return 0; | ||
247 | else if (total_data_size < data_in_this_rsp) { | ||
248 | cFYI(1, "total data %d smaller than data in frame %d", | ||
249 | total_data_size, data_in_this_rsp); | ||
250 | return -EINVAL; | ||
251 | } | ||
252 | |||
253 | remaining = total_data_size - data_in_this_rsp; | ||
254 | |||
255 | cFYI(1, "missing %d bytes from transact2, check next response", | ||
256 | remaining); | ||
257 | if (total_data_size > CIFSMaxBufSize) { | ||
258 | cERROR(1, "TotalDataSize %d is over maximum buffer %d", | ||
259 | total_data_size, CIFSMaxBufSize); | ||
260 | return -EINVAL; | ||
261 | } | ||
262 | return remaining; | ||
263 | } | ||
264 | |||
265 | static int | ||
266 | coalesce_t2(char *second_buf, struct smb_hdr *target_hdr) | ||
267 | { | ||
268 | struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)second_buf; | ||
269 | struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)target_hdr; | ||
270 | char *data_area_of_tgt; | ||
271 | char *data_area_of_src; | ||
272 | int remaining; | ||
273 | unsigned int byte_count, total_in_tgt; | ||
274 | __u16 tgt_total_cnt, src_total_cnt, total_in_src; | ||
275 | |||
276 | src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount); | ||
277 | tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); | ||
278 | |||
279 | if (tgt_total_cnt != src_total_cnt) | ||
280 | cFYI(1, "total data count of primary and secondary t2 differ " | ||
281 | "source=%hu target=%hu", src_total_cnt, tgt_total_cnt); | ||
282 | |||
283 | total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); | ||
284 | |||
285 | remaining = tgt_total_cnt - total_in_tgt; | ||
286 | |||
287 | if (remaining < 0) { | ||
288 | cFYI(1, "Server sent too much data. tgt_total_cnt=%hu " | ||
289 | "total_in_tgt=%hu", tgt_total_cnt, total_in_tgt); | ||
290 | return -EPROTO; | ||
291 | } | ||
292 | |||
293 | if (remaining == 0) { | ||
294 | /* nothing to do, ignore */ | ||
295 | cFYI(1, "no more data remains"); | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount); | ||
300 | if (remaining < total_in_src) | ||
301 | cFYI(1, "transact2 2nd response contains too much data"); | ||
302 | |||
303 | /* find end of first SMB data area */ | ||
304 | data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + | ||
305 | get_unaligned_le16(&pSMBt->t2_rsp.DataOffset); | ||
306 | |||
307 | /* validate target area */ | ||
308 | data_area_of_src = (char *)&pSMBs->hdr.Protocol + | ||
309 | get_unaligned_le16(&pSMBs->t2_rsp.DataOffset); | ||
310 | |||
311 | data_area_of_tgt += total_in_tgt; | ||
312 | |||
313 | total_in_tgt += total_in_src; | ||
314 | /* is the result too big for the field? */ | ||
315 | if (total_in_tgt > USHRT_MAX) { | ||
316 | cFYI(1, "coalesced DataCount too large (%u)", total_in_tgt); | ||
317 | return -EPROTO; | ||
318 | } | ||
319 | put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount); | ||
320 | |||
321 | /* fix up the BCC */ | ||
322 | byte_count = get_bcc(target_hdr); | ||
323 | byte_count += total_in_src; | ||
324 | /* is the result too big for the field? */ | ||
325 | if (byte_count > USHRT_MAX) { | ||
326 | cFYI(1, "coalesced BCC too large (%u)", byte_count); | ||
327 | return -EPROTO; | ||
328 | } | ||
329 | put_bcc(byte_count, target_hdr); | ||
330 | |||
331 | byte_count = be32_to_cpu(target_hdr->smb_buf_length); | ||
332 | byte_count += total_in_src; | ||
333 | /* don't allow buffer to overflow */ | ||
334 | if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { | ||
335 | cFYI(1, "coalesced BCC exceeds buffer size (%u)", byte_count); | ||
336 | return -ENOBUFS; | ||
337 | } | ||
338 | target_hdr->smb_buf_length = cpu_to_be32(byte_count); | ||
339 | |||
340 | /* copy second buffer into end of first buffer */ | ||
341 | memcpy(data_area_of_tgt, data_area_of_src, total_in_src); | ||
342 | |||
343 | if (remaining != total_in_src) { | ||
344 | /* more responses to go */ | ||
345 | cFYI(1, "waiting for more secondary responses"); | ||
346 | return 1; | ||
347 | } | ||
348 | |||
349 | /* we are done */ | ||
350 | cFYI(1, "found the last secondary response"); | ||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | static bool | ||
355 | cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server, | ||
356 | char *buf, int malformed) | ||
357 | { | ||
358 | if (malformed) | ||
359 | return false; | ||
360 | if (check2ndT2(buf) <= 0) | ||
361 | return false; | ||
362 | mid->multiRsp = true; | ||
363 | if (mid->resp_buf) { | ||
364 | /* merge response - fix up 1st*/ | ||
365 | malformed = coalesce_t2(buf, mid->resp_buf); | ||
366 | if (malformed > 0) | ||
367 | return true; | ||
368 | /* All parts received or packet is malformed. */ | ||
369 | mid->multiEnd = true; | ||
370 | dequeue_mid(mid, malformed); | ||
371 | return true; | ||
372 | } | ||
373 | if (!server->large_buf) { | ||
374 | /*FIXME: switch to already allocated largebuf?*/ | ||
375 | cERROR(1, "1st trans2 resp needs bigbuf"); | ||
376 | } else { | ||
377 | /* Have first buffer */ | ||
378 | mid->resp_buf = buf; | ||
379 | mid->large_buf = true; | ||
380 | server->bigbuf = NULL; | ||
381 | } | ||
382 | return true; | ||
383 | } | ||
384 | |||
216 | struct smb_version_operations smb1_operations = { | 385 | struct smb_version_operations smb1_operations = { |
217 | .send_cancel = send_nt_cancel, | 386 | .send_cancel = send_nt_cancel, |
218 | .compare_fids = cifs_compare_fids, | 387 | .compare_fids = cifs_compare_fids, |
@@ -229,6 +398,7 @@ struct smb_version_operations smb1_operations = { | |||
229 | .check_message = checkSMB, | 398 | .check_message = checkSMB, |
230 | .dump_detail = cifs_dump_detail, | 399 | .dump_detail = cifs_dump_detail, |
231 | .is_oplock_break = is_valid_oplock_break, | 400 | .is_oplock_break = is_valid_oplock_break, |
401 | .check_trans2 = cifs_check_trans2, | ||
232 | }; | 402 | }; |
233 | 403 | ||
234 | struct smb_version_values smb1_values = { | 404 | struct smb_version_values smb1_values = { |