VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[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
127         if(ssocket == NULL)
128                 return -ENOTSOCK; /* BB eventually add reconnect code here */
129         iov.iov_base = smb_buffer;
130         iov.iov_len = smb_buf_length + 4;
131
132         smb_msg.msg_name = sin;
133         smb_msg.msg_namelen = sizeof (struct sockaddr);
134         smb_msg.msg_control = NULL;
135         smb_msg.msg_controllen = 0;
136         smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
137
138         /* smb header is converted in header_assemble. bcc and rest of SMB word
139            area, and byte area if necessary, is converted to littleendian in 
140            cifssmb.c and RFC1001 len is converted to bigendian in smb_send 
141            Flags2 is converted in SendReceive */
142
143         smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
144         cFYI(1, ("Sending smb of length %d ", smb_buf_length));
145         dump_smb(smb_buffer, smb_buf_length + 4);
146
147         while(iov.iov_len > 0) {
148                 rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, smb_buf_length + 4);
149                 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
150                         i++;
151                         if(i > 60) {
152                                 cERROR(1,
153                                    ("sends on sock %p stuck for 30 seconds",
154                                     ssocket));
155                                 rc = -EAGAIN;
156                                 break;
157                         }
158                         set_current_state(TASK_INTERRUPTIBLE);
159                         schedule_timeout(HZ/2);
160                         continue;
161                 }
162                 if (rc < 0) 
163                         break;
164                 iov.iov_base += rc;
165                 iov.iov_len -= rc;
166         }
167
168         if (rc < 0) {
169                 cERROR(1,("Error %d sending data on socket to server.", rc));
170         } else {
171                 rc = 0;
172         }
173
174         return rc;
175 }
176
177 int
178 SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
179             struct smb_hdr *in_buf, struct smb_hdr *out_buf,
180             int *pbytes_returned, const int long_op)
181 {
182         int rc = 0;
183         unsigned int receive_len;
184         long timeout;
185         struct mid_q_entry *midQ;
186
187         if (ses == NULL) {
188                 cERROR(1,("Null smb session"));
189                 return -EIO;
190         }
191         if(ses->server == NULL) {
192                 cERROR(1,("Null tcp session"));
193                 return -EIO;
194         }
195
196         /* Ensure that we do not send more than 50 overlapping requests 
197            to the same server. We may make this configurable later or
198            use ses->maxReq */
199         if(long_op == -1) {
200                 /* oplock breaks must not be held up */
201                 atomic_inc(&ses->server->inFlight);
202         } else {
203                 spin_lock(&GlobalMid_Lock); 
204                 while(1) {        
205                         if(atomic_read(&ses->server->inFlight) >= CIFS_MAX_REQ){
206                                 spin_unlock(&GlobalMid_Lock);
207                                 wait_event(ses->server->request_q,
208                                         atomic_read(&ses->server->inFlight)
209                                          < CIFS_MAX_REQ);
210                                 spin_lock(&GlobalMid_Lock);
211                         } else {
212                                 if(ses->server->tcpStatus == CifsExiting) {
213                                         spin_unlock(&GlobalMid_Lock);
214                                         return -ENOENT;
215                                 }
216
217                         /* can not count locking commands against total since
218                            they are allowed to block on server */
219                                         
220                                 if(long_op < 3) {
221                                 /* update # of requests on the wire to server */
222                                         atomic_inc(&ses->server->inFlight);
223                                 }
224                                 spin_unlock(&GlobalMid_Lock);
225                                 break;
226                         }
227                 }
228         }
229         /* make sure that we sign in the same order that we send on this socket 
230            and avoid races inside tcp sendmsg code that could cause corruption
231            of smb data */
232
233         down(&ses->server->tcpSem); 
234
235         if (ses->server->tcpStatus == CifsExiting) {
236                 rc = -ENOENT;
237                 goto out_unlock;
238         } else if (ses->server->tcpStatus == CifsNeedReconnect) {
239                 cFYI(1,("tcp session dead - return to caller to retry"));
240                 rc = -EAGAIN;
241                 goto out_unlock;
242         } else if (ses->status != CifsGood) {
243                 /* check if SMB session is bad because we are setting it up */
244                 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && 
245                         (in_buf->Command != SMB_COM_NEGOTIATE)) {
246                         rc = -EAGAIN;
247                         goto out_unlock;
248                 } /* else ok - we are setting up session */
249         }
250         midQ = AllocMidQEntry(in_buf, ses);
251         if (midQ == NULL) {
252                 up(&ses->server->tcpSem);
253                 /* If not lock req, update # of requests on wire to server */
254                 if(long_op < 3) {
255                         atomic_dec(&ses->server->inFlight); 
256                         wake_up(&ses->server->request_q);
257                 }
258                 return -ENOMEM;
259         }
260
261         if (in_buf->smb_buf_length > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4) {
262                 up(&ses->server->tcpSem);
263                 cERROR(1,
264                        ("Illegal length, greater than maximum frame, %d ",
265                         in_buf->smb_buf_length));
266                 DeleteMidQEntry(midQ);
267                 /* If not lock req, update # of requests on wire to server */
268                 if(long_op < 3) {
269                         atomic_dec(&ses->server->inFlight); 
270                         wake_up(&ses->server->request_q);
271                 }
272                 return -EIO;
273         }
274
275         if (in_buf->smb_buf_length > 12)
276                 in_buf->Flags2 = cpu_to_le16(in_buf->Flags2);
277         
278         rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number);
279
280         midQ->midState = MID_REQUEST_SUBMITTED;
281         rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
282                       (struct sockaddr *) &(ses->server->addr.sockAddr));
283         if(rc < 0) {
284                 DeleteMidQEntry(midQ);
285                 up(&ses->server->tcpSem);
286                 /* If not lock req, update # of requests on wire to server */
287                 if(long_op < 3) {
288                         atomic_dec(&ses->server->inFlight); 
289                         wake_up(&ses->server->request_q);
290                 }
291                 return rc;
292         } else
293                 up(&ses->server->tcpSem);
294         if (long_op == -1)
295                 goto cifs_no_response_exit;
296         else if (long_op == 2) /* writes past end of file can take looooong time */
297                 timeout = 300 * HZ;
298         else if (long_op == 1)
299                 timeout = 45 * HZ; /* should be greater than 
300                         servers oplock break timeout (about 43 seconds) */
301         else if (long_op > 2) {
302                 timeout = MAX_SCHEDULE_TIMEOUT;
303         } else
304                 timeout = 15 * HZ;
305         /* wait for 15 seconds or until woken up due to response arriving or 
306            due to last connection to this server being unmounted */
307         if (signal_pending(current)) {
308                 /* if signal pending do not hold up user for full smb timeout
309                 but we still give response a change to complete */
310                 if(midQ->midState & MID_REQUEST_SUBMITTED) {
311                         set_current_state(TASK_UNINTERRUPTIBLE);
312                         timeout = sleep_on_timeout(&ses->server->response_q,2 * HZ);
313                 }
314         } else { /* using normal timeout */
315                 /* timeout = wait_event_interruptible_timeout(ses->server->response_q,
316                         (midQ->midState & MID_RESPONSE_RECEIVED) || 
317                         ((ses->server->tcpStatus != CifsGood) &&
318                          (ses->server->tcpStatus != CifsNew)),
319                         timeout); */ 
320                 /* Can not allow user interrupts- wreaks havoc with performance */
321                 if(midQ->midState & MID_REQUEST_SUBMITTED) {
322                         set_current_state(TASK_UNINTERRUPTIBLE);
323                         timeout = sleep_on_timeout(&ses->server->response_q,timeout);
324                 }
325         }
326     
327         spin_lock(&GlobalMid_Lock);
328         if (midQ->resp_buf) {
329                 spin_unlock(&GlobalMid_Lock);
330                 receive_len = be32_to_cpu(midQ->resp_buf->smb_buf_length);
331         } else {
332                 cERROR(1,("No response buffer"));
333                 if(midQ->midState == MID_REQUEST_SUBMITTED) {
334                         if(ses->server->tcpStatus == CifsExiting)
335                                 rc = -EHOSTDOWN;
336                         else {
337                                 ses->server->tcpStatus = CifsNeedReconnect;
338                                 midQ->midState = MID_RETRY_NEEDED;
339                         }
340                 }
341
342                 if (rc != -EHOSTDOWN) {
343                         if(midQ->midState == MID_RETRY_NEEDED) {
344                                 rc = -EAGAIN;
345                                 cFYI(1,("marking request for retry"));
346                         } else {
347                                 rc = -EIO;
348                         }
349                 }
350                 spin_unlock(&GlobalMid_Lock);
351                 DeleteMidQEntry(midQ);
352                 /* If not lock req, update # of requests on wire to server */
353                 if(long_op < 3) {
354                         atomic_dec(&ses->server->inFlight); 
355                         wake_up(&ses->server->request_q);
356                 }
357                 return rc;
358         }
359   
360         if (receive_len > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE) {
361                 cERROR(1,
362                        ("Frame too large received.  Length: %d  Xid: %d",
363                         receive_len, xid));
364                 rc = -EIO;
365         } else {                /* rcvd frame is ok */
366
367                 if (midQ->resp_buf && out_buf
368                     && (midQ->midState == MID_RESPONSE_RECEIVED)) {
369                         memcpy(out_buf, midQ->resp_buf,
370                                receive_len +
371                                4 /* include 4 byte RFC1001 header */ );
372
373                         dump_smb(out_buf, 92);
374                         /* convert the length into a more usable form */
375                         out_buf->smb_buf_length =
376                             be32_to_cpu(out_buf->smb_buf_length);
377                         if((out_buf->smb_buf_length > 24) &&
378                            (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) {
379                                 rc = cifs_verify_signature(out_buf, ses->mac_signing_key,midQ->sequence_number); /* BB fix BB */
380                                 if(rc)
381                                         cFYI(1,("Unexpected signature received from server"));
382                         }
383
384                         if (out_buf->smb_buf_length > 12)
385                                 out_buf->Flags2 = le16_to_cpu(out_buf->Flags2);
386                         if (out_buf->smb_buf_length > 28)
387                                 out_buf->Pid = le16_to_cpu(out_buf->Pid);
388                         if (out_buf->smb_buf_length > 28)
389                                 out_buf->PidHigh =
390                                     le16_to_cpu(out_buf->PidHigh);
391
392                         *pbytes_returned = out_buf->smb_buf_length;
393
394                         /* BB special case reconnect tid and reconnect uid here? */
395                         rc = map_smb_to_linux_error(out_buf);
396
397                         /* convert ByteCount if necessary */
398                         if (receive_len >=
399                             sizeof (struct smb_hdr) -
400                             4 /* do not count RFC1001 header */  +
401                             (2 * out_buf->WordCount) + 2 /* bcc */ )
402                                 BCC(out_buf) = le16_to_cpu(BCC(out_buf));
403                 } else {
404                         rc = -EIO;
405                         cFYI(1,("Bad MID state? "));
406                 }
407         }
408 cifs_no_response_exit:
409         DeleteMidQEntry(midQ);
410
411         if(long_op < 3) {
412                 atomic_dec(&ses->server->inFlight); 
413                 wake_up(&ses->server->request_q);
414         }
415
416         return rc;
417
418 out_unlock:
419         up(&ses->server->tcpSem);
420         /* If not lock req, update # of requests on wire to server */
421         if(long_op < 3) {
422                 atomic_dec(&ses->server->inFlight); 
423                 wake_up(&ses->server->request_q);
424         }
425
426         return rc;
427 }