7a667c06e3b18490331d157bdf968b60816a8f7a
[linux-flexiantxendom0-3.2.10.git] / fs / cifs / transport.c
1 /*
2  *   fs/cifs/transport.c
3  *
4  *   Copyright (c) International Business Machines  Corp., 2002
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/version.h>
27 #include <asm/uaccess.h>
28 #include <asm/processor.h>
29 #include "cifspdu.h"
30 #include "cifsglob.h"
31 #include "cifsproto.h"
32 #include "cifs_debug.h"
33
34 extern kmem_cache_t *cifs_mid_cachep;
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         int timeout = 10 * HZ;
42
43 /* BB add spinlock to protect midq for each session BB */
44         if (ses == NULL) {
45                 cERROR(1, ("Null session passed in to AllocMidQEntry "));
46                 return NULL;
47         }
48         if (ses->server == NULL) {
49                 cERROR(1, ("Null TCP session in AllocMidQEntry"));
50                 return NULL;
51         }
52         
53         temp = (struct mid_q_entry *) kmem_cache_alloc(cifs_mid_cachep,
54                                                        SLAB_KERNEL);
55         if (temp == NULL)
56                 return temp;
57         else {
58                 memset(temp, 0, sizeof (struct mid_q_entry));
59                 temp->mid = smb_buffer->Mid;    /* always LE */
60                 temp->pid = current->pid;
61                 temp->command = smb_buffer->Command;
62                 cFYI(1, ("For smb_command %d", temp->command));
63                 do_gettimeofday(&temp->when_sent);
64                 temp->ses = ses;
65                 temp->tsk = current;
66         }
67
68         while ((ses->server->tcpStatus != CifsGood) && (timeout > 0)){ 
69                 /* Give the tcp thread up to 10 seconds to reconnect */
70                 /* Should we wake up tcp thread first? BB  */
71                 timeout = wait_event_interruptible_timeout(ses->server->response_q,
72                         (ses->server->tcpStatus == CifsGood), timeout);
73         }
74
75         if (ses->server->tcpStatus == CifsGood) {
76                 write_lock(&GlobalMid_Lock);
77                 list_add_tail(&temp->qhead, &ses->server->pending_mid_q);
78                 atomic_inc(&midCount);
79                 temp->midState = MID_REQUEST_ALLOCATED;
80                 write_unlock(&GlobalMid_Lock);
81         } else { 
82                 cERROR(1,("Need to reconnect after session died to server"));
83                 if (temp)
84                         kmem_cache_free(cifs_mid_cachep, temp);
85                 return NULL;
86         }
87         return temp;
88 }
89
90 void
91 DeleteMidQEntry(struct mid_q_entry *midEntry)
92 {
93         /* BB add spinlock to protect midq for each session BB */
94         write_lock(&GlobalMid_Lock);
95         midEntry->midState = MID_FREE;
96         list_del(&midEntry->qhead);
97         atomic_dec(&midCount);
98         write_unlock(&GlobalMid_Lock);
99         buf_release(midEntry->resp_buf);
100         kmem_cache_free(cifs_mid_cachep, midEntry);
101 }
102
103 struct oplock_q_entry *
104 AllocOplockQEntry(struct file * file, struct cifsTconInfo * tcon)
105 {
106         struct oplock_q_entry *temp;
107         if ((file == NULL) || (tcon == NULL)) {
108                 cERROR(1, ("Null parms passed to AllocOplockQEntry"));
109                 return NULL;
110         }
111         temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep,
112                                                        SLAB_KERNEL);
113         if (temp == NULL)
114                 return temp;
115         else {
116                 temp->file_to_flush = file;
117                 temp->tcon = tcon;
118                 write_lock(&GlobalMid_Lock);
119                 list_add_tail(&temp->qhead, &GlobalOplock_Q);
120                 write_unlock(&GlobalMid_Lock);
121         }
122         return temp;
123
124 }
125
126 void DeleteOplockQEntry(struct oplock_q_entry * oplockEntry)
127 {
128         /* BB add spinlock to protect midq for each session BB */
129         write_lock(&GlobalMid_Lock); 
130     /* should we check if list empty first? */
131         list_del(&oplockEntry->qhead);
132         write_unlock(&GlobalMid_Lock);
133         kmem_cache_free(cifs_oplock_cachep, oplockEntry);
134 }
135
136 int
137 smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
138          unsigned int smb_buf_length, struct sockaddr *sin)
139 {
140         int rc = 0;
141         struct msghdr smb_msg;
142         struct iovec iov;
143         mm_segment_t temp_fs;
144
145         if(ssocket == NULL)
146                 return -ENOTSOCK; /* BB eventually add reconnect code here */
147 /*  ssocket->sk->allocation = GFP_BUFFER; *//* BB is this spurious? */
148         iov.iov_base = smb_buffer;
149         iov.iov_len = smb_buf_length + 4;
150
151         smb_msg.msg_name = sin;
152         smb_msg.msg_namelen = sizeof (struct sockaddr);
153         smb_msg.msg_iov = &iov;
154         smb_msg.msg_iovlen = 1;
155         smb_msg.msg_control = NULL;
156         smb_msg.msg_controllen = 0;
157         smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
158
159         /* smb header is converted in header_assemble. bcc and rest of SMB word
160            area, and byte area if necessary, is converted to littleendian in 
161            cifssmb.c and RFC1001 len is converted to bigendian in smb_send 
162            Flags2 is converted in SendReceive */
163
164         smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
165         cFYI(1, ("Sending smb of length %d ", smb_buf_length));
166         dump_smb(smb_buffer, smb_buf_length + 4);
167
168         temp_fs = get_fs();     /* we must turn off socket api parm checking */
169         set_fs(get_ds());
170         rc = sock_sendmsg(ssocket, &smb_msg, smb_buf_length + 4);
171
172         set_fs(temp_fs);
173
174         if (rc < 0) {
175                 cERROR(1,
176                        ("Error %d sending data on socket to server.", rc));
177         } else
178                 rc = 0;
179
180         return rc;
181 }
182
183 int
184 SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
185             struct smb_hdr *in_buf, struct smb_hdr *out_buf,
186             int *pbytes_returned, const int long_op)
187 {
188         int rc = 0;
189         unsigned int receive_len;
190         long timeout;
191         struct mid_q_entry *midQ;
192
193         midQ = AllocMidQEntry(in_buf, ses);
194         if (midQ == NULL)
195                 return -EIO;
196         if (in_buf->smb_buf_length > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4) {
197                 cERROR(1,
198                        ("Illegal length, greater than maximum frame, %d ",
199                         in_buf->smb_buf_length));
200                 DeleteMidQEntry(midQ);
201                 return -EIO;
202         }
203
204         if (in_buf->smb_buf_length > 12)
205                 in_buf->Flags2 = cpu_to_le16(in_buf->Flags2);
206         
207         rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number);
208
209         midQ->midState = MID_REQUEST_SUBMITTED;
210         rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
211                       (struct sockaddr *) &(ses->server->sockAddr));
212
213         if (long_op == -1)
214                 goto cifs_no_response_exit;
215         if (long_op > 1) /* writes past end of file can take looooong time */
216                 timeout = 300 * HZ;
217         else if (long_op == 1)
218                 timeout = 60 * HZ;
219         else
220                 timeout = 15 * HZ;
221         /* wait for 15 seconds or until woken up due to response arriving or 
222            due to last connection to this server being unmounted */
223
224         timeout = wait_event_interruptible_timeout(ses->server->response_q,
225                                 midQ->
226                                 midState & MID_RESPONSE_RECEIVED,
227                                 timeout);
228         if (signal_pending(current)) {
229                 cERROR(1, ("CIFS: caught signal"));
230                 DeleteMidQEntry(midQ);
231                 return -EINTR;
232         } else {
233                 if (midQ->resp_buf)
234                         receive_len =
235                             be32_to_cpu(midQ->resp_buf->smb_buf_length);
236                 else {
237                         DeleteMidQEntry(midQ);
238                         return -EIO;
239                 }
240         }
241
242         if (timeout == 0) {
243                 cFYI(1,
244                      ("Timeout on receive. Assume response SMB is invalid."));
245                 rc = -ETIMEDOUT;
246         } else if (receive_len > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE) {
247                 cERROR(1,
248                        ("Frame too large received.  Length: %d  Xid: %d",
249                         receive_len, xid));
250                 rc = -EIO;
251         } else {                /* rcvd frame is ok */
252
253                 if (midQ->resp_buf && out_buf
254                     && (midQ->midState == MID_RESPONSE_RECEIVED)) {
255                         memcpy(out_buf, midQ->resp_buf,
256                                receive_len +
257                                4 /* include 4 byte RFC1001 header */ );
258
259                         dump_smb(out_buf, 92);
260                         /* convert the length into a more usable form */
261                         out_buf->smb_buf_length =
262                             be32_to_cpu(out_buf->smb_buf_length);
263                         if((out_buf->smb_buf_length > 24) &&
264                            (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) {
265                                 rc = cifs_verify_signature(out_buf, ses->mac_signing_key,midQ->sequence_number); /* BB fix BB */
266                                 if(rc)
267                                         cFYI(1,("Unexpected signature received from server"));
268                         }
269
270                         if (out_buf->smb_buf_length > 12)
271                                 out_buf->Flags2 = le16_to_cpu(out_buf->Flags2);
272                         if (out_buf->smb_buf_length > 28)
273                                 out_buf->Pid = le16_to_cpu(out_buf->Pid);
274                         if (out_buf->smb_buf_length > 28)
275                                 out_buf->PidHigh =
276                                     le16_to_cpu(out_buf->PidHigh);
277
278                         *pbytes_returned = out_buf->smb_buf_length;
279
280                         /* BB special case reconnect tid and reconnect uid here? */
281                         rc = map_smb_to_linux_error(out_buf);
282
283                         /* convert ByteCount if necessary */
284                         if (receive_len >=
285                             sizeof (struct smb_hdr) -
286                             4 /* do not count RFC1001 header */  +
287                             (2 * out_buf->WordCount) + 2 /* bcc */ )
288                                 BCC(out_buf) = le16_to_cpu(BCC(out_buf));
289                 } else
290                         rc = -EIO;
291         }
292 cifs_no_response_exit:
293         DeleteMidQEntry(midQ);  /* BB what if process is killed?
294                          - BB add background daemon to clean up Mid entries from
295                          killed processes & test killing process with active mid */
296         return rc;
297 }