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