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