]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - fs/cifs/transport.c
[PATCH] cifs: Do not interpret oplock break responses as responses to an unrelated...
[linux-2.6-omap-h63xx.git] / fs / cifs / transport.c
1 /*
2  *   fs/cifs/transport.c
3  *
4  *   Copyright (C) International Business Machines  Corp., 2002,2004
5  *   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   
35 extern mempool_t *cifs_mid_poolp;
36 extern kmem_cache_t *cifs_oplock_cachep;
37
38 static struct mid_q_entry *
39 AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
40 {
41         struct mid_q_entry *temp;
42
43         if (ses == NULL) {
44                 cERROR(1, ("Null session passed in to AllocMidQEntry "));
45                 return NULL;
46         }
47         if (ses->server == NULL) {
48                 cERROR(1, ("Null TCP session in AllocMidQEntry"));
49                 return NULL;
50         }
51         
52         temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp,SLAB_KERNEL | SLAB_NOFS);
53         if (temp == NULL)
54                 return temp;
55         else {
56                 memset(temp, 0, sizeof (struct mid_q_entry));
57                 temp->mid = smb_buffer->Mid;    /* always LE */
58                 temp->pid = current->pid;
59                 temp->command = smb_buffer->Command;
60                 cFYI(1, ("For smb_command %d", temp->command));
61                 do_gettimeofday(&temp->when_sent);
62                 temp->ses = ses;
63                 temp->tsk = current;
64         }
65
66         spin_lock(&GlobalMid_Lock);
67         list_add_tail(&temp->qhead, &ses->server->pending_mid_q);
68         atomic_inc(&midCount);
69         temp->midState = MID_REQUEST_ALLOCATED;
70         spin_unlock(&GlobalMid_Lock);
71         return temp;
72 }
73
74 static void
75 DeleteMidQEntry(struct mid_q_entry *midEntry)
76 {
77         spin_lock(&GlobalMid_Lock);
78         midEntry->midState = MID_FREE;
79         list_del(&midEntry->qhead);
80         atomic_dec(&midCount);
81         spin_unlock(&GlobalMid_Lock);
82         cifs_buf_release(midEntry->resp_buf);
83         mempool_free(midEntry, cifs_mid_poolp);
84 }
85
86 struct oplock_q_entry *
87 AllocOplockQEntry(struct inode * pinode, __u16 fid, struct cifsTconInfo * tcon)
88 {
89         struct oplock_q_entry *temp;
90         if ((pinode== NULL) || (tcon == NULL)) {
91                 cERROR(1, ("Null parms passed to AllocOplockQEntry"));
92                 return NULL;
93         }
94         temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep,
95                                                        SLAB_KERNEL);
96         if (temp == NULL)
97                 return temp;
98         else {
99                 temp->pinode = pinode;
100                 temp->tcon = tcon;
101                 temp->netfid = fid;
102                 spin_lock(&GlobalMid_Lock);
103                 list_add_tail(&temp->qhead, &GlobalOplock_Q);
104                 spin_unlock(&GlobalMid_Lock);
105         }
106         return temp;
107
108 }
109
110 void DeleteOplockQEntry(struct oplock_q_entry * oplockEntry)
111 {
112         spin_lock(&GlobalMid_Lock); 
113     /* should we check if list empty first? */
114         list_del(&oplockEntry->qhead);
115         spin_unlock(&GlobalMid_Lock);
116         kmem_cache_free(cifs_oplock_cachep, oplockEntry);
117 }
118
119 int
120 smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
121          unsigned int smb_buf_length, struct sockaddr *sin)
122 {
123         int rc = 0;
124         int i = 0;
125         struct msghdr smb_msg;
126         struct kvec iov;
127         unsigned len = smb_buf_length + 4;
128
129         if(ssocket == NULL)
130                 return -ENOTSOCK; /* BB eventually add reconnect code here */
131         iov.iov_base = smb_buffer;
132         iov.iov_len = len;
133
134         smb_msg.msg_name = sin;
135         smb_msg.msg_namelen = sizeof (struct sockaddr);
136         smb_msg.msg_control = NULL;
137         smb_msg.msg_controllen = 0;
138         smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
139
140         /* smb header is converted in header_assemble. bcc and rest of SMB word
141            area, and byte area if necessary, is converted to littleendian in 
142            cifssmb.c and RFC1001 len is converted to bigendian in smb_send 
143            Flags2 is converted in SendReceive */
144
145         smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
146         cFYI(1, ("Sending smb of length %d ", smb_buf_length));
147         dump_smb(smb_buffer, len);
148
149         while (len > 0) {
150                 rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len);
151                 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
152                         i++;
153                         if(i > 60) {
154                                 cERROR(1,
155                                    ("sends on sock %p stuck for 30 seconds",
156                                     ssocket));
157                                 rc = -EAGAIN;
158                                 break;
159                         }
160                         msleep(500);
161                         continue;
162                 }
163                 if (rc < 0) 
164                         break;
165                 iov.iov_base += rc;
166                 iov.iov_len -= rc;
167                 len -= rc;
168         }
169
170         if (rc < 0) {
171                 cERROR(1,("Error %d sending data on socket to server.", rc));
172         } else {
173                 rc = 0;
174         }
175
176         return rc;
177 }
178
179 #ifdef CIFS_EXPERIMENTAL
180 /* BB finish off this function, adding support for writing set of pages as iovec */
181 /* and also adding support for operations that need to parse the response smb    */
182
183 int
184 smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer,
185          unsigned int smb_buf_length, struct kvec * write_vector /* page list */, struct sockaddr *sin)
186 {
187         int rc = 0;
188         int i = 0;
189         struct msghdr smb_msg;
190         number_of_pages += 1; /* account for SMB header */
191         struct kvec * piov  = kmalloc(number_of_pages * sizeof(struct kvec));
192         unsigned len = smb_buf_length + 4;
193
194         if(ssocket == NULL)
195                 return -ENOTSOCK; /* BB eventually add reconnect code here */
196         iov.iov_base = smb_buffer;
197         iov.iov_len = len;
198
199         smb_msg.msg_name = sin;
200         smb_msg.msg_namelen = sizeof (struct sockaddr);
201         smb_msg.msg_control = NULL;
202         smb_msg.msg_controllen = 0;
203         smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
204
205         /* smb header is converted in header_assemble. bcc and rest of SMB word
206            area, and byte area if necessary, is converted to littleendian in 
207            cifssmb.c and RFC1001 len is converted to bigendian in smb_send 
208            Flags2 is converted in SendReceive */
209
210         smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
211         cFYI(1, ("Sending smb of length %d ", smb_buf_length));
212         dump_smb(smb_buffer, len);
213
214         while (len > 0) {
215                 rc = kernel_sendmsg(ssocket, &smb_msg, &iov, number_of_pages, len?);
216                 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
217                         i++;
218                         if(i > 60) {
219                                 cERROR(1,
220                                    ("sends on sock %p stuck for 30 seconds",
221                                     ssocket));
222                                 rc = -EAGAIN;
223                                 break;
224                         }
225                         msleep(500);
226                         continue;
227                 }
228                 if (rc < 0) 
229                         break;
230                 iov.iov_base += rc;
231                 iov.iov_len -= rc;
232                 len -= rc;
233         }
234
235         if (rc < 0) {
236                 cERROR(1,("Error %d sending data on socket to server.", rc));
237         } else {
238                 rc = 0;
239         }
240
241         return rc;
242 }
243
244
245 int
246 CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses,
247             struct smb_hdr *in_buf, struct kvec * write_vector /* page list */, int *pbytes_returned, const int long_op)
248 {
249         int rc = 0;
250         unsigned long timeout = 15 * HZ;
251         struct mid_q_entry *midQ = NULL;
252
253         if (ses == NULL) {
254                 cERROR(1,("Null smb session"));
255                 return -EIO;
256         }
257         if(ses->server == NULL) {
258                 cERROR(1,("Null tcp session"));
259                 return -EIO;
260         }
261         if(pbytes_returned == NULL)
262                 return -EIO;
263         else
264                 *pbytes_returned = 0;
265
266   
267
268         /* Ensure that we do not send more than 50 overlapping requests 
269            to the same server. We may make this configurable later or
270            use ses->maxReq */
271         if(long_op == -1) {
272                 /* oplock breaks must not be held up */
273                 atomic_inc(&ses->server->inFlight);
274         } else {
275                 spin_lock(&GlobalMid_Lock); 
276                 while(1) {        
277                         if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){
278                                 spin_unlock(&GlobalMid_Lock);
279                                 wait_event(ses->server->request_q,
280                                         atomic_read(&ses->server->inFlight)
281                                          < cifs_max_pending);
282                                 spin_lock(&GlobalMid_Lock);
283                         } else {
284                                 if(ses->server->tcpStatus == CifsExiting) {
285                                         spin_unlock(&GlobalMid_Lock);
286                                         return -ENOENT;
287                                 }
288
289                         /* can not count locking commands against total since
290                            they are allowed to block on server */
291                                         
292                                 if(long_op < 3) {
293                                 /* update # of requests on the wire to server */
294                                         atomic_inc(&ses->server->inFlight);
295                                 }
296                                 spin_unlock(&GlobalMid_Lock);
297                                 break;
298                         }
299                 }
300         }
301         /* make sure that we sign in the same order that we send on this socket 
302            and avoid races inside tcp sendmsg code that could cause corruption
303            of smb data */
304
305         down(&ses->server->tcpSem); 
306
307         if (ses->server->tcpStatus == CifsExiting) {
308                 rc = -ENOENT;
309                 goto cifs_out_label;
310         } else if (ses->server->tcpStatus == CifsNeedReconnect) {
311                 cFYI(1,("tcp session dead - return to caller to retry"));
312                 rc = -EAGAIN;
313                 goto cifs_out_label;
314         } else if (ses->status != CifsGood) {
315                 /* check if SMB session is bad because we are setting it up */
316                 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && 
317                         (in_buf->Command != SMB_COM_NEGOTIATE)) {
318                         rc = -EAGAIN;
319                         goto cifs_out_label;
320                 } /* else ok - we are setting up session */
321         }
322         midQ = AllocMidQEntry(in_buf, ses);
323         if (midQ == NULL) {
324                 up(&ses->server->tcpSem);
325                 /* If not lock req, update # of requests on wire to server */
326                 if(long_op < 3) {
327                         atomic_dec(&ses->server->inFlight); 
328                         wake_up(&ses->server->request_q);
329                 }
330                 return -ENOMEM;
331         }
332
333         if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
334                 up(&ses->server->tcpSem);
335                 cERROR(1,
336                        ("Illegal length, greater than maximum frame, %d ",
337                         in_buf->smb_buf_length));
338                 DeleteMidQEntry(midQ);
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 -EIO;
345         }
346
347         /* BB can we sign efficiently in this path? */
348         rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
349
350         midQ->midState = MID_REQUEST_SUBMITTED;
351 /*      rc = smb_sendv(ses->server->ssocket, in_buf, in_buf->smb_buf_length, piovec,
352                       (struct sockaddr *) &(ses->server->addr.sockAddr));*/
353         if(rc < 0) {
354                 DeleteMidQEntry(midQ);
355                 up(&ses->server->tcpSem);
356                 /* If not lock req, update # of requests on wire to server */
357                 if(long_op < 3) {
358                         atomic_dec(&ses->server->inFlight); 
359                         wake_up(&ses->server->request_q);
360                 }
361                 return rc;
362         } else
363                 up(&ses->server->tcpSem);
364 cifs_out_label:
365         if(midQ)
366                 DeleteMidQEntry(midQ);
367                                                                                                                            
368         if(long_op < 3) {
369                 atomic_dec(&ses->server->inFlight);
370                 wake_up(&ses->server->request_q);
371         }
372
373         return rc;
374 }
375
376
377 #endif /* CIFS_EXPERIMENTAL */
378
379 int
380 SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
381             struct smb_hdr *in_buf, struct smb_hdr *out_buf,
382             int *pbytes_returned, const int long_op)
383 {
384         int rc = 0;
385         unsigned int receive_len;
386         unsigned long timeout;
387         struct mid_q_entry *midQ;
388
389         if (ses == NULL) {
390                 cERROR(1,("Null smb session"));
391                 return -EIO;
392         }
393         if(ses->server == NULL) {
394                 cERROR(1,("Null tcp session"));
395                 return -EIO;
396         }
397
398         /* Ensure that we do not send more than 50 overlapping requests 
399            to the same server. We may make this configurable later or
400            use ses->maxReq */
401         if(long_op == -1) {
402                 /* oplock breaks must not be held up */
403                 atomic_inc(&ses->server->inFlight);
404         } else {
405                 spin_lock(&GlobalMid_Lock); 
406                 while(1) {        
407                         if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){
408                                 spin_unlock(&GlobalMid_Lock);
409                                 wait_event(ses->server->request_q,
410                                         atomic_read(&ses->server->inFlight)
411                                          < cifs_max_pending);
412                                 spin_lock(&GlobalMid_Lock);
413                         } else {
414                                 if(ses->server->tcpStatus == CifsExiting) {
415                                         spin_unlock(&GlobalMid_Lock);
416                                         return -ENOENT;
417                                 }
418
419                         /* can not count locking commands against total since
420                            they are allowed to block on server */
421                                         
422                                 if(long_op < 3) {
423                                 /* update # of requests on the wire to server */
424                                         atomic_inc(&ses->server->inFlight);
425                                 }
426                                 spin_unlock(&GlobalMid_Lock);
427                                 break;
428                         }
429                 }
430         }
431         /* make sure that we sign in the same order that we send on this socket 
432            and avoid races inside tcp sendmsg code that could cause corruption
433            of smb data */
434
435         down(&ses->server->tcpSem); 
436
437         if (ses->server->tcpStatus == CifsExiting) {
438                 rc = -ENOENT;
439                 goto out_unlock;
440         } else if (ses->server->tcpStatus == CifsNeedReconnect) {
441                 cFYI(1,("tcp session dead - return to caller to retry"));
442                 rc = -EAGAIN;
443                 goto out_unlock;
444         } else if (ses->status != CifsGood) {
445                 /* check if SMB session is bad because we are setting it up */
446                 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && 
447                         (in_buf->Command != SMB_COM_NEGOTIATE)) {
448                         rc = -EAGAIN;
449                         goto out_unlock;
450                 } /* else ok - we are setting up session */
451         }
452         midQ = AllocMidQEntry(in_buf, ses);
453         if (midQ == NULL) {
454                 up(&ses->server->tcpSem);
455                 /* If not lock req, update # of requests on wire to server */
456                 if(long_op < 3) {
457                         atomic_dec(&ses->server->inFlight); 
458                         wake_up(&ses->server->request_q);
459                 }
460                 return -ENOMEM;
461         }
462
463         if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
464                 up(&ses->server->tcpSem);
465                 cERROR(1,
466                        ("Illegal length, greater than maximum frame, %d ",
467                         in_buf->smb_buf_length));
468                 DeleteMidQEntry(midQ);
469                 /* If not lock req, update # of requests on wire to server */
470                 if(long_op < 3) {
471                         atomic_dec(&ses->server->inFlight); 
472                         wake_up(&ses->server->request_q);
473                 }
474                 return -EIO;
475         }
476
477         rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
478
479         midQ->midState = MID_REQUEST_SUBMITTED;
480         rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
481                       (struct sockaddr *) &(ses->server->addr.sockAddr));
482         if(rc < 0) {
483                 DeleteMidQEntry(midQ);
484                 up(&ses->server->tcpSem);
485                 /* If not lock req, update # of requests on wire to server */
486                 if(long_op < 3) {
487                         atomic_dec(&ses->server->inFlight); 
488                         wake_up(&ses->server->request_q);
489                 }
490                 return rc;
491         } else
492                 up(&ses->server->tcpSem);
493         if (long_op == -1)
494                 goto cifs_no_response_exit;
495         else if (long_op == 2) /* writes past end of file can take looooong time */
496                 timeout = 300 * HZ;
497         else if (long_op == 1)
498                 timeout = 45 * HZ; /* should be greater than 
499                         servers oplock break timeout (about 43 seconds) */
500         else if (long_op > 2) {
501                 timeout = MAX_SCHEDULE_TIMEOUT;
502         } else
503                 timeout = 15 * HZ;
504         /* wait for 15 seconds or until woken up due to response arriving or 
505            due to last connection to this server being unmounted */
506         if (signal_pending(current)) {
507                 /* if signal pending do not hold up user for full smb timeout
508                 but we still give response a change to complete */
509                 timeout = 2 * HZ;
510         }   
511
512         /* No user interrupts in wait - wreaks havoc with performance */
513         if(timeout != MAX_SCHEDULE_TIMEOUT) {
514                 timeout += jiffies;
515                 wait_event(ses->server->response_q,
516                         (!(midQ->midState & MID_REQUEST_SUBMITTED)) || 
517                         time_after(jiffies, timeout) || 
518                         ((ses->server->tcpStatus != CifsGood) &&
519                          (ses->server->tcpStatus != CifsNew)));
520         } else {
521                 wait_event(ses->server->response_q,
522                         (!(midQ->midState & MID_REQUEST_SUBMITTED)) || 
523                         ((ses->server->tcpStatus != CifsGood) &&
524                          (ses->server->tcpStatus != CifsNew)));
525         }
526
527         spin_lock(&GlobalMid_Lock);
528         if (midQ->resp_buf) {
529                 spin_unlock(&GlobalMid_Lock);
530                 receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf);
531         } else {
532                 cERROR(1,("No response buffer"));
533                 if(midQ->midState == MID_REQUEST_SUBMITTED) {
534                         if(ses->server->tcpStatus == CifsExiting)
535                                 rc = -EHOSTDOWN;
536                         else {
537                                 ses->server->tcpStatus = CifsNeedReconnect;
538                                 midQ->midState = MID_RETRY_NEEDED;
539                         }
540                 }
541
542                 if (rc != -EHOSTDOWN) {
543                         if(midQ->midState == MID_RETRY_NEEDED) {
544                                 rc = -EAGAIN;
545                                 cFYI(1,("marking request for retry"));
546                         } else {
547                                 rc = -EIO;
548                         }
549                 }
550                 spin_unlock(&GlobalMid_Lock);
551                 DeleteMidQEntry(midQ);
552                 /* If not lock req, update # of requests on wire to server */
553                 if(long_op < 3) {
554                         atomic_dec(&ses->server->inFlight); 
555                         wake_up(&ses->server->request_q);
556                 }
557                 return rc;
558         }
559   
560         if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
561                 cERROR(1, ("Frame too large received.  Length: %d  Xid: %d",
562                         receive_len, xid));
563                 rc = -EIO;
564         } else {                /* rcvd frame is ok */
565
566                 if (midQ->resp_buf && out_buf
567                     && (midQ->midState == MID_RESPONSE_RECEIVED)) {
568                         out_buf->smb_buf_length = receive_len;
569                         memcpy((char *)out_buf + 4,
570                                (char *)midQ->resp_buf + 4,
571                                receive_len);
572
573                         dump_smb(out_buf, 92);
574                         /* convert the length into a more usable form */
575                         if((receive_len > 24) &&
576                            (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
577                                         SECMODE_SIGN_ENABLED))) {
578                                 rc = cifs_verify_signature(out_buf,
579                                                 ses->server->mac_signing_key,
580                                                 midQ->sequence_number+1);
581                                 if(rc) {
582                                         cERROR(1,("Unexpected packet signature received from server"));
583                                         /* BB FIXME - add code to kill session here */
584                                 }
585                         }
586
587                         *pbytes_returned = out_buf->smb_buf_length;
588
589                         /* BB special case reconnect tid and uid here? */
590                         rc = map_smb_to_linux_error(out_buf);
591
592                         /* convert ByteCount if necessary */
593                         if (receive_len >=
594                             sizeof (struct smb_hdr) -
595                             4 /* do not count RFC1001 header */  +
596                             (2 * out_buf->WordCount) + 2 /* bcc */ )
597                                 BCC(out_buf) = le16_to_cpu(BCC(out_buf));
598                 } else {
599                         rc = -EIO;
600                         cFYI(1,("Bad MID state? "));
601                 }
602         }
603 cifs_no_response_exit:
604         DeleteMidQEntry(midQ);
605
606         if(long_op < 3) {
607                 atomic_dec(&ses->server->inFlight); 
608                 wake_up(&ses->server->request_q);
609         }
610
611         return rc;
612
613 out_unlock:
614         up(&ses->server->tcpSem);
615         /* If not lock req, update # of requests on wire to server */
616         if(long_op < 3) {
617                 atomic_dec(&ses->server->inFlight); 
618                 wake_up(&ses->server->request_q);
619         }
620
621         return rc;
622 }