linux/fs/smbfs/sock.c
<<
>>
Prefs
   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/mm.h>
  19#include <linux/netdevice.h>
  20#include <linux/workqueue.h>
  21#include <net/scm.h>
  22#include <net/tcp_states.h>
  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
  37static 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 */
  49static struct smb_sb_info *
  50server_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 */
  58void
  59smb_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
  69int
  70smb_valid_socket(struct inode * inode)
  71{
  72        return (inode && S_ISSOCK(inode->i_mode) && 
  73                SOCKET_I(inode)->type == SOCK_STREAM);
  74}
  75
  76static struct socket *
  77server_sock(struct smb_sb_info *server)
  78{
  79        struct file *file;
  80
  81        if (server && (file = server->sock_file))
  82        {
  83#ifdef SMBFS_PARANOIA
  84                if (!smb_valid_socket(file->f_path.dentry->d_inode))
  85                        PARANOIA("bad socket!\n");
  86#endif
  87                return SOCKET_I(file->f_path.dentry->d_inode);
  88        }
  89        return NULL;
  90}
  91
  92void
  93smb_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
 107static int
 108smb_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
 141int
 142smb_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 */
 158static int
 159smb_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 */
 199int
 200smb_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;
 247out:
 248        return result;
 249}
 250
 251static 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 */
 260int
 261smb_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
 299out:
 300        return result;
 301}
 302
 303/*
 304 * smb_receive
 305 * Only called by the smbiod thread.
 306 */
 307int
 308smb_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 = min_t(int, smb_move_iov(&p, &num, iov, req->rq_bytes_recvd),
 333                        (req->rq_rlen - req->rq_bytes_recvd));
 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
 345out:
 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 */
 355int
 356smb_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        }
 384out:
 385        return result;
 386}
 387