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/cifs/smb1ops.c | |
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/cifs/smb1ops.c')
-rw-r--r-- | fs/cifs/smb1ops.c | 170 |
1 files changed, 170 insertions, 0 deletions
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 = { |