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