Merge git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6
[pandora-kernel.git] / drivers / staging / smbfs / sock.c
1 /*
2  *  sock.c
3  *
4  *  Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
5  *  Copyright (C) 1997 by Volker Lendecke
6  *
7  *  Please add a note about your changes to smbfs in the ChangeLog file.
8  */
9
10 #include <linux/fs.h>
11 #include <linux/time.h>
12 #include <linux/errno.h>
13 #include <linux/socket.h>
14 #include <linux/fcntl.h>
15 #include <linux/file.h>
16 #include <linux/in.h>
17 #include <linux/net.h>
18 #include <linux/mm.h>
19 #include <linux/netdevice.h>
20 #include <linux/workqueue.h>
21 #include <net/scm.h>
22 #include <net/tcp_states.h>
23 #include <net/ip.h>
24
25 #include <asm/uaccess.h>
26 #include <asm/ioctls.h>
27
28 #include "smb_fs.h"
29 #include "smb.h"
30 #include "smbno.h"
31 #include "smb_debug.h"
32 #include "proto.h"
33 #include "request.h"
34
35
36 static int
37 _recvfrom(struct socket *socket, unsigned char *ubuf, int size, unsigned flags)
38 {
39         struct kvec iov = {ubuf, size};
40         struct msghdr msg = {.msg_flags = flags};
41         msg.msg_flags |= MSG_DONTWAIT | MSG_NOSIGNAL;
42         return kernel_recvmsg(socket, &msg, &iov, 1, size, msg.msg_flags);
43 }
44
45 /*
46  * Return the server this socket belongs to
47  */
48 static struct smb_sb_info *
49 server_from_socket(struct socket *socket)
50 {
51         return socket->sk->sk_user_data;
52 }
53
54 /*
55  * Called when there is data on the socket.
56  */
57 void
58 smb_data_ready(struct sock *sk, int len)
59 {
60         struct smb_sb_info *server = server_from_socket(sk->sk_socket);
61         void (*data_ready)(struct sock *, int) = server->data_ready;
62
63         data_ready(sk, len);
64         VERBOSE("(%p, %d)\n", sk, len);
65         smbiod_wake_up();
66 }
67
68 int
69 smb_valid_socket(struct inode * inode)
70 {
71         return (inode && S_ISSOCK(inode->i_mode) && 
72                 SOCKET_I(inode)->type == SOCK_STREAM);
73 }
74
75 static struct socket *
76 server_sock(struct smb_sb_info *server)
77 {
78         struct file *file;
79
80         if (server && (file = server->sock_file))
81         {
82 #ifdef SMBFS_PARANOIA
83                 if (!smb_valid_socket(file->f_path.dentry->d_inode))
84                         PARANOIA("bad socket!\n");
85 #endif
86                 return SOCKET_I(file->f_path.dentry->d_inode);
87         }
88         return NULL;
89 }
90
91 void
92 smb_close_socket(struct smb_sb_info *server)
93 {
94         struct file * file = server->sock_file;
95
96         if (file) {
97                 struct socket *sock = server_sock(server);
98
99                 VERBOSE("closing socket %p\n", sock);
100                 sock->sk->sk_data_ready = server->data_ready;
101                 server->sock_file = NULL;
102                 fput(file);
103         }
104 }
105
106 static int
107 smb_get_length(struct socket *socket, unsigned char *header)
108 {
109         int result;
110
111         result = _recvfrom(socket, header, 4, MSG_PEEK);
112         if (result == -EAGAIN)
113                 return -ENODATA;
114         if (result < 0) {
115                 PARANOIA("recv error = %d\n", -result);
116                 return result;
117         }
118         if (result < 4)
119                 return -ENODATA;
120
121         switch (header[0]) {
122         case 0x00:
123         case 0x82:
124                 break;
125
126         case 0x85:
127                 DEBUG1("Got SESSION KEEP ALIVE\n");
128                 _recvfrom(socket, header, 4, 0);        /* read away */
129                 return -ENODATA;
130
131         default:
132                 PARANOIA("Invalid NBT packet, code=%x\n", header[0]);
133                 return -EIO;
134         }
135
136         /* The length in the RFC NB header is the raw data length */
137         return smb_len(header);
138 }
139
140 int
141 smb_recv_available(struct smb_sb_info *server)
142 {
143         mm_segment_t oldfs;
144         int avail, err;
145         struct socket *sock = server_sock(server);
146
147         oldfs = get_fs();
148         set_fs(get_ds());
149         err = sock->ops->ioctl(sock, SIOCINQ, (unsigned long) &avail);
150         set_fs(oldfs);
151         return (err >= 0) ? avail : err;
152 }
153
154 /*
155  * Adjust the kvec to move on 'n' bytes (from nfs/sunrpc)
156  */
157 static int
158 smb_move_iov(struct kvec **data, size_t *num, struct kvec *vec, unsigned amount)
159 {
160         struct kvec *iv = *data;
161         int i;
162         int len;
163
164         /*
165          *      Eat any sent kvecs
166          */
167         while (iv->iov_len <= amount) {
168                 amount -= iv->iov_len;
169                 iv++;
170                 (*num)--;
171         }
172
173         /*
174          *      And chew down the partial one
175          */
176         vec[0].iov_len = iv->iov_len-amount;
177         vec[0].iov_base =((unsigned char *)iv->iov_base)+amount;
178         iv++;
179
180         len = vec[0].iov_len;
181
182         /*
183          *      And copy any others
184          */
185         for (i = 1; i < *num; i++) {
186                 vec[i] = *iv++;
187                 len += vec[i].iov_len;
188         }
189
190         *data = vec;
191         return len;
192 }
193
194 /*
195  * smb_receive_header
196  * Only called by the smbiod thread.
197  */
198 int
199 smb_receive_header(struct smb_sb_info *server)
200 {
201         struct socket *sock;
202         int result = 0;
203         unsigned char peek_buf[4];
204
205         result = -EIO; 
206         sock = server_sock(server);
207         if (!sock)
208                 goto out;
209         if (sock->sk->sk_state != TCP_ESTABLISHED)
210                 goto out;
211
212         if (!server->smb_read) {
213                 result = smb_get_length(sock, peek_buf);
214                 if (result < 0) {
215                         if (result == -ENODATA)
216                                 result = 0;
217                         goto out;
218                 }
219                 server->smb_len = result + 4;
220
221                 if (server->smb_len < SMB_HEADER_LEN) {
222                         PARANOIA("short packet: %d\n", result);
223                         server->rstate = SMB_RECV_DROP;
224                         result = -EIO;
225                         goto out;
226                 }
227                 if (server->smb_len > SMB_MAX_PACKET_SIZE) {
228                         PARANOIA("long packet: %d\n", result);
229                         server->rstate = SMB_RECV_DROP;
230                         result = -EIO;
231                         goto out;
232                 }
233         }
234
235         result = _recvfrom(sock, server->header + server->smb_read,
236                            SMB_HEADER_LEN - server->smb_read, 0);
237         VERBOSE("_recvfrom: %d\n", result);
238         if (result < 0) {
239                 VERBOSE("receive error: %d\n", result);
240                 goto out;
241         }
242         server->smb_read += result;
243
244         if (server->smb_read == SMB_HEADER_LEN)
245                 server->rstate = SMB_RECV_HCOMPLETE;
246 out:
247         return result;
248 }
249
250 static char drop_buffer[PAGE_SIZE];
251
252 /*
253  * smb_receive_drop - read and throw away the data
254  * Only called by the smbiod thread.
255  *
256  * FIXME: we are in the kernel, could we just tell the socket that we want
257  * to drop stuff from the buffer?
258  */
259 int
260 smb_receive_drop(struct smb_sb_info *server)
261 {
262         struct socket *sock;
263         unsigned int flags;
264         struct kvec iov;
265         struct msghdr msg;
266         int rlen = smb_len(server->header) - server->smb_read + 4;
267         int result = -EIO;
268
269         if (rlen > PAGE_SIZE)
270                 rlen = PAGE_SIZE;
271
272         sock = server_sock(server);
273         if (!sock)
274                 goto out;
275         if (sock->sk->sk_state != TCP_ESTABLISHED)
276                 goto out;
277
278         flags = MSG_DONTWAIT | MSG_NOSIGNAL;
279         iov.iov_base = drop_buffer;
280         iov.iov_len = PAGE_SIZE;
281         msg.msg_flags = flags;
282         msg.msg_name = NULL;
283         msg.msg_namelen = 0;
284         msg.msg_control = NULL;
285
286         result = kernel_recvmsg(sock, &msg, &iov, 1, rlen, flags);
287
288         VERBOSE("read: %d\n", result);
289         if (result < 0) {
290                 VERBOSE("receive error: %d\n", result);
291                 goto out;
292         }
293         server->smb_read += result;
294
295         if (server->smb_read >= server->smb_len)
296                 server->rstate = SMB_RECV_END;
297
298 out:
299         return result;
300 }
301
302 /*
303  * smb_receive
304  * Only called by the smbiod thread.
305  */
306 int
307 smb_receive(struct smb_sb_info *server, struct smb_request *req)
308 {
309         struct socket *sock;
310         unsigned int flags;
311         struct kvec iov[4];
312         struct kvec *p = req->rq_iov;
313         size_t num = req->rq_iovlen;
314         struct msghdr msg;
315         int rlen;
316         int result = -EIO;
317
318         sock = server_sock(server);
319         if (!sock)
320                 goto out;
321         if (sock->sk->sk_state != TCP_ESTABLISHED)
322                 goto out;
323
324         flags = MSG_DONTWAIT | MSG_NOSIGNAL;
325         msg.msg_flags = flags;
326         msg.msg_name = NULL;
327         msg.msg_namelen = 0;
328         msg.msg_control = NULL;
329
330         /* Dont repeat bytes and count available bufferspace */
331         rlen = min_t(int, smb_move_iov(&p, &num, iov, req->rq_bytes_recvd),
332                         (req->rq_rlen - req->rq_bytes_recvd));
333
334         result = kernel_recvmsg(sock, &msg, p, num, rlen, flags);
335
336         VERBOSE("read: %d\n", result);
337         if (result < 0) {
338                 VERBOSE("receive error: %d\n", result);
339                 goto out;
340         }
341         req->rq_bytes_recvd += result;
342         server->smb_read += result;
343
344 out:
345         return result;
346 }
347
348 /*
349  * Try to send a SMB request. This may return after sending only parts of the
350  * request. SMB_REQ_TRANSMITTED will be set if a request was fully sent.
351  *
352  * Parts of this was taken from xprt_sendmsg from net/sunrpc/xprt.c
353  */
354 int
355 smb_send_request(struct smb_request *req)
356 {
357         struct smb_sb_info *server = req->rq_server;
358         struct socket *sock;
359         struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT};
360         int slen = req->rq_slen - req->rq_bytes_sent;
361         int result = -EIO;
362         struct kvec iov[4];
363         struct kvec *p = req->rq_iov;
364         size_t num = req->rq_iovlen;
365
366         sock = server_sock(server);
367         if (!sock)
368                 goto out;
369         if (sock->sk->sk_state != TCP_ESTABLISHED)
370                 goto out;
371
372         /* Dont repeat bytes */
373         if (req->rq_bytes_sent)
374                 smb_move_iov(&p, &num, iov, req->rq_bytes_sent);
375
376         result = kernel_sendmsg(sock, &msg, p, num, slen);
377
378         if (result >= 0) {
379                 req->rq_bytes_sent += result;
380                 if (req->rq_bytes_sent >= req->rq_slen)
381                         req->rq_flags |= SMB_REQ_TRANSMITTED;
382         }
383 out:
384         return result;
385 }