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