blob: c288fbe7953d982e1980fc8dd6db9b7815044030 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * request.c
3 *
4 * Copyright (C) 2001 by Urban Widmark
5 *
6 * Please add a note about your changes to smbfs in the ChangeLog file.
7 */
8
Milind Arun Choudhary1525dcc2007-05-08 00:29:03 -07009#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070010#include <linux/types.h>
11#include <linux/fs.h>
12#include <linux/slab.h>
13#include <linux/net.h>
14
15#include <linux/smb_fs.h>
16#include <linux/smbno.h>
17#include <linux/smb_mount.h>
18
19#include "smb_debug.h"
20#include "request.h"
21#include "proto.h"
22
23/* #define SMB_SLAB_DEBUG (SLAB_RED_ZONE | SLAB_POISON) */
24#define SMB_SLAB_DEBUG 0
25
Linus Torvalds1da177e2005-04-16 15:20:36 -070026/* cache for request structures */
Christoph Lametere18b8902006-12-06 20:33:20 -080027static struct kmem_cache *req_cachep;
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29static int smb_request_send_req(struct smb_request *req);
30
31/*
32 /proc/slabinfo:
33 name, active, num, objsize, active_slabs, num_slaps, #pages
34*/
35
36
37int smb_init_request_cache(void)
38{
39 req_cachep = kmem_cache_create("smb_request",
40 sizeof(struct smb_request), 0,
41 SMB_SLAB_DEBUG | SLAB_HWCACHE_ALIGN,
42 NULL, NULL);
43 if (req_cachep == NULL)
44 return -ENOMEM;
45
46 return 0;
47}
48
49void smb_destroy_request_cache(void)
50{
Alexey Dobriyan1a1d92c2006-09-27 01:49:40 -070051 kmem_cache_destroy(req_cachep);
Linus Torvalds1da177e2005-04-16 15:20:36 -070052}
53
54/*
55 * Allocate and initialise a request structure
56 */
57static struct smb_request *smb_do_alloc_request(struct smb_sb_info *server,
58 int bufsize)
59{
60 struct smb_request *req;
61 unsigned char *buf = NULL;
62
Robert P. J. Dayc3762222007-02-10 01:45:03 -080063 req = kmem_cache_zalloc(req_cachep, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 VERBOSE("allocating request: %p\n", req);
65 if (!req)
66 goto out;
67
68 if (bufsize > 0) {
Pekka Enbergd0633892006-01-14 13:21:13 -080069 buf = kmalloc(bufsize, GFP_NOFS);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070 if (!buf) {
71 kmem_cache_free(req_cachep, req);
72 return NULL;
73 }
74 }
75
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 req->rq_buffer = buf;
77 req->rq_bufsize = bufsize;
78 req->rq_server = server;
79 init_waitqueue_head(&req->rq_wait);
80 INIT_LIST_HEAD(&req->rq_queue);
81 atomic_set(&req->rq_count, 1);
82
83out:
84 return req;
85}
86
87struct smb_request *smb_alloc_request(struct smb_sb_info *server, int bufsize)
88{
89 struct smb_request *req = NULL;
90
91 for (;;) {
92 atomic_inc(&server->nr_requests);
93 if (atomic_read(&server->nr_requests) <= MAX_REQUEST_HARD) {
94 req = smb_do_alloc_request(server, bufsize);
95 if (req != NULL)
96 break;
97 }
98
99#if 0
100 /*
101 * Try to free up at least one request in order to stay
102 * below the hard limit
103 */
104 if (nfs_try_to_free_pages(server))
105 continue;
106
107 if (signalled() && (server->flags & NFS_MOUNT_INTR))
108 return ERR_PTR(-ERESTARTSYS);
109 current->policy = SCHED_YIELD;
110 schedule();
111#else
112 /* FIXME: we want something like nfs does above, but that
113 requires changes to all callers and can wait. */
114 break;
115#endif
116 }
117 return req;
118}
119
120static void smb_free_request(struct smb_request *req)
121{
122 atomic_dec(&req->rq_server->nr_requests);
123 if (req->rq_buffer && !(req->rq_flags & SMB_REQ_STATIC))
Pekka Enbergd0633892006-01-14 13:21:13 -0800124 kfree(req->rq_buffer);
125 kfree(req->rq_trans2buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 kmem_cache_free(req_cachep, req);
127}
128
129/*
130 * What prevents a rget to race with a rput? The count must never drop to zero
131 * while it is in use. Only rput if it is ok that it is free'd.
132 */
133static void smb_rget(struct smb_request *req)
134{
135 atomic_inc(&req->rq_count);
136}
137void smb_rput(struct smb_request *req)
138{
139 if (atomic_dec_and_test(&req->rq_count)) {
140 list_del_init(&req->rq_queue);
141 smb_free_request(req);
142 }
143}
144
145/* setup to receive the data part of the SMB */
146static int smb_setup_bcc(struct smb_request *req)
147{
148 int result = 0;
149 req->rq_rlen = smb_len(req->rq_header) + 4 - req->rq_bytes_recvd;
150
151 if (req->rq_rlen > req->rq_bufsize) {
152 PARANOIA("Packet too large %d > %d\n",
153 req->rq_rlen, req->rq_bufsize);
154 return -ENOBUFS;
155 }
156
157 req->rq_iov[0].iov_base = req->rq_buffer;
158 req->rq_iov[0].iov_len = req->rq_rlen;
159 req->rq_iovlen = 1;
160
161 return result;
162}
163
164/*
165 * Prepare a "normal" request structure.
166 */
167static int smb_setup_request(struct smb_request *req)
168{
169 int len = smb_len(req->rq_header) + 4;
170 req->rq_slen = len;
171
172 /* if we expect a data part in the reply we set the iov's to read it */
173 if (req->rq_resp_bcc)
174 req->rq_setup_read = smb_setup_bcc;
175
176 /* This tries to support re-using the same request */
177 req->rq_bytes_sent = 0;
178 req->rq_rcls = 0;
179 req->rq_err = 0;
180 req->rq_errno = 0;
181 req->rq_fragment = 0;
Pekka Enbergd0633892006-01-14 13:21:13 -0800182 kfree(req->rq_trans2buffer);
Vasily Averin1174cf72007-03-16 13:38:24 -0800183 req->rq_trans2buffer = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184
185 return 0;
186}
187
188/*
189 * Prepare a transaction2 request structure
190 */
191static int smb_setup_trans2request(struct smb_request *req)
192{
193 struct smb_sb_info *server = req->rq_server;
194 int mparam, mdata;
195 static unsigned char padding[4];
196
197 /* I know the following is very ugly, but I want to build the
198 smb packet as efficiently as possible. */
199
200 const int smb_parameters = 15;
201 const int header = SMB_HEADER_LEN + 2 * smb_parameters + 2;
Milind Arun Choudhary1525dcc2007-05-08 00:29:03 -0700202 const int oparam = ALIGN(header + 3, sizeof(u32));
203 const int odata = ALIGN(oparam + req->rq_lparm, sizeof(u32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 const int bcc = (req->rq_data ? odata + req->rq_ldata :
205 oparam + req->rq_lparm) - header;
206
207 if ((bcc + oparam) > server->opt.max_xmit)
208 return -ENOMEM;
209 smb_setup_header(req, SMBtrans2, smb_parameters, bcc);
210
211 /*
212 * max parameters + max data + max setup == bufsize to make NT4 happy
213 * and not abort the transfer or split into multiple responses. It also
214 * makes smbfs happy as handling packets larger than the buffer size
215 * is extra work.
216 *
217 * OS/2 is probably going to hate me for this ...
218 */
219 mparam = SMB_TRANS2_MAX_PARAM;
220 mdata = req->rq_bufsize - mparam;
221
222 mdata = server->opt.max_xmit - mparam - 100;
223 if (mdata < 1024) {
224 mdata = 1024;
225 mparam = 20;
226 }
227
228#if 0
229 /* NT/win2k has ~4k max_xmit, so with this we request more than it wants
230 to return as one SMB. Useful for testing the fragmented trans2
231 handling. */
232 mdata = 8192;
233#endif
234
235 WSET(req->rq_header, smb_tpscnt, req->rq_lparm);
236 WSET(req->rq_header, smb_tdscnt, req->rq_ldata);
237 WSET(req->rq_header, smb_mprcnt, mparam);
238 WSET(req->rq_header, smb_mdrcnt, mdata);
239 WSET(req->rq_header, smb_msrcnt, 0); /* max setup always 0 ? */
240 WSET(req->rq_header, smb_flags, 0);
241 DSET(req->rq_header, smb_timeout, 0);
242 WSET(req->rq_header, smb_pscnt, req->rq_lparm);
243 WSET(req->rq_header, smb_psoff, oparam - 4);
244 WSET(req->rq_header, smb_dscnt, req->rq_ldata);
245 WSET(req->rq_header, smb_dsoff, req->rq_data ? odata - 4 : 0);
246 *(req->rq_header + smb_suwcnt) = 0x01; /* setup count */
247 *(req->rq_header + smb_suwcnt + 1) = 0x00; /* reserved */
248 WSET(req->rq_header, smb_setup0, req->rq_trans2_command);
249
250 req->rq_iovlen = 2;
251 req->rq_iov[0].iov_base = (void *) req->rq_header;
252 req->rq_iov[0].iov_len = oparam;
253 req->rq_iov[1].iov_base = (req->rq_parm==NULL) ? padding : req->rq_parm;
254 req->rq_iov[1].iov_len = req->rq_lparm;
255 req->rq_slen = oparam + req->rq_lparm;
256
257 if (req->rq_data) {
258 req->rq_iovlen += 2;
259 req->rq_iov[2].iov_base = padding;
260 req->rq_iov[2].iov_len = odata - oparam - req->rq_lparm;
261 req->rq_iov[3].iov_base = req->rq_data;
262 req->rq_iov[3].iov_len = req->rq_ldata;
263 req->rq_slen = odata + req->rq_ldata;
264 }
265
266 /* always a data part for trans2 replies */
267 req->rq_setup_read = smb_setup_bcc;
268
269 return 0;
270}
271
272/*
273 * Add a request and tell smbiod to process it
274 */
275int smb_add_request(struct smb_request *req)
276{
277 long timeleft;
278 struct smb_sb_info *server = req->rq_server;
279 int result = 0;
280
281 smb_setup_request(req);
282 if (req->rq_trans2_command) {
283 if (req->rq_buffer == NULL) {
284 PARANOIA("trans2 attempted without response buffer!\n");
285 return -EIO;
286 }
287 result = smb_setup_trans2request(req);
288 }
289 if (result < 0)
290 return result;
291
292#ifdef SMB_DEBUG_PACKET_SIZE
293 add_xmit_stats(req);
294#endif
295
296 /* add 'req' to the queue of requests */
297 if (smb_lock_server_interruptible(server))
298 return -EINTR;
299
300 /*
301 * Try to send the request as the process. If that fails we queue the
302 * request and let smbiod send it later.
303 */
304
305 /* FIXME: each server has a number on the maximum number of parallel
306 requests. 10, 50 or so. We should not allow more requests to be
307 active. */
308 if (server->mid > 0xf000)
309 server->mid = 0;
310 req->rq_mid = server->mid++;
311 WSET(req->rq_header, smb_mid, req->rq_mid);
312
313 result = 0;
314 if (server->state == CONN_VALID) {
315 if (list_empty(&server->xmitq))
316 result = smb_request_send_req(req);
317 if (result < 0) {
318 /* Connection lost? */
319 server->conn_error = result;
320 server->state = CONN_INVALID;
321 }
322 }
323 if (result != 1)
324 list_add_tail(&req->rq_queue, &server->xmitq);
325 smb_rget(req);
326
327 if (server->state != CONN_VALID)
328 smbiod_retry(server);
329
330 smb_unlock_server(server);
331
332 smbiod_wake_up();
333
334 timeleft = wait_event_interruptible_timeout(req->rq_wait,
335 req->rq_flags & SMB_REQ_RECEIVED, 30*HZ);
336 if (!timeleft || signal_pending(current)) {
337 /*
338 * On timeout or on interrupt we want to try and remove the
339 * request from the recvq/xmitq.
Jan Niehusmann48564e62006-05-15 09:44:12 -0700340 * First check if the request is still part of a queue. (May
341 * have been removed by some error condition)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 */
343 smb_lock_server(server);
Jan Niehusmann48564e62006-05-15 09:44:12 -0700344 if (!list_empty(&req->rq_queue)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 list_del_init(&req->rq_queue);
346 smb_rput(req);
347 }
348 smb_unlock_server(server);
349 }
350
351 if (!timeleft) {
352 PARANOIA("request [%p, mid=%d] timed out!\n",
353 req, req->rq_mid);
354 VERBOSE("smb_com: %02x\n", *(req->rq_header + smb_com));
355 VERBOSE("smb_rcls: %02x\n", *(req->rq_header + smb_rcls));
356 VERBOSE("smb_flg: %02x\n", *(req->rq_header + smb_flg));
357 VERBOSE("smb_tid: %04x\n", WVAL(req->rq_header, smb_tid));
358 VERBOSE("smb_pid: %04x\n", WVAL(req->rq_header, smb_pid));
359 VERBOSE("smb_uid: %04x\n", WVAL(req->rq_header, smb_uid));
360 VERBOSE("smb_mid: %04x\n", WVAL(req->rq_header, smb_mid));
361 VERBOSE("smb_wct: %02x\n", *(req->rq_header + smb_wct));
362
363 req->rq_rcls = ERRSRV;
364 req->rq_err = ERRtimeout;
365
366 /* Just in case it was "stuck" */
367 smbiod_wake_up();
368 }
369 VERBOSE("woke up, rcls=%d\n", req->rq_rcls);
370
371 if (req->rq_rcls != 0)
372 req->rq_errno = smb_errno(req);
373 if (signal_pending(current))
374 req->rq_errno = -ERESTARTSYS;
375 return req->rq_errno;
376}
377
378/*
379 * Send a request and place it on the recvq if successfully sent.
380 * Must be called with the server lock held.
381 */
382static int smb_request_send_req(struct smb_request *req)
383{
384 struct smb_sb_info *server = req->rq_server;
385 int result;
386
387 if (req->rq_bytes_sent == 0) {
388 WSET(req->rq_header, smb_tid, server->opt.tid);
389 WSET(req->rq_header, smb_pid, 1);
390 WSET(req->rq_header, smb_uid, server->opt.server_uid);
391 }
392
393 result = smb_send_request(req);
394 if (result < 0 && result != -EAGAIN)
395 goto out;
396
397 result = 0;
398 if (!(req->rq_flags & SMB_REQ_TRANSMITTED))
399 goto out;
400
Akinobu Mitaf1166292006-06-26 00:24:46 -0700401 list_move_tail(&req->rq_queue, &server->recvq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 result = 1;
403out:
404 return result;
405}
406
407/*
408 * Sends one request for this server. (smbiod)
409 * Must be called with the server lock held.
410 * Returns: <0 on error
411 * 0 if no request could be completely sent
412 * 1 if all data for one request was sent
413 */
414int smb_request_send_server(struct smb_sb_info *server)
415{
416 struct list_head *head;
417 struct smb_request *req;
418 int result;
419
420 if (server->state != CONN_VALID)
421 return 0;
422
423 /* dequeue first request, if any */
424 req = NULL;
425 head = server->xmitq.next;
426 if (head != &server->xmitq) {
427 req = list_entry(head, struct smb_request, rq_queue);
428 }
429 if (!req)
430 return 0;
431
432 result = smb_request_send_req(req);
433 if (result < 0) {
434 server->conn_error = result;
Akinobu Mitaf1166292006-06-26 00:24:46 -0700435 list_move(&req->rq_queue, &server->xmitq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 result = -EIO;
437 goto out;
438 }
439
440out:
441 return result;
442}
443
444/*
445 * Try to find a request matching this "mid". Typically the first entry will
446 * be the matching one.
447 */
448static struct smb_request *find_request(struct smb_sb_info *server, int mid)
449{
450 struct list_head *tmp;
451 struct smb_request *req = NULL;
452
453 list_for_each(tmp, &server->recvq) {
454 req = list_entry(tmp, struct smb_request, rq_queue);
455 if (req->rq_mid == mid) {
456 break;
457 }
458 req = NULL;
459 }
460
461 if (!req) {
462 VERBOSE("received reply with mid %d but no request!\n",
463 WVAL(server->header, smb_mid));
464 server->rstate = SMB_RECV_DROP;
465 }
466
467 return req;
468}
469
470/*
471 * Called when we have read the smb header and believe this is a response.
472 */
473static int smb_init_request(struct smb_sb_info *server, struct smb_request *req)
474{
475 int hdrlen, wct;
476
477 memcpy(req->rq_header, server->header, SMB_HEADER_LEN);
478
479 wct = *(req->rq_header + smb_wct);
480 if (wct > 20) {
481 PARANOIA("wct too large, %d > 20\n", wct);
482 server->rstate = SMB_RECV_DROP;
483 return 0;
484 }
485
486 req->rq_resp_wct = wct;
487 hdrlen = SMB_HEADER_LEN + wct*2 + 2;
488 VERBOSE("header length: %d smb_wct: %2d\n", hdrlen, wct);
489
490 req->rq_bytes_recvd = SMB_HEADER_LEN;
491 req->rq_rlen = hdrlen;
492 req->rq_iov[0].iov_base = req->rq_header;
493 req->rq_iov[0].iov_len = hdrlen;
494 req->rq_iovlen = 1;
495 server->rstate = SMB_RECV_PARAM;
496
497#ifdef SMB_DEBUG_PACKET_SIZE
498 add_recv_stats(smb_len(server->header));
499#endif
500 return 0;
501}
502
503/*
504 * Reads the SMB parameters
505 */
506static int smb_recv_param(struct smb_sb_info *server, struct smb_request *req)
507{
508 int result;
509
510 result = smb_receive(server, req);
511 if (result < 0)
512 return result;
513 if (req->rq_bytes_recvd < req->rq_rlen)
514 return 0;
515
516 VERBOSE("result: %d smb_bcc: %04x\n", result,
517 WVAL(req->rq_header, SMB_HEADER_LEN +
518 (*(req->rq_header + smb_wct) * 2)));
519
520 result = 0;
521 req->rq_iov[0].iov_base = NULL;
522 req->rq_rlen = 0;
523 if (req->rq_callback)
524 req->rq_callback(req);
525 else if (req->rq_setup_read)
526 result = req->rq_setup_read(req);
527 if (result < 0) {
528 server->rstate = SMB_RECV_DROP;
529 return result;
530 }
531
532 server->rstate = req->rq_rlen > 0 ? SMB_RECV_DATA : SMB_RECV_END;
533
534 req->rq_bytes_recvd = 0; // recvd out of the iov
535
536 VERBOSE("rlen: %d\n", req->rq_rlen);
537 if (req->rq_rlen < 0) {
538 PARANOIA("Parameters read beyond end of packet!\n");
539 server->rstate = SMB_RECV_END;
540 return -EIO;
541 }
542 return 0;
543}
544
545/*
546 * Reads the SMB data
547 */
548static int smb_recv_data(struct smb_sb_info *server, struct smb_request *req)
549{
550 int result;
551
552 result = smb_receive(server, req);
553 if (result < 0)
554 goto out;
555 if (req->rq_bytes_recvd < req->rq_rlen)
556 goto out;
557 server->rstate = SMB_RECV_END;
558out:
559 VERBOSE("result: %d\n", result);
560 return result;
561}
562
563/*
564 * Receive a transaction2 response
565 * Return: 0 if the response has been fully read
566 * 1 if there are further "fragments" to read
567 * <0 if there is an error
568 */
569static int smb_recv_trans2(struct smb_sb_info *server, struct smb_request *req)
570{
571 unsigned char *inbuf;
572 unsigned int parm_disp, parm_offset, parm_count, parm_tot;
573 unsigned int data_disp, data_offset, data_count, data_tot;
574 int hdrlen = SMB_HEADER_LEN + req->rq_resp_wct*2 - 2;
575
576 VERBOSE("handling trans2\n");
577
578 inbuf = req->rq_header;
579 data_tot = WVAL(inbuf, smb_tdrcnt);
580 parm_tot = WVAL(inbuf, smb_tprcnt);
581 parm_disp = WVAL(inbuf, smb_prdisp);
582 parm_offset = WVAL(inbuf, smb_proff);
583 parm_count = WVAL(inbuf, smb_prcnt);
584 data_disp = WVAL(inbuf, smb_drdisp);
585 data_offset = WVAL(inbuf, smb_droff);
586 data_count = WVAL(inbuf, smb_drcnt);
587
588 /* Modify offset for the split header/buffer we use */
589 if (data_count || data_offset) {
590 if (unlikely(data_offset < hdrlen))
591 goto out_bad_data;
592 else
593 data_offset -= hdrlen;
594 }
595 if (parm_count || parm_offset) {
596 if (unlikely(parm_offset < hdrlen))
597 goto out_bad_parm;
598 else
599 parm_offset -= hdrlen;
600 }
601
602 if (parm_count == parm_tot && data_count == data_tot) {
603 /*
604 * This packet has all the trans2 data.
605 *
606 * We setup the request so that this will be the common
607 * case. It may be a server error to not return a
608 * response that fits.
609 */
610 VERBOSE("single trans2 response "
611 "dcnt=%u, pcnt=%u, doff=%u, poff=%u\n",
612 data_count, parm_count,
613 data_offset, parm_offset);
614 req->rq_ldata = data_count;
615 req->rq_lparm = parm_count;
616 req->rq_data = req->rq_buffer + data_offset;
617 req->rq_parm = req->rq_buffer + parm_offset;
618 if (unlikely(parm_offset + parm_count > req->rq_rlen))
619 goto out_bad_parm;
620 if (unlikely(data_offset + data_count > req->rq_rlen))
621 goto out_bad_data;
622 return 0;
623 }
624
625 VERBOSE("multi trans2 response "
626 "frag=%d, dcnt=%u, pcnt=%u, doff=%u, poff=%u\n",
627 req->rq_fragment,
628 data_count, parm_count,
629 data_offset, parm_offset);
630
631 if (!req->rq_fragment) {
632 int buf_len;
633
634 /* We got the first trans2 fragment */
635 req->rq_fragment = 1;
636 req->rq_total_data = data_tot;
637 req->rq_total_parm = parm_tot;
638 req->rq_ldata = 0;
639 req->rq_lparm = 0;
640
641 buf_len = data_tot + parm_tot;
642 if (buf_len > SMB_MAX_PACKET_SIZE)
643 goto out_too_long;
644
645 req->rq_trans2bufsize = buf_len;
Pekka Enbergd0633892006-01-14 13:21:13 -0800646 req->rq_trans2buffer = kzalloc(buf_len, GFP_NOFS);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 if (!req->rq_trans2buffer)
648 goto out_no_mem;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649
650 req->rq_parm = req->rq_trans2buffer;
651 req->rq_data = req->rq_trans2buffer + parm_tot;
652 } else if (unlikely(req->rq_total_data < data_tot ||
653 req->rq_total_parm < parm_tot))
654 goto out_data_grew;
655
656 if (unlikely(parm_disp + parm_count > req->rq_total_parm ||
657 parm_offset + parm_count > req->rq_rlen))
658 goto out_bad_parm;
659 if (unlikely(data_disp + data_count > req->rq_total_data ||
660 data_offset + data_count > req->rq_rlen))
661 goto out_bad_data;
662
663 inbuf = req->rq_buffer;
664 memcpy(req->rq_parm + parm_disp, inbuf + parm_offset, parm_count);
665 memcpy(req->rq_data + data_disp, inbuf + data_offset, data_count);
666
667 req->rq_ldata += data_count;
668 req->rq_lparm += parm_count;
669
670 /*
671 * Check whether we've received all of the data. Note that
672 * we use the packet totals -- total lengths might shrink!
673 */
674 if (req->rq_ldata >= data_tot && req->rq_lparm >= parm_tot) {
675 req->rq_ldata = data_tot;
676 req->rq_lparm = parm_tot;
677 return 0;
678 }
679 return 1;
680
681out_too_long:
682 printk(KERN_ERR "smb_trans2: data/param too long, data=%u, parm=%u\n",
683 data_tot, parm_tot);
684 goto out_EIO;
685out_no_mem:
686 printk(KERN_ERR "smb_trans2: couldn't allocate data area of %d bytes\n",
687 req->rq_trans2bufsize);
688 req->rq_errno = -ENOMEM;
689 goto out;
690out_data_grew:
691 printk(KERN_ERR "smb_trans2: data/params grew!\n");
692 goto out_EIO;
693out_bad_parm:
694 printk(KERN_ERR "smb_trans2: invalid parms, disp=%u, cnt=%u, tot=%u, ofs=%u\n",
695 parm_disp, parm_count, parm_tot, parm_offset);
696 goto out_EIO;
697out_bad_data:
698 printk(KERN_ERR "smb_trans2: invalid data, disp=%u, cnt=%u, tot=%u, ofs=%u\n",
699 data_disp, data_count, data_tot, data_offset);
700out_EIO:
701 req->rq_errno = -EIO;
702out:
703 return req->rq_errno;
704}
705
706/*
707 * State machine for receiving responses. We handle the fact that we can't
708 * read the full response in one try by having states telling us how much we
709 * have read.
710 *
711 * Must be called with the server lock held (only called from smbiod).
712 *
713 * Return: <0 on error
714 */
715int smb_request_recv(struct smb_sb_info *server)
716{
717 struct smb_request *req = NULL;
718 int result = 0;
719
720 if (smb_recv_available(server) <= 0)
721 return 0;
722
723 VERBOSE("state: %d\n", server->rstate);
724 switch (server->rstate) {
725 case SMB_RECV_DROP:
726 result = smb_receive_drop(server);
727 if (result < 0)
728 break;
729 if (server->rstate == SMB_RECV_DROP)
730 break;
731 server->rstate = SMB_RECV_START;
732 /* fallthrough */
733 case SMB_RECV_START:
734 server->smb_read = 0;
735 server->rstate = SMB_RECV_HEADER;
736 /* fallthrough */
737 case SMB_RECV_HEADER:
738 result = smb_receive_header(server);
739 if (result < 0)
740 break;
741 if (server->rstate == SMB_RECV_HEADER)
742 break;
743 if (! (*(server->header + smb_flg) & SMB_FLAGS_REPLY) ) {
744 server->rstate = SMB_RECV_REQUEST;
745 break;
746 }
747 if (server->rstate != SMB_RECV_HCOMPLETE)
748 break;
749 /* fallthrough */
750 case SMB_RECV_HCOMPLETE:
751 req = find_request(server, WVAL(server->header, smb_mid));
752 if (!req)
753 break;
754 smb_init_request(server, req);
755 req->rq_rcls = *(req->rq_header + smb_rcls);
756 req->rq_err = WVAL(req->rq_header, smb_err);
757 if (server->rstate != SMB_RECV_PARAM)
758 break;
759 /* fallthrough */
760 case SMB_RECV_PARAM:
761 if (!req)
762 req = find_request(server,WVAL(server->header,smb_mid));
763 if (!req)
764 break;
765 result = smb_recv_param(server, req);
766 if (result < 0)
767 break;
768 if (server->rstate != SMB_RECV_DATA)
769 break;
770 /* fallthrough */
771 case SMB_RECV_DATA:
772 if (!req)
773 req = find_request(server,WVAL(server->header,smb_mid));
774 if (!req)
775 break;
776 result = smb_recv_data(server, req);
777 if (result < 0)
778 break;
779 break;
780
781 /* We should never be called with any of these states */
782 case SMB_RECV_END:
783 case SMB_RECV_REQUEST:
Adrian Bunkccb6e362005-11-07 00:59:23 -0800784 BUG();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 }
786
787 if (result < 0) {
788 /* We saw an error */
789 return result;
790 }
791
792 if (server->rstate != SMB_RECV_END)
793 return 0;
794
795 result = 0;
796 if (req->rq_trans2_command && req->rq_rcls == SUCCESS)
797 result = smb_recv_trans2(server, req);
798
799 /*
800 * Response completely read. Drop any extra bytes sent by the server.
801 * (Yes, servers sometimes add extra bytes to responses)
802 */
803 VERBOSE("smb_len: %d smb_read: %d\n",
804 server->smb_len, server->smb_read);
805 if (server->smb_read < server->smb_len)
806 smb_receive_drop(server);
807
808 server->rstate = SMB_RECV_START;
809
810 if (!result) {
811 list_del_init(&req->rq_queue);
812 req->rq_flags |= SMB_REQ_RECEIVED;
813 smb_rput(req);
814 wake_up_interruptible(&req->rq_wait);
815 }
816 return 0;
817}