| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 18 | #include <linux/mm.h> | 
|  | 19 | #include <linux/netdevice.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 20 | #include <linux/workqueue.h> | 
|  | 21 | #include <net/scm.h> | 
| Arnaldo Carvalho de Melo | c752f07 | 2005-08-09 20:08:28 -0700 | [diff] [blame] | 22 | #include <net/tcp_states.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | 
| Josef Sipek | 17b75e6 | 2006-12-08 02:37:39 -0800 | [diff] [blame] | 84 | if (!smb_valid_socket(file->f_path.dentry->d_inode)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 85 | PARANOIA("bad socket!\n"); | 
|  | 86 | #endif | 
| Josef Sipek | 17b75e6 | 2006-12-08 02:37:39 -0800 | [diff] [blame] | 87 | return SOCKET_I(file->f_path.dentry->d_inode); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 */ | 
|  | 332 | rlen = smb_move_iov(&p, &num, iov, req->rq_bytes_recvd); | 
|  | 333 | if (req->rq_rlen < rlen) | 
|  | 334 | rlen = req->rq_rlen; | 
|  | 335 |  | 
|  | 336 | result = kernel_recvmsg(sock, &msg, p, num, rlen, flags); | 
|  | 337 |  | 
|  | 338 | VERBOSE("read: %d\n", result); | 
|  | 339 | if (result < 0) { | 
|  | 340 | VERBOSE("receive error: %d\n", result); | 
|  | 341 | goto out; | 
|  | 342 | } | 
|  | 343 | req->rq_bytes_recvd += result; | 
|  | 344 | server->smb_read += result; | 
|  | 345 |  | 
|  | 346 | out: | 
|  | 347 | return result; | 
|  | 348 | } | 
|  | 349 |  | 
|  | 350 | /* | 
|  | 351 | * Try to send a SMB request. This may return after sending only parts of the | 
|  | 352 | * request. SMB_REQ_TRANSMITTED will be set if a request was fully sent. | 
|  | 353 | * | 
|  | 354 | * Parts of this was taken from xprt_sendmsg from net/sunrpc/xprt.c | 
|  | 355 | */ | 
|  | 356 | int | 
|  | 357 | smb_send_request(struct smb_request *req) | 
|  | 358 | { | 
|  | 359 | struct smb_sb_info *server = req->rq_server; | 
|  | 360 | struct socket *sock; | 
|  | 361 | struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT}; | 
|  | 362 | int slen = req->rq_slen - req->rq_bytes_sent; | 
|  | 363 | int result = -EIO; | 
|  | 364 | struct kvec iov[4]; | 
|  | 365 | struct kvec *p = req->rq_iov; | 
|  | 366 | size_t num = req->rq_iovlen; | 
|  | 367 |  | 
|  | 368 | sock = server_sock(server); | 
|  | 369 | if (!sock) | 
|  | 370 | goto out; | 
|  | 371 | if (sock->sk->sk_state != TCP_ESTABLISHED) | 
|  | 372 | goto out; | 
|  | 373 |  | 
|  | 374 | /* Dont repeat bytes */ | 
|  | 375 | if (req->rq_bytes_sent) | 
|  | 376 | smb_move_iov(&p, &num, iov, req->rq_bytes_sent); | 
|  | 377 |  | 
|  | 378 | result = kernel_sendmsg(sock, &msg, p, num, slen); | 
|  | 379 |  | 
|  | 380 | if (result >= 0) { | 
|  | 381 | req->rq_bytes_sent += result; | 
|  | 382 | if (req->rq_bytes_sent >= req->rq_slen) | 
|  | 383 | req->rq_flags |= SMB_REQ_TRANSMITTED; | 
|  | 384 | } | 
|  | 385 | out: | 
|  | 386 | return result; | 
|  | 387 | } |