[funini.com] -> [kei@sodan] -> Kernel Reading

root/fs/smbfs/sock.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. _recvfrom
  2. server_from_socket
  3. smb_data_ready
  4. smb_valid_socket
  5. server_sock
  6. smb_close_socket
  7. smb_get_length
  8. smb_recv_available
  9. smb_move_iov
  10. smb_receive_header
  11. smb_receive_drop
  12. smb_receive
  13. smb_send_request

/*
 *  sock.c
 *
 *  Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
 *  Copyright (C) 1997 by Volker Lendecke
 *
 *  Please add a note about your changes to smbfs in the ChangeLog file.
 */

#include <linux/fs.h>
#include <linux/time.h>
#include <linux/errno.h>
#include <linux/socket.h>
#include <linux/fcntl.h>
#include <linux/file.h>
#include <linux/in.h>
#include <linux/net.h>
#include <linux/mm.h>
#include <linux/netdevice.h>
#include <linux/workqueue.h>
#include <net/scm.h>
#include <net/tcp_states.h>
#include <net/ip.h>

#include <linux/smb_fs.h>
#include <linux/smb.h>
#include <linux/smbno.h>

#include <asm/uaccess.h>
#include <asm/ioctls.h>

#include "smb_debug.h"
#include "proto.h"
#include "request.h"


static int
_recvfrom(struct socket *socket, unsigned char *ubuf, int size, unsigned flags)
{
        struct kvec iov = {ubuf, size};
        struct msghdr msg = {.msg_flags = flags};
        msg.msg_flags |= MSG_DONTWAIT | MSG_NOSIGNAL;
        return kernel_recvmsg(socket, &msg, &iov, 1, size, msg.msg_flags);
}

/*
 * Return the server this socket belongs to
 */
static struct smb_sb_info *
server_from_socket(struct socket *socket)
{
        return socket->sk->sk_user_data;
}

/*
 * Called when there is data on the socket.
 */
void
smb_data_ready(struct sock *sk, int len)
{
        struct smb_sb_info *server = server_from_socket(sk->sk_socket);
        void (*data_ready)(struct sock *, int) = server->data_ready;

        data_ready(sk, len);
        VERBOSE("(%p, %d)\n", sk, len);
        smbiod_wake_up();
}

int
smb_valid_socket(struct inode * inode)
{
        return (inode && S_ISSOCK(inode->i_mode) && 
                SOCKET_I(inode)->type == SOCK_STREAM);
}

static struct socket *
server_sock(struct smb_sb_info *server)
{
        struct file *file;

        if (server && (file = server->sock_file))
        {
#ifdef SMBFS_PARANOIA
                if (!smb_valid_socket(file->f_path.dentry->d_inode))
                        PARANOIA("bad socket!\n");
#endif
                return SOCKET_I(file->f_path.dentry->d_inode);
        }
        return NULL;
}

void
smb_close_socket(struct smb_sb_info *server)
{
        struct file * file = server->sock_file;

        if (file) {
                struct socket *sock = server_sock(server);

                VERBOSE("closing socket %p\n", sock);
                sock->sk->sk_data_ready = server->data_ready;
                server->sock_file = NULL;
                fput(file);
        }
}

static int
smb_get_length(struct socket *socket, unsigned char *header)
{
        int result;

        result = _recvfrom(socket, header, 4, MSG_PEEK);
        if (result == -EAGAIN)
                return -ENODATA;
        if (result < 0) {
                PARANOIA("recv error = %d\n", -result);
                return result;
        }
        if (result < 4)
                return -ENODATA;

        switch (header[0]) {
        case 0x00:
        case 0x82:
                break;

        case 0x85:
                DEBUG1("Got SESSION KEEP ALIVE\n");
                _recvfrom(socket, header, 4, 0);        /* read away */
                return -ENODATA;

        default:
                PARANOIA("Invalid NBT packet, code=%x\n", header[0]);
                return -EIO;
        }

        /* The length in the RFC NB header is the raw data length */
        return smb_len(header);
}

int
smb_recv_available(struct smb_sb_info *server)
{
        mm_segment_t oldfs;
        int avail, err;
        struct socket *sock = server_sock(server);

        oldfs = get_fs();
        set_fs(get_ds());
        err = sock->ops->ioctl(sock, SIOCINQ, (unsigned long) &avail);
        set_fs(oldfs);
        return (err >= 0) ? avail : err;
}

/*
 * Adjust the kvec to move on 'n' bytes (from nfs/sunrpc)
 */
static int
smb_move_iov(struct kvec **data, size_t *num, struct kvec *vec, unsigned amount)
{
        struct kvec *iv = *data;
        int i;
        int len;

        /*
         *      Eat any sent kvecs
         */
        while (iv->iov_len <= amount) {
                amount -= iv->iov_len;
                iv++;
                (*num)--;
        }

        /*
         *      And chew down the partial one
         */
        vec[0].iov_len = iv->iov_len-amount;
        vec[0].iov_base =((unsigned char *)iv->iov_base)+amount;
        iv++;

        len = vec[0].iov_len;

        /*
         *      And copy any others
         */
        for (i = 1; i < *num; i++) {
                vec[i] = *iv++;
                len += vec[i].iov_len;
        }

        *data = vec;
        return len;
}

/*
 * smb_receive_header
 * Only called by the smbiod thread.
 */
