Pull one more egcs 1.1.2 workaround.
[linux-2.6/linux-mips.git] / fs / smbfs / sock.c
blob6125f1b227e674dec711efd2c3ceeb7a710cce20
1 /*
2 * sock.c
4 * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
5 * Copyright (C) 1997 by Volker Lendecke
7 * Please add a note about your changes to smbfs in the ChangeLog file.
8 */
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>
26 #include <linux/smb_fs.h>
27 #include <linux/smb.h>
28 #include <linux/smbno.h>
30 #include <asm/uaccess.h>
31 #include <asm/ioctls.h>
33 #include "smb_debug.h"
34 #include "proto.h"
35 #include "request.h"
38 static int
39 _recvfrom(struct socket *socket, unsigned char *ubuf, int size, unsigned flags)
41 struct iovec iov;
42 struct msghdr msg;
43 struct kiocb iocb;
44 struct sock_iocb *si;
45 mm_segment_t fs;
47 fs = get_fs();
48 set_fs(get_ds());
49 flags |= MSG_DONTWAIT | MSG_NOSIGNAL;
51 msg.msg_flags = flags;
52 msg.msg_name = NULL;
53 msg.msg_namelen = 0;
54 msg.msg_iov = &iov;
55 msg.msg_iovlen = 1;
56 msg.msg_control = NULL;
57 iov.iov_base = ubuf;
58 iov.iov_len = size;
60 init_sync_kiocb(&iocb, NULL);
61 si = kiocb_to_siocb(&iocb);
62 si->sock = socket;
63 si->scm = &si->async_scm;
64 si->msg = &msg;
65 si->size = size;
66 si->flags = flags;
68 memset(si->scm, 0, sizeof(*si->scm));
70 size = socket->ops->recvmsg(&iocb, socket, &msg, size, flags, si->scm);
71 if (size >= 0)
72 scm_recv(socket, &msg, si->scm, flags);
73 if (-EIOCBQUEUED == size)
74 size = wait_on_sync_kiocb(&iocb);
76 set_fs(fs);
77 return size;
81 * Return the server this socket belongs to
83 static struct smb_sb_info *
84 server_from_socket(struct socket *socket)
86 return socket->sk->user_data;
90 * Called when there is data on the socket.
92 void
93 smb_data_ready(struct sock *sk, int len)
95 struct smb_sb_info *server = server_from_socket(sk->socket);
96 void (*data_ready)(struct sock *, int) = server->data_ready;
98 data_ready(sk, len);
99 VERBOSE("(%p, %d)\n", sk, len);
100 smbiod_wake_up();
104 smb_valid_socket(struct inode * inode)
106 return (inode && S_ISSOCK(inode->i_mode) &&
107 SOCKET_I(inode)->type == SOCK_STREAM);
110 static struct socket *
111 server_sock(struct smb_sb_info *server)
113 struct file *file;
115 if (server && (file = server->sock_file))
117 #ifdef SMBFS_PARANOIA
118 if (!smb_valid_socket(file->f_dentry->d_inode))
119 PARANOIA("bad socket!\n");
120 #endif
121 return SOCKET_I(file->f_dentry->d_inode);
123 return NULL;
126 void
127 smb_close_socket(struct smb_sb_info *server)
129 struct file * file = server->sock_file;
131 if (file) {
132 struct socket *sock = server_sock(server);
134 VERBOSE("closing socket %p\n", sock);
135 sock->sk->data_ready = server->data_ready;
136 server->sock_file = NULL;
137 fput(file);
141 static int
142 smb_get_length(struct socket *socket, unsigned char *header)
144 int result;
146 result = _recvfrom(socket, header, 4, MSG_PEEK);
147 if (result == -EAGAIN)
148 return -ENODATA;
149 if (result < 0) {
150 PARANOIA("recv error = %d\n", -result);
151 return result;
153 if (result < 4)
154 return -ENODATA;
156 switch (header[0]) {
157 case 0x00:
158 case 0x82:
159 break;
161 case 0x85:
162 DEBUG1("Got SESSION KEEP ALIVE\n");
163 _recvfrom(socket, header, 4, 0); /* read away */
164 return -ENODATA;
166 default:
167 PARANOIA("Invalid NBT packet, code=%x\n", header[0]);
168 return -EIO;
171 /* The length in the RFC NB header is the raw data length */
172 return smb_len(header);
176 smb_recv_available(struct smb_sb_info *server)
178 mm_segment_t oldfs;
179 int avail, err;
180 struct socket *sock = server_sock(server);
182 oldfs = get_fs();
183 set_fs(get_ds());
184 err = sock->ops->ioctl(sock, SIOCINQ, (unsigned long) &avail);
185 set_fs(oldfs);
186 return (err >= 0) ? avail : err;
190 * Adjust the iovec to move on 'n' bytes (from nfs/sunrpc)
192 static int
193 smb_move_iov(struct msghdr *msg, struct iovec *niv, unsigned amount)
195 struct iovec *iv = msg->msg_iov;
196 int i;
197 int len;
200 * Eat any sent iovecs
202 while (iv->iov_len <= amount) {
203 amount -= iv->iov_len;
204 iv++;
205 msg->msg_iovlen--;
209 * And chew down the partial one
211 niv[0].iov_len = iv->iov_len-amount;
212 niv[0].iov_base =((unsigned char *)iv->iov_base)+amount;
213 iv++;
215 len = niv[0].iov_len;
218 * And copy any others
220 for (i = 1; i < msg->msg_iovlen; i++) {
221 niv[i] = *iv++;
222 len += niv[i].iov_len;
225 msg->msg_iov = niv;
226 return len;
230 * smb_receive_header
231 * Only called by the smbiod thread.
234 smb_receive_header(struct smb_sb_info *server)
236 struct socket *sock;
237 int result = 0;
238 unsigned char peek_buf[4];
240 result = -EIO;
241 sock = server_sock(server);
242 if (!sock)
243 goto out;
244 if (sock->sk->state != TCP_ESTABLISHED)
245 goto out;
247 if (!server->smb_read) {
248 result = smb_get_length(sock, peek_buf);
249 if (result < 0) {
250 if (result == -ENODATA)
251 result = 0;
252 goto out;
254 server->smb_len = result + 4;
256 if (server->smb_len < SMB_HEADER_LEN) {
257 PARANOIA("short packet: %d\n", result);
258 server->rstate = SMB_RECV_DROP;
259 result = -EIO;
260 goto out;
262 if (server->smb_len > SMB_MAX_PACKET_SIZE) {
263 PARANOIA("long packet: %d\n", result);
264 server->rstate = SMB_RECV_DROP;
265 result = -EIO;
266 goto out;
270 result = _recvfrom(sock, server->header + server->smb_read,
271 SMB_HEADER_LEN - server->smb_read, 0);
272 VERBOSE("_recvfrom: %d\n", result);
273 if (result < 0) {
274 VERBOSE("receive error: %d\n", result);
275 goto out;
277 server->smb_read += result;
279 if (server->smb_read == SMB_HEADER_LEN)
280 server->rstate = SMB_RECV_HCOMPLETE;
281 out:
282 return result;
285 static char drop_buffer[PAGE_SIZE];
288 * smb_receive_drop - read and throw away the data
289 * Only called by the smbiod thread.
291 * FIXME: we are in the kernel, could we just tell the socket that we want
292 * to drop stuff from the buffer?
295 smb_receive_drop(struct smb_sb_info *server)
297 struct socket *sock;
298 unsigned int flags;
299 struct iovec iov;
300 struct msghdr msg;
301 struct kiocb iocb;
302 struct sock_iocb *si;
303 mm_segment_t fs;
304 int rlen = smb_len(server->header) - server->smb_read + 4;
305 int result = -EIO;
307 sock = server_sock(server);
308 if (!sock)
309 goto out;
310 if (sock->sk->state != TCP_ESTABLISHED)
311 goto out;
313 fs = get_fs();
314 set_fs(get_ds());
316 flags = MSG_DONTWAIT | MSG_NOSIGNAL;
317 iov.iov_base = drop_buffer;
318 iov.iov_len = PAGE_SIZE;
319 msg.msg_flags = flags;
320 msg.msg_name = NULL;
321 msg.msg_namelen = 0;
322 msg.msg_iov = &iov;
323 msg.msg_iovlen = 1;
324 msg.msg_control = NULL;
326 if (rlen > PAGE_SIZE)
327 rlen = PAGE_SIZE;
329 init_sync_kiocb(&iocb, NULL);
330 si = kiocb_to_siocb(&iocb);
331 si->sock = sock;
332 si->scm = &si->async_scm;
333 si->msg = &msg;
334 si->size = rlen;
335 si->flags = flags;
337 memset(si->scm, 0, sizeof(*si->scm));
339 result = sock->ops->recvmsg(&iocb, sock, &msg, rlen, flags, si->scm);
340 if (result >= 0)
341 scm_recv(sock, &msg, si->scm, flags);
342 if (-EIOCBQUEUED == result)
343 result = wait_on_sync_kiocb(&iocb);
345 set_fs(fs);
347 VERBOSE("read: %d\n", result);
348 if (result < 0) {
349 VERBOSE("receive error: %d\n", result);
350 goto out;
352 server->smb_read += result;
354 if (server->smb_read >= server->smb_len)
355 server->rstate = SMB_RECV_END;
357 out:
358 return result;
362 * smb_receive
363 * Only called by the smbiod thread.
366 smb_receive(struct smb_sb_info *server, struct smb_request *req)
368 struct socket *sock;
369 unsigned int flags;
370 struct iovec iov[4];
371 struct msghdr msg;
372 struct kiocb iocb;
373 struct sock_iocb *si;
374 mm_segment_t fs;
375 int rlen;
376 int result = -EIO;
378 sock = server_sock(server);
379 if (!sock)
380 goto out;
381 if (sock->sk->state != TCP_ESTABLISHED)
382 goto out;
384 fs = get_fs();
385 set_fs(get_ds());
387 flags = MSG_DONTWAIT | MSG_NOSIGNAL;;
388 msg.msg_flags = flags;
389 msg.msg_name = NULL;
390 msg.msg_namelen = 0;
391 msg.msg_iov = req->rq_iov;
392 msg.msg_iovlen = req->rq_iovlen;
393 msg.msg_control = NULL;
395 /* Dont repeat bytes and count available bufferspace */
396 rlen = smb_move_iov(&msg, iov, req->rq_bytes_recvd);
397 if (req->rq_rlen < rlen)
398 rlen = req->rq_rlen;
400 init_sync_kiocb(&iocb, NULL);
401 si = kiocb_to_siocb(&iocb);
402 si->sock = sock;
403 si->scm = &si->async_scm;
404 si->msg = &msg;
405 si->size = rlen;
406 si->flags = flags;
408 memset(si->scm, 0, sizeof(*si->scm));
410 result = sock->ops->recvmsg(&iocb, sock, &msg, rlen, flags, si->scm);
411 if (result >= 0)
412 scm_recv(sock, &msg, si->scm, flags);
413 if (-EIOCBQUEUED == result)
414 result = wait_on_sync_kiocb(&iocb);
416 set_fs(fs);
418 VERBOSE("read: %d\n", result);
419 if (result < 0) {
420 VERBOSE("receive error: %d\n", result);
421 goto out;
423 req->rq_bytes_recvd += result;
424 server->smb_read += result;
426 out:
427 return result;
431 * Try to send a SMB request. This may return after sending only parts of the
432 * request. SMB_REQ_TRANSMITTED will be set if a request was fully sent.
434 * Parts of this was taken from xprt_sendmsg from net/sunrpc/xprt.c
437 smb_send_request(struct smb_request *req)
439 mm_segment_t fs;
440 struct smb_sb_info *server = req->rq_server;
441 struct socket *sock;
442 struct kiocb iocb;
443 struct sock_iocb *si;
444 struct msghdr msg;
445 int slen = req->rq_slen - req->rq_bytes_sent;
446 int result = -EIO;
447 struct iovec iov[4];
449 sock = server_sock(server);
450 if (!sock)
451 goto out;
452 if (sock->sk->state != TCP_ESTABLISHED)
453 goto out;
455 msg.msg_name = NULL;
456 msg.msg_namelen = 0;
457 msg.msg_control = NULL;
458 msg.msg_controllen = 0;
459 msg.msg_iov = req->rq_iov;
460 msg.msg_iovlen = req->rq_iovlen;
461 msg.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT;
463 /* Dont repeat bytes */
464 if (req->rq_bytes_sent)
465 smb_move_iov(&msg, iov, req->rq_bytes_sent);
467 init_sync_kiocb(&iocb, NULL);
468 si = kiocb_to_siocb(&iocb);
469 si->scm = &si->async_scm;
470 si->sock = sock;
471 si->msg = &msg;
472 si->size = slen;
474 fs = get_fs();
475 set_fs(get_ds());
476 result = scm_send(sock, &msg, si->scm);
477 if (result >= 0) {
478 result = sock->ops->sendmsg(&iocb, sock, &msg, slen, si->scm);
479 if (-EIOCBQUEUED != result)
480 scm_destroy(si->scm);
482 if (-EIOCBQUEUED == result)
483 result = wait_on_sync_kiocb(&iocb);
484 set_fs(fs);
486 if (result >= 0) {
487 req->rq_bytes_sent += result;
488 if (req->rq_bytes_sent >= req->rq_slen)
489 req->rq_flags |= SMB_REQ_TRANSMITTED;
491 out:
492 return result;