blob: 04f4af07fdd40a6344793430918e32086fd98727 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * fs/cifs/transport.c
3 *
Steve Frenchb8643e12005-04-28 22:41:07 -07004 * Copyright (C) International Business Machines Corp., 2002,2005
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Author(s): Steve French (sfrench@us.ibm.com)
6 *
7 * This library is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
15 * the GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <linux/fs.h>
23#include <linux/list.h>
24#include <linux/wait.h>
25#include <linux/net.h>
26#include <linux/delay.h>
27#include <asm/uaccess.h>
28#include <asm/processor.h>
29#include <linux/mempool.h>
30#include "cifspdu.h"
31#include "cifsglob.h"
32#include "cifsproto.h"
33#include "cifs_debug.h"
34
35extern mempool_t *cifs_mid_poolp;
36extern kmem_cache_t *cifs_oplock_cachep;
37
38static struct mid_q_entry *
39AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
40{
41 struct mid_q_entry *temp;
42
43 if (ses == NULL) {
Steve French275cde12005-04-28 22:41:10 -070044 cERROR(1, ("Null session passed in to AllocMidQEntry"));
Linus Torvalds1da177e2005-04-16 15:20:36 -070045 return NULL;
46 }
47 if (ses->server == NULL) {
48 cERROR(1, ("Null TCP session in AllocMidQEntry"));
49 return NULL;
50 }
51
Steve Frenchd6e04ae2005-06-13 13:24:43 -050052 temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp,
53 SLAB_KERNEL | SLAB_NOFS);
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 if (temp == NULL)
55 return temp;
56 else {
57 memset(temp, 0, sizeof (struct mid_q_entry));
58 temp->mid = smb_buffer->Mid; /* always LE */
59 temp->pid = current->pid;
60 temp->command = smb_buffer->Command;
61 cFYI(1, ("For smb_command %d", temp->command));
62 do_gettimeofday(&temp->when_sent);
63 temp->ses = ses;
64 temp->tsk = current;
65 }
66
67 spin_lock(&GlobalMid_Lock);
68 list_add_tail(&temp->qhead, &ses->server->pending_mid_q);
69 atomic_inc(&midCount);
70 temp->midState = MID_REQUEST_ALLOCATED;
71 spin_unlock(&GlobalMid_Lock);
72 return temp;
73}
74
75static void
76DeleteMidQEntry(struct mid_q_entry *midEntry)
77{
78 spin_lock(&GlobalMid_Lock);
79 midEntry->midState = MID_FREE;
80 list_del(&midEntry->qhead);
81 atomic_dec(&midCount);
82 spin_unlock(&GlobalMid_Lock);
Steve Frenchb8643e12005-04-28 22:41:07 -070083 if(midEntry->largeBuf)
84 cifs_buf_release(midEntry->resp_buf);
85 else
86 cifs_small_buf_release(midEntry->resp_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 mempool_free(midEntry, cifs_mid_poolp);
88}
89
90struct oplock_q_entry *
91AllocOplockQEntry(struct inode * pinode, __u16 fid, struct cifsTconInfo * tcon)
92{
93 struct oplock_q_entry *temp;
94 if ((pinode== NULL) || (tcon == NULL)) {
95 cERROR(1, ("Null parms passed to AllocOplockQEntry"));
96 return NULL;
97 }
98 temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep,
99 SLAB_KERNEL);
100 if (temp == NULL)
101 return temp;
102 else {
103 temp->pinode = pinode;
104 temp->tcon = tcon;
105 temp->netfid = fid;
106 spin_lock(&GlobalMid_Lock);
107 list_add_tail(&temp->qhead, &GlobalOplock_Q);
108 spin_unlock(&GlobalMid_Lock);
109 }
110 return temp;
111
112}
113
114void DeleteOplockQEntry(struct oplock_q_entry * oplockEntry)
115{
116 spin_lock(&GlobalMid_Lock);
117 /* should we check if list empty first? */
118 list_del(&oplockEntry->qhead);
119 spin_unlock(&GlobalMid_Lock);
120 kmem_cache_free(cifs_oplock_cachep, oplockEntry);
121}
122
123int
124smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
125 unsigned int smb_buf_length, struct sockaddr *sin)
126{
127 int rc = 0;
128 int i = 0;
129 struct msghdr smb_msg;
130 struct kvec iov;
131 unsigned len = smb_buf_length + 4;
132
133 if(ssocket == NULL)
134 return -ENOTSOCK; /* BB eventually add reconnect code here */
135 iov.iov_base = smb_buffer;
136 iov.iov_len = len;
137
138 smb_msg.msg_name = sin;
139 smb_msg.msg_namelen = sizeof (struct sockaddr);
140 smb_msg.msg_control = NULL;
141 smb_msg.msg_controllen = 0;
142 smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
143
144 /* smb header is converted in header_assemble. bcc and rest of SMB word
145 area, and byte area if necessary, is converted to littleendian in
146 cifssmb.c and RFC1001 len is converted to bigendian in smb_send
147 Flags2 is converted in SendReceive */
148
149 smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
150 cFYI(1, ("Sending smb of length %d ", smb_buf_length));
151 dump_smb(smb_buffer, len);
152
153 while (len > 0) {
154 rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len);
155 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
156 i++;
157 if(i > 60) {
158 cERROR(1,
159 ("sends on sock %p stuck for 30 seconds",
160 ssocket));
161 rc = -EAGAIN;
162 break;
163 }
164 msleep(500);
165 continue;
166 }
167 if (rc < 0)
168 break;
169 iov.iov_base += rc;
170 iov.iov_len -= rc;
171 len -= rc;
172 }
173
174 if (rc < 0) {
175 cERROR(1,("Error %d sending data on socket to server.", rc));
176 } else {
177 rc = 0;
178 }
179
180 return rc;
181}
182
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500183#ifdef CONFIG_CIFS_EXPERIMENTAL
184static int
185smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer,
186 unsigned int smb_hdr_length, const char * data, unsigned int datalen,
187 struct sockaddr *sin)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188{
189 int rc = 0;
190 int i = 0;
191 struct msghdr smb_msg;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500192 struct kvec iov[2];
193 unsigned len = smb_hdr_length + 4;
194
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 if(ssocket == NULL)
196 return -ENOTSOCK; /* BB eventually add reconnect code here */
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500197 iov[0].iov_base = smb_buffer;
198 iov[0].iov_len = len;
199 iov[1].iov_base = data;
200 iov[2].iov_len = datalen;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 smb_msg.msg_name = sin;
202 smb_msg.msg_namelen = sizeof (struct sockaddr);
203 smb_msg.msg_control = NULL;
204 smb_msg.msg_controllen = 0;
205 smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
206
207 /* smb header is converted in header_assemble. bcc and rest of SMB word
208 area, and byte area if necessary, is converted to littleendian in
209 cifssmb.c and RFC1001 len is converted to bigendian in smb_send
210 Flags2 is converted in SendReceive */
211
212 smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500213 cFYI(1, ("Sending smb of length %d ", len + datalen));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 dump_smb(smb_buffer, len);
215
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500216 while (len + datalen > 0) {
217 rc = kernel_sendmsg(ssocket, &smb_msg, iov, 2, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
219 i++;
220 if(i > 60) {
221 cERROR(1,
222 ("sends on sock %p stuck for 30 seconds",
223 ssocket));
224 rc = -EAGAIN;
225 break;
226 }
227 msleep(500);
228 continue;
229 }
230 if (rc < 0)
231 break;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500232 if(iov[0].iov_len > 0) {
233 if(rc >= len) {
234 iov[0].iov_len = 0;
235 rc -= len;
236 } else { /* some of hdr was not sent */
237 len -= rc;
238 iov[0].iov_len -= rc;
239 iov[0].iov_base += rc;
240 continue;
241 }
242 }
243 if((iov[0].iov_len == 0) && (rc > 0)){
244 iov[1].iov_base += rc;
245 iov[1].iov_len -= rc;
246 datalen -= rc;
247 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 }
249
250 if (rc < 0) {
251 cERROR(1,("Error %d sending data on socket to server.", rc));
252 } else {
253 rc = 0;
254 }
255
256 return rc;
257}
258
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259int
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500260SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
261 struct smb_hdr *in_buf, int hdrlen, const char * data,
262 int datalen, int *pbytes_returned, const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263{
264 int rc = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500265 unsigned int receive_len;
266 unsigned long timeout;
267 struct mid_q_entry *midQ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268
269 if (ses == NULL) {
270 cERROR(1,("Null smb session"));
271 return -EIO;
272 }
273 if(ses->server == NULL) {
274 cERROR(1,("Null tcp session"));
275 return -EIO;
276 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500278 if(ses->server->tcpStatus == CifsExiting)
Steve French31ca3bc2005-04-28 22:41:11 -0700279 return -ENOENT;
280
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 /* Ensure that we do not send more than 50 overlapping requests
282 to the same server. We may make this configurable later or
283 use ses->maxReq */
284 if(long_op == -1) {
285 /* oplock breaks must not be held up */
286 atomic_inc(&ses->server->inFlight);
287 } else {
288 spin_lock(&GlobalMid_Lock);
289 while(1) {
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500290 if(atomic_read(&ses->server->inFlight) >=
291 cifs_max_pending){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 spin_unlock(&GlobalMid_Lock);
293 wait_event(ses->server->request_q,
294 atomic_read(&ses->server->inFlight)
295 < cifs_max_pending);
296 spin_lock(&GlobalMid_Lock);
297 } else {
298 if(ses->server->tcpStatus == CifsExiting) {
299 spin_unlock(&GlobalMid_Lock);
300 return -ENOENT;
301 }
302
303 /* can not count locking commands against total since
304 they are allowed to block on server */
305
306 if(long_op < 3) {
307 /* update # of requests on the wire to server */
308 atomic_inc(&ses->server->inFlight);
309 }
310 spin_unlock(&GlobalMid_Lock);
311 break;
312 }
313 }
314 }
315 /* make sure that we sign in the same order that we send on this socket
316 and avoid races inside tcp sendmsg code that could cause corruption
317 of smb data */
318
319 down(&ses->server->tcpSem);
320
321 if (ses->server->tcpStatus == CifsExiting) {
322 rc = -ENOENT;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500323 goto out_unlock2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 } else if (ses->server->tcpStatus == CifsNeedReconnect) {
325 cFYI(1,("tcp session dead - return to caller to retry"));
326 rc = -EAGAIN;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500327 goto out_unlock2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 } else if (ses->status != CifsGood) {
329 /* check if SMB session is bad because we are setting it up */
330 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
331 (in_buf->Command != SMB_COM_NEGOTIATE)) {
332 rc = -EAGAIN;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500333 goto out_unlock2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 } /* else ok - we are setting up session */
335 }
336 midQ = AllocMidQEntry(in_buf, ses);
337 if (midQ == NULL) {
338 up(&ses->server->tcpSem);
339 /* If not lock req, update # of requests on wire to server */
340 if(long_op < 3) {
341 atomic_dec(&ses->server->inFlight);
342 wake_up(&ses->server->request_q);
343 }
344 return -ENOMEM;
345 }
346
347 if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
348 up(&ses->server->tcpSem);
349 cERROR(1,
350 ("Illegal length, greater than maximum frame, %d ",
351 in_buf->smb_buf_length));
352 DeleteMidQEntry(midQ);
353 /* If not lock req, update # of requests on wire to server */
354 if(long_op < 3) {
355 atomic_dec(&ses->server->inFlight);
356 wake_up(&ses->server->request_q);
357 }
358 return -EIO;
359 }
360
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500361/* BB FIXME */
362/* rc = cifs_sign_smb2(in_buf, data, ses->server, &midQ->sequence_number); */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363
364 midQ->midState = MID_REQUEST_SUBMITTED;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500365 rc = smb_send2(ses->server->ssocket, in_buf, hdrlen, data, datalen,
366 (struct sockaddr *) &(ses->server->addr.sockAddr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 if(rc < 0) {
368 DeleteMidQEntry(midQ);
369 up(&ses->server->tcpSem);
370 /* If not lock req, update # of requests on wire to server */
371 if(long_op < 3) {
372 atomic_dec(&ses->server->inFlight);
373 wake_up(&ses->server->request_q);
374 }
375 return rc;
376 } else
377 up(&ses->server->tcpSem);
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500378 if (long_op == -1)
379 goto cifs_no_response_exit2;
380 else if (long_op == 2) /* writes past end of file can take loong time */
381 timeout = 300 * HZ;
382 else if (long_op == 1)
383 timeout = 45 * HZ; /* should be greater than
384 servers oplock break timeout (about 43 seconds) */
385 else if (long_op > 2) {
386 timeout = MAX_SCHEDULE_TIMEOUT;
387 } else
388 timeout = 15 * HZ;
389 /* wait for 15 seconds or until woken up due to response arriving or
390 due to last connection to this server being unmounted */
391 if (signal_pending(current)) {
392 /* if signal pending do not hold up user for full smb timeout
393 but we still give response a change to complete */
394 timeout = 2 * HZ;
395 }
396
397 /* No user interrupts in wait - wreaks havoc with performance */
398 if(timeout != MAX_SCHEDULE_TIMEOUT) {
399 timeout += jiffies;
400 wait_event(ses->server->response_q,
401 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
402 time_after(jiffies, timeout) ||
403 ((ses->server->tcpStatus != CifsGood) &&
404 (ses->server->tcpStatus != CifsNew)));
405 } else {
406 wait_event(ses->server->response_q,
407 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
408 ((ses->server->tcpStatus != CifsGood) &&
409 (ses->server->tcpStatus != CifsNew)));
410 }
411
412 spin_lock(&GlobalMid_Lock);
413 if (midQ->resp_buf) {
414 spin_unlock(&GlobalMid_Lock);
415 receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf);
416 } else {
417 cERROR(1,("No response buffer"));
418 if(midQ->midState == MID_REQUEST_SUBMITTED) {
419 if(ses->server->tcpStatus == CifsExiting)
420 rc = -EHOSTDOWN;
421 else {
422 ses->server->tcpStatus = CifsNeedReconnect;
423 midQ->midState = MID_RETRY_NEEDED;
424 }
425 }
426
427 if (rc != -EHOSTDOWN) {
428 if(midQ->midState == MID_RETRY_NEEDED) {
429 rc = -EAGAIN;
430 cFYI(1,("marking request for retry"));
431 } else {
432 rc = -EIO;
433 }
434 }
435 spin_unlock(&GlobalMid_Lock);
436 DeleteMidQEntry(midQ);
437 /* If not lock req, update # of requests on wire to server */
438 if(long_op < 3) {
439 atomic_dec(&ses->server->inFlight);
440 wake_up(&ses->server->request_q);
441 }
442 return rc;
443 }
444
445 if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
446 cERROR(1, ("Frame too large received. Length: %d Xid: %d",
447 receive_len, xid));
448 rc = -EIO;
449 } else { /* rcvd frame is ok */
450
451 if (midQ->resp_buf &&
452 (midQ->midState == MID_RESPONSE_RECEIVED)) {
453 in_buf->smb_buf_length = receive_len;
454 /* BB verify that length would not overrun small buf */
455 memcpy((char *)in_buf + 4,
456 (char *)midQ->resp_buf + 4,
457 receive_len);
458
459 dump_smb(in_buf, 80);
460 /* convert the length into a more usable form */
461 if((receive_len > 24) &&
462 (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
463 SECMODE_SIGN_ENABLED))) {
464 rc = cifs_verify_signature(in_buf,
465 ses->server->mac_signing_key,
466 midQ->sequence_number+1);
467 if(rc) {
468 cERROR(1,("Unexpected SMB signature"));
469 /* BB FIXME add code to kill session */
470 }
471 }
472
473 *pbytes_returned = in_buf->smb_buf_length;
474
475 /* BB special case reconnect tid and uid here? */
476 rc = map_smb_to_linux_error(in_buf);
477
478 /* convert ByteCount if necessary */
479 if (receive_len >=
480 sizeof (struct smb_hdr) -
481 4 /* do not count RFC1001 header */ +
482 (2 * in_buf->WordCount) + 2 /* bcc */ )
483 BCC(in_buf) = le16_to_cpu(BCC(in_buf));
484 } else {
485 rc = -EIO;
486 cFYI(1,("Bad MID state? "));
487 }
488 }
489cifs_no_response_exit2:
490 DeleteMidQEntry(midQ);
491
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 if(long_op < 3) {
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500493 atomic_dec(&ses->server->inFlight);
494 wake_up(&ses->server->request_q);
495 }
496
497 return rc;
498
499out_unlock2:
500 up(&ses->server->tcpSem);
501 /* If not lock req, update # of requests on wire to server */
502 if(long_op < 3) {
503 atomic_dec(&ses->server->inFlight);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 wake_up(&ses->server->request_q);
505 }
506
507 return rc;
508}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509#endif /* CIFS_EXPERIMENTAL */
510
511int
512SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
513 struct smb_hdr *in_buf, struct smb_hdr *out_buf,
514 int *pbytes_returned, const int long_op)
515{
516 int rc = 0;
517 unsigned int receive_len;
518 unsigned long timeout;
519 struct mid_q_entry *midQ;
520
521 if (ses == NULL) {
522 cERROR(1,("Null smb session"));
523 return -EIO;
524 }
525 if(ses->server == NULL) {
526 cERROR(1,("Null tcp session"));
527 return -EIO;
528 }
529
Steve French31ca3bc2005-04-28 22:41:11 -0700530 if(ses->server->tcpStatus == CifsExiting)
531 return -ENOENT;
532
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 /* Ensure that we do not send more than 50 overlapping requests
534 to the same server. We may make this configurable later or
535 use ses->maxReq */
536 if(long_op == -1) {
537 /* oplock breaks must not be held up */
538 atomic_inc(&ses->server->inFlight);
539 } else {
540 spin_lock(&GlobalMid_Lock);
541 while(1) {
Steve French275cde12005-04-28 22:41:10 -0700542 if(atomic_read(&ses->server->inFlight) >=
543 cifs_max_pending){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 spin_unlock(&GlobalMid_Lock);
545 wait_event(ses->server->request_q,
546 atomic_read(&ses->server->inFlight)
547 < cifs_max_pending);
548 spin_lock(&GlobalMid_Lock);
549 } else {
550 if(ses->server->tcpStatus == CifsExiting) {
551 spin_unlock(&GlobalMid_Lock);
552 return -ENOENT;
553 }
554
555 /* can not count locking commands against total since
556 they are allowed to block on server */
557
558 if(long_op < 3) {
559 /* update # of requests on the wire to server */
560 atomic_inc(&ses->server->inFlight);
561 }
562 spin_unlock(&GlobalMid_Lock);
563 break;
564 }
565 }
566 }
567 /* make sure that we sign in the same order that we send on this socket
568 and avoid races inside tcp sendmsg code that could cause corruption
569 of smb data */
570
571 down(&ses->server->tcpSem);
572
573 if (ses->server->tcpStatus == CifsExiting) {
574 rc = -ENOENT;
575 goto out_unlock;
576 } else if (ses->server->tcpStatus == CifsNeedReconnect) {
577 cFYI(1,("tcp session dead - return to caller to retry"));
578 rc = -EAGAIN;
579 goto out_unlock;
580 } else if (ses->status != CifsGood) {
581 /* check if SMB session is bad because we are setting it up */
582 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
583 (in_buf->Command != SMB_COM_NEGOTIATE)) {
584 rc = -EAGAIN;
585 goto out_unlock;
586 } /* else ok - we are setting up session */
587 }
588 midQ = AllocMidQEntry(in_buf, ses);
589 if (midQ == NULL) {
590 up(&ses->server->tcpSem);
591 /* If not lock req, update # of requests on wire to server */
592 if(long_op < 3) {
593 atomic_dec(&ses->server->inFlight);
594 wake_up(&ses->server->request_q);
595 }
596 return -ENOMEM;
597 }
598
599 if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
600 up(&ses->server->tcpSem);
601 cERROR(1,
602 ("Illegal length, greater than maximum frame, %d ",
603 in_buf->smb_buf_length));
604 DeleteMidQEntry(midQ);
605 /* If not lock req, update # of requests on wire to server */
606 if(long_op < 3) {
607 atomic_dec(&ses->server->inFlight);
608 wake_up(&ses->server->request_q);
609 }
610 return -EIO;
611 }
612
Steve Frenchad009ac2005-04-28 22:41:05 -0700613 rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614
615 midQ->midState = MID_REQUEST_SUBMITTED;
616 rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
617 (struct sockaddr *) &(ses->server->addr.sockAddr));
618 if(rc < 0) {
619 DeleteMidQEntry(midQ);
620 up(&ses->server->tcpSem);
621 /* If not lock req, update # of requests on wire to server */
622 if(long_op < 3) {
623 atomic_dec(&ses->server->inFlight);
624 wake_up(&ses->server->request_q);
625 }
626 return rc;
627 } else
628 up(&ses->server->tcpSem);
629 if (long_op == -1)
630 goto cifs_no_response_exit;
Steve French275cde12005-04-28 22:41:10 -0700631 else if (long_op == 2) /* writes past end of file can take loong time */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 timeout = 300 * HZ;
633 else if (long_op == 1)
634 timeout = 45 * HZ; /* should be greater than
635 servers oplock break timeout (about 43 seconds) */
636 else if (long_op > 2) {
637 timeout = MAX_SCHEDULE_TIMEOUT;
638 } else
639 timeout = 15 * HZ;
640 /* wait for 15 seconds or until woken up due to response arriving or
641 due to last connection to this server being unmounted */
642 if (signal_pending(current)) {
643 /* if signal pending do not hold up user for full smb timeout
644 but we still give response a change to complete */
645 timeout = 2 * HZ;
646 }
647
648 /* No user interrupts in wait - wreaks havoc with performance */
649 if(timeout != MAX_SCHEDULE_TIMEOUT) {
650 timeout += jiffies;
651 wait_event(ses->server->response_q,
652 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
653 time_after(jiffies, timeout) ||
654 ((ses->server->tcpStatus != CifsGood) &&
655 (ses->server->tcpStatus != CifsNew)));
656 } else {
657 wait_event(ses->server->response_q,
658 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
659 ((ses->server->tcpStatus != CifsGood) &&
660 (ses->server->tcpStatus != CifsNew)));
661 }
662
663 spin_lock(&GlobalMid_Lock);
664 if (midQ->resp_buf) {
665 spin_unlock(&GlobalMid_Lock);
666 receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf);
667 } else {
668 cERROR(1,("No response buffer"));
669 if(midQ->midState == MID_REQUEST_SUBMITTED) {
670 if(ses->server->tcpStatus == CifsExiting)
671 rc = -EHOSTDOWN;
672 else {
673 ses->server->tcpStatus = CifsNeedReconnect;
674 midQ->midState = MID_RETRY_NEEDED;
675 }
676 }
677
678 if (rc != -EHOSTDOWN) {
679 if(midQ->midState == MID_RETRY_NEEDED) {
680 rc = -EAGAIN;
681 cFYI(1,("marking request for retry"));
682 } else {
683 rc = -EIO;
684 }
685 }
686 spin_unlock(&GlobalMid_Lock);
687 DeleteMidQEntry(midQ);
688 /* If not lock req, update # of requests on wire to server */
689 if(long_op < 3) {
690 atomic_dec(&ses->server->inFlight);
691 wake_up(&ses->server->request_q);
692 }
693 return rc;
694 }
695
696 if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
Steve Frenchad009ac2005-04-28 22:41:05 -0700697 cERROR(1, ("Frame too large received. Length: %d Xid: %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 receive_len, xid));
699 rc = -EIO;
700 } else { /* rcvd frame is ok */
701
702 if (midQ->resp_buf && out_buf
703 && (midQ->midState == MID_RESPONSE_RECEIVED)) {
704 out_buf->smb_buf_length = receive_len;
705 memcpy((char *)out_buf + 4,
706 (char *)midQ->resp_buf + 4,
707 receive_len);
708
709 dump_smb(out_buf, 92);
710 /* convert the length into a more usable form */
711 if((receive_len > 24) &&
Steve Frenchad009ac2005-04-28 22:41:05 -0700712 (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
713 SECMODE_SIGN_ENABLED))) {
714 rc = cifs_verify_signature(out_buf,
715 ses->server->mac_signing_key,
716 midQ->sequence_number+1);
717 if(rc) {
Steve French275cde12005-04-28 22:41:10 -0700718 cERROR(1,("Unexpected SMB signature"));
719 /* BB FIXME add code to kill session */
Steve Frenchad009ac2005-04-28 22:41:05 -0700720 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 }
722
723 *pbytes_returned = out_buf->smb_buf_length;
724
Steve Frenchad009ac2005-04-28 22:41:05 -0700725 /* BB special case reconnect tid and uid here? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 rc = map_smb_to_linux_error(out_buf);
727
728 /* convert ByteCount if necessary */
729 if (receive_len >=
730 sizeof (struct smb_hdr) -
731 4 /* do not count RFC1001 header */ +
732 (2 * out_buf->WordCount) + 2 /* bcc */ )
733 BCC(out_buf) = le16_to_cpu(BCC(out_buf));
734 } else {
735 rc = -EIO;
736 cFYI(1,("Bad MID state? "));
737 }
738 }
739cifs_no_response_exit:
740 DeleteMidQEntry(midQ);
741
742 if(long_op < 3) {
743 atomic_dec(&ses->server->inFlight);
744 wake_up(&ses->server->request_q);
745 }
746
747 return rc;
748
749out_unlock:
750 up(&ses->server->tcpSem);
751 /* If not lock req, update # of requests on wire to server */
752 if(long_op < 3) {
753 atomic_dec(&ses->server->inFlight);
754 wake_up(&ses->server->request_q);
755 }
756
757 return rc;
758}