int
smb_receive_header(struct smb_sb_info *server)
{
        struct socket *sock;
        int result = 0;
        unsigned char peek_buf[4];

        result = -EIO; 
        sock = server_sock(server);
        if (!sock)
                goto out;
        if (sock->sk->sk_state != TCP_ESTABLISHED)
                goto out;

        if (!server->smb_read) {
                result = smb_get_length(sock, peek_buf);
                if (result < 0) {
                        if (result == -ENODATA)
                                result = 0;
                        goto out;
                }
                server->smb_len = result + 4;

                if (server->smb_len < SMB_HEADER_LEN) {
                        PARANOIA("short packet: %d\n", result);
                        server->rstate = SMB_RECV_DROP;
                        result = -EIO;
                        goto out;
                }
                if (server->smb_len > SMB_MAX_PACKET_SIZE) {
                        PARANOIA("long packet: %d\n", result);
                        server->rstate = SMB_RECV_DROP;
                        result = -EIO;
                        goto out;
                }
        }

        result = _recvfrom(sock, server->header + server->smb_read,
                           SMB_HEADER_LEN - server->smb_read, 0);
        VERBOSE("_recvfrom: %d\n", result);
        if (result < 0) {
                VERBOSE("receive error: %d\n", result);
                goto out;
        }
        server->smb_read += result;

        if (server->smb_read == SMB_HEADER_LEN)
                server->rstate = SMB_RECV_HCOMPLETE;
out:
        return result;
}

static char drop_buffer[PAGE_SIZE];

/*
 * smb_receive_drop - read and throw away the data
 * Only called by the smbiod thread.
 *
 * FIXME: we are in the kernel, could we just tell the socket that we want
 * to drop stuff from the buffer?
 */
int
smb_receive_drop(struct smb_sb_info *server)
{
        struct socket *sock;
        unsigned int flags;
        struct kvec iov;
        struct msghdr msg;
        int rlen = smb_len(server->header) - server->smb_read + 4;
        int result = -EIO;

        if (rlen > PAGE_SIZE)
                rlen = PAGE_SIZE;

        sock = server_sock(server);
        if (!sock)
                goto out;
        if (sock->sk->sk_state != TCP_ESTABLISHED)
                goto out;

        flags = MSG_DONTWAIT | MSG_NOSIGNAL;
        iov.iov_base = drop_buffer;
        iov.iov_len = PAGE_SIZE;
        msg.msg_flags = flags;
        msg.msg_name = NULL;
        msg.msg_namelen = 0;
        msg.msg_control = NULL;

        result = kernel_recvmsg(sock, &msg, &iov, 1, rlen, flags);

        VERBOSE("read: %d\n", result);
        if (result < 0) {
                VERBOSE("receive error: %d\n", result);
                goto out;
        }
        server->smb_read += result;

        if (server->smb_read >= server->smb_len)
                server->rstate = SMB_RECV_END;

out:
        return result;
}

/*
 * smb_receive
 * Only called by the smbiod thread.
 */
int
smb_receive(struct smb_sb_info *server, struct smb_request *req)
{
        struct socket *sock;
        unsigned int flags;
        struct kvec iov[4];
        struct kvec *p = req->rq_iov;
        size_t num = req->rq_iovlen;
        struct msghdr msg;
        int rlen;
        int result = -EIO;

        sock = server_sock(server);
        if (!sock)
                goto out;
        if (sock->sk->sk_state != TCP_ESTABLISHED)
                goto out;

        flags = MSG_DONTWAIT | MSG_NOSIGNAL;
        msg.msg_flags = flags;
        msg.msg_name = NULL;
        msg.msg_namelen = 0;
        msg.msg_control = NULL;

        /* Dont repeat bytes and count available bufferspace */
        rlen = min_t(int, smb_move_iov(&p, &num, iov, req->rq_bytes_recvd),
                        (req->rq_rlen - req->rq_bytes_recvd));

        result = kernel_recvmsg(sock, &msg, p, num, rlen, flags);

        VERBOSE("read: %d\n", result);
        if (result < 0) {
                VERBOSE("receive error: %d\n", result);
                goto out;
        }
        req->rq_bytes_recvd += result;
        server->smb_read += result;

out:
        return result;
}

/*
 * Try to send a SMB request. This may return after sending only parts of the
 * request. SMB_REQ_TRANSMITTED will be set if a request was fully sent.
 *
 * Parts of this was taken from xprt_sendmsg from net/sunrpc/xprt.c
 */
int
smb_send_request(struct smb_request *req)
{
        struct smb_sb_info *server = req->rq_server;
        struct socket *sock;
        struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT};
        int slen = req->rq_slen - req->rq_bytes_sent;
        int result = -EIO;
        struct kvec iov[4];
        struct kvec *p = req->rq_iov;
        size_t num = req->rq_iovlen;

        sock = server_sock(server);
        if (!sock)
                goto out;
        if (sock->sk->sk_state != TCP_ESTABLISHED)
                goto out;

        /* Dont repeat bytes */
        if (req->rq_bytes_sent)
                smb_move_iov(&p, &num, iov, req->rq_bytes_sent);

        result = kernel_sendmsg(sock, &msg, p, num, slen);

        if (result >= 0) {
                req->rq_bytes_sent += result;
                if (req->rq_bytes_sent >= req->rq_slen)
                        req->rq_flags |= SMB_REQ_TRANSMITTED;
        }
out:
        return result;
}

/* [<][>][^][v][top][bottom][index][help] */

[funini.com] -> [kei@sodan] -> Kernel Reading