patch-2.1.14 linux/fs/smbfs/sock.c

Next file: linux/include/asm-alpha/termbits.h
Previous file: linux/fs/smbfs/proc.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.13/linux/fs/smbfs/sock.c linux/fs/smbfs/sock.c
@@ -1,7 +1,7 @@
 /*
  *  sock.c
  *
- *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *  Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke
  *
  */
 
@@ -11,7 +11,6 @@
 #include <linux/socket.h>
 #include <linux/fcntl.h>
 #include <linux/stat.h>
-#include <asm/uaccess.h>
 #include <linux/in.h>
 #include <linux/net.h>
 #include <linux/mm.h>
@@ -21,6 +20,7 @@
 #include <linux/smb.h>
 #include <linux/smbno.h>
 
+#include <asm/uaccess.h>
 
 #define _S(nr) (1<<((nr)-1))
 
@@ -28,747 +28,687 @@
 _recvfrom(struct socket *sock, unsigned char *ubuf, int size,
 	  int noblock, unsigned flags, struct sockaddr_in *sa, int *addr_len)
 {
-        struct iovec iov;
-        struct msghdr msg;
+	struct iovec iov;
+	struct msghdr msg;
 
-        iov.iov_base = ubuf;
-        iov.iov_len  = size;
+	iov.iov_base = ubuf;
+	iov.iov_len = size;
 
-        msg.msg_name      = (void *)sa;
-        msg.msg_namelen   = 0;
-        if (addr_len)
-                msg.msg_namelen = *addr_len;
-        msg.msg_control = NULL;
-        msg.msg_iov       = &iov;
-        msg.msg_iovlen    = 1;
+	msg.msg_name = (void *) sa;
+	msg.msg_namelen = 0;
+	if (addr_len)
+		msg.msg_namelen = *addr_len;
+	msg.msg_control = NULL;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
 
-        return sock->ops->recvmsg(sock, &msg, size, noblock, flags, addr_len);
+	return sock->ops->recvmsg(sock, &msg, size, noblock, flags, addr_len);
 }
 
 static int
 _send(struct socket *sock, const void *buff, int len,
       int nonblock, unsigned flags)
 {
-        struct iovec iov;
-        struct msghdr msg;
+	struct iovec iov;
+	struct msghdr msg;
 
-        iov.iov_base = (void *)buff;
-        iov.iov_len  = len;
+	iov.iov_base = (void *) buff;
+	iov.iov_len = len;
 
-        msg.msg_name      = NULL;
-        msg.msg_namelen   = 0;
-        msg.msg_control = NULL;
-        msg.msg_iov       = &iov;
-        msg.msg_iovlen    = 1;
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+	msg.msg_control = NULL;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
 
-        return sock->ops->sendmsg(sock, &msg, len, nonblock, flags);
+	return sock->ops->sendmsg(sock, &msg, len, nonblock, flags);
 }
 
 static void
-smb_data_callback(struct sock *sk,int len)
+smb_data_callback(struct sock *sk, int len)
 {
-        struct socket *sock = sk->socket;
+	struct socket *sock = sk->socket;
 
-	if(!sk->dead)
+	if (!sk->dead)
 	{
-                unsigned char peek_buf[4];
-                int result;
-                unsigned short fs;
-
-                fs = get_fs();
-                set_fs(get_ds());
-
-		result = _recvfrom(sock, (void *)peek_buf, 1, 1,
-                                             MSG_PEEK, NULL, NULL);
-
-                while ((result != -EAGAIN) && (peek_buf[0] == 0x85)) {
-
-                        /* got SESSION KEEP ALIVE */
-                        result = _recvfrom(sock, (void *)peek_buf,
-                                                     4, 1, 0, NULL, NULL);
-
-                        DDPRINTK("smb_data_callback:"
-                                 " got SESSION KEEP ALIVE\n");
-
-                        if (result == -EAGAIN)
-                                break;
+		unsigned char peek_buf[4];
+		int result;
+		unsigned short fs;
+
+		fs = get_fs();
+		set_fs(get_ds());
+
+		result = _recvfrom(sock, (void *) peek_buf, 1, 1,
+				   MSG_PEEK, NULL, NULL);
+
+		while ((result != -EAGAIN) && (peek_buf[0] == 0x85))
+		{
+			/* got SESSION KEEP ALIVE */
+			result = _recvfrom(sock, (void *) peek_buf,
+					   4, 1, 0, NULL, NULL);
+
+			DDPRINTK("smb_data_callback:"
+				 " got SESSION KEEP ALIVE\n");
+
+			if (result == -EAGAIN)
+			{
+				break;
+			}
+			result = _recvfrom(sock, (void *) peek_buf,
+					   1, 1, MSG_PEEK,
+					   NULL, NULL);
+		}
+		set_fs(fs);
+
+		if (result != -EAGAIN)
+		{
+			wake_up_interruptible(sk->sleep);
+		}
+	}
+}
 
-                        result = _recvfrom(sock, (void *)peek_buf,
-                                                     1, 1, MSG_PEEK,
-                                                     NULL, NULL);
+int
+smb_catch_keepalive(struct smb_server *server)
+{
+	struct file *file;
+	struct inode *inode;
+	struct socket *sock;
+	struct sock *sk;
+
+	if ((server == NULL)
+	    || ((file = server->sock_file) == NULL)
+	    || ((inode = file->f_inode) == NULL)
+	    || (!S_ISSOCK(inode->i_mode)))
+	{
+		printk("smb_catch_keepalive: did not get valid server!\n");
+		server->data_ready = NULL;
+		return -EINVAL;
+	}
+	sock = &(inode->u.socket_i);
 
-                }
+	if (sock->type != SOCK_STREAM)
+	{
+		printk("smb_catch_keepalive: did not get SOCK_STREAM\n");
+		server->data_ready = NULL;
+		return -EINVAL;
+	}
+	sk = (struct sock *) (sock->data);
 
-                set_fs(fs);
+	if (sk == NULL)
+	{
+		printk("smb_catch_keepalive: sk == NULL");
+		server->data_ready = NULL;
+		return -EINVAL;
+	}
+	DDPRINTK("smb_catch_keepalive.: sk->d_r = %x, server->d_r = %x\n",
+		 (unsigned int) (sk->data_ready),
+		 (unsigned int) (server->data_ready));
 
-                if (result != -EAGAIN) {
-                        wake_up_interruptible(sk->sleep);
-                }
+	if (sk->data_ready == smb_data_callback)
+	{
+		printk("smb_catch_keepalive: already done\n");
+		return -EINVAL;
 	}
+	server->data_ready = sk->data_ready;
+	sk->data_ready = smb_data_callback;
+	return 0;
 }
 
 int
-smb_catch_keepalive(struct smb_server *server)
+smb_dont_catch_keepalive(struct smb_server *server)
 {
-        struct file   *file;
-        struct inode  *inode;
-        struct socket *sock;
-        struct sock   *sk;
-
-        if (   (server == NULL)
-            || ((file  = server->sock_file) == NULL)
-            || ((inode = file->f_inode) == NULL)
-            || (!S_ISSOCK(inode->i_mode))) {
-
-                printk("smb_catch_keepalive: did not get valid server!\n");
-                server->data_ready = NULL;
-                return -EINVAL;
-        }
-
-        sock = &(inode->u.socket_i);
-
-        if (sock->type != SOCK_STREAM) {
-                printk("smb_catch_keepalive: did not get SOCK_STREAM\n");
-                server->data_ready = NULL;
-                return -EINVAL;
-        }
-
-        sk   = (struct sock *)(sock->data);
-
-        if (sk == NULL) {
-                printk("smb_catch_keepalive: sk == NULL");
-                server->data_ready = NULL;
-                return -EINVAL;
-        }
-
-        DDPRINTK("smb_catch_keepalive.: sk->d_r = %x, server->d_r = %x\n",
-                 (unsigned int)(sk->data_ready),
-                 (unsigned int)(server->data_ready));
-
-        if (sk->data_ready == smb_data_callback) {
-                printk("smb_catch_keepalive: already done\n");
-                return -EINVAL;
-        }
-
-        server->data_ready = sk->data_ready;
-        sk->data_ready = smb_data_callback;
-        return 0;
+	struct file *file;
+	struct inode *inode;
+	struct socket *sock;
+	struct sock *sk;
+
+	if ((server == NULL)
+	    || ((file = server->sock_file) == NULL)
+	    || ((inode = file->f_inode) == NULL)
+	    || (!S_ISSOCK(inode->i_mode)))
+	{
+		printk("smb_dont_catch_keepalive: "
+		       "did not get valid server!\n");
+		return -EINVAL;
+	}
+	sock = &(inode->u.socket_i);
+
+	if (sock->type != SOCK_STREAM)
+	{
+		printk("smb_dont_catch_keepalive: did not get SOCK_STREAM\n");
+		return -EINVAL;
+	}
+	sk = (struct sock *) (sock->data);
+
+	if (sk == NULL)
+	{
+		printk("smb_dont_catch_keepalive: sk == NULL");
+		return -EINVAL;
+	}
+	if (server->data_ready == NULL)
+	{
+		printk("smb_dont_catch_keepalive: "
+		       "server->data_ready == NULL\n");
+		return -EINVAL;
+	}
+	if (sk->data_ready != smb_data_callback)
+	{
+		printk("smb_dont_catch_keepalive: "
+		       "sk->data_callback != smb_data_callback\n");
+		return -EINVAL;
+	}
+	DDPRINTK("smb_dont_catch_keepalive: sk->d_r = %x, server->d_r = %x\n",
+		 (unsigned int) (sk->data_ready),
+		 (unsigned int) (server->data_ready));
+
+	sk->data_ready = server->data_ready;
+	server->data_ready = NULL;
+	return 0;
 }
-                
-int
-smb_dont_catch_keepalive(struct smb_server *server)
+
+static int
+smb_send_raw(struct socket *sock, unsigned char *source, int length)
 {
-        struct file   *file;
-        struct inode  *inode;
-        struct socket *sock;
-        struct sock   *sk;
-
-        if (   (server == NULL)
-            || ((file  = server->sock_file) == NULL)
-            || ((inode = file->f_inode) == NULL)
-            || (!S_ISSOCK(inode->i_mode))) {
-
-                printk("smb_dont_catch_keepalive: "
-                       "did not get valid server!\n");
-                return -EINVAL;
-        }
-
-        sock = &(inode->u.socket_i);
-
-        if (sock->type != SOCK_STREAM) {
-                printk("smb_dont_catch_keepalive: did not get SOCK_STREAM\n");
-                return -EINVAL;
-        }
-
-        sk   = (struct sock *)(sock->data);
-
-        if (sk == NULL) {
-                printk("smb_dont_catch_keepalive: sk == NULL");
-                return -EINVAL;
-        }
-
-        if (server->data_ready == NULL) {
-                printk("smb_dont_catch_keepalive: "
-                       "server->data_ready == NULL\n");
-                return -EINVAL;
-        }
-
-        if (sk->data_ready != smb_data_callback) {
-                printk("smb_dont_catch_keepalive: "
-                       "sk->data_callback != smb_data_callback\n");
-                return -EINVAL;
-        }
-
-        DDPRINTK("smb_dont_catch_keepalive: sk->d_r = %x, server->d_r = %x\n",
-                 (unsigned int)(sk->data_ready),
-                 (unsigned int)(server->data_ready));
-
-        sk->data_ready = server->data_ready;
-        server->data_ready = NULL;
-        return 0;
+	int result;
+	int already_sent = 0;
+
+	while (already_sent < length)
+	{
+		result = _send(sock,
+			       (void *) (source + already_sent),
+			       length - already_sent, 0, 0);
+
+		if (result < 0)
+		{
+			DPRINTK("smb_send_raw: sendto error = %d\n",
+				-result);
+			return result;
+		}
+		already_sent += result;
+	}
+	return already_sent;
 }
 
-/*
- * smb_receive_raw
- * fs points to the correct segment, sock != NULL, target != NULL
- * The smb header is only stored if want_header != 0.
- */
 static int
-smb_receive_raw(struct socket *sock, unsigned char *target,
-                int max_raw_length, int want_header)
+smb_receive_raw(struct socket *sock, unsigned char *target, int length)
 {
-        int len, result;
-        int already_read;
-        unsigned char peek_buf[4];
-        unsigned short fs;      /* We fool the kernel to believe
-                                   we call from user space. */
+	int result;
+	int already_read = 0;
 
+	while (already_read < length)
+	{
+		result = _recvfrom(sock,
+				   (void *) (target + already_read),
+				   length - already_read, 0, 0,
+				   NULL, NULL);
+
+		if (result < 0)
+		{
+			DPRINTK("smb_receive_raw: recvfrom error = %d\n",
+				-result);
+			return result;
+		}
+		already_read += result;
+	}
+	return already_read;
+}
 
- re_recv:
+static int
+smb_get_length(struct socket *sock, unsigned char *header)
+{
+	int result;
+	unsigned char peek_buf[4];
+	unsigned short fs;
 
+      re_recv:
 	fs = get_fs();
 	set_fs(get_ds());
-        result = _recvfrom(sock, (void *)peek_buf, 4, 0,
-                                     0, NULL, NULL);
-        set_fs(fs);
-
-        if (result < 0) {
-                DPRINTK("smb_receive_raw: recv error = %d\n", -result);
-                return result;
-        }
-
-        if (result < 4) {
-                DPRINTK("smb_receive_raw: got less than 4 bytes\n");
-                return -EIO;
-        }
-
-        switch (peek_buf[0]) {
-
-        case 0x00:
-        case 0x82:
-                break;
-
-        case 0x85:
-                DPRINTK("smb_receive_raw: Got SESSION KEEP ALIVE\n");
-                goto re_recv;
-                
-        default:
-                printk("smb_receive_raw: Invalid packet\n");
-                return -EIO;
-        }
-
-        /* The length in the RFC NB header is the raw data length */
-	len = smb_len(peek_buf); 
-        if (len > max_raw_length) { 
-                printk("smb_receive_raw: Received length (%d) > max_xmit (%d)!\n", 
-		       len, max_raw_length);
-                return -EIO;
-	}
-
-        if (want_header != 0) {
-                copy_to_user(target, peek_buf, 4);
-                target += 4;
-        }
-
-        already_read = 0;
-
-        while (already_read < len) {
-                
-                result = _recvfrom(sock,
-                                 (void *)(target+already_read),
-                                 len - already_read, 0, 0,
-                                 NULL, NULL);
-   
-                if (result < 0) {
-                        printk("smb_receive_raw: recvfrom error = %d\n",
-                               -result);
-                        return result;
-                }
-
-                already_read += result;
-        }
-        return already_read;
+	result = smb_receive_raw(sock, peek_buf, 4);
+	set_fs(fs);
+
+	if (result < 0)
+	{
+		DPRINTK("smb_get_length: recv error = %d\n", -result);
+		return result;
+	}
+	switch (peek_buf[0])
+	{
+	case 0x00:
+	case 0x82:
+		break;
+
+	case 0x85:
+		DPRINTK("smb_get_length: Got SESSION KEEP ALIVE\n");
+		goto re_recv;
+
+	default:
+		printk("smb_get_length: Invalid NBT packet\n");
+		return -EIO;
+	}
+
+	if (header != NULL)
+	{
+		memcpy(header, peek_buf, 4);
+	}
+	/* The length in the RFC NB header is the raw data length */
+	return smb_len(peek_buf);
+}
+
+static struct socket *
+server_sock(struct smb_server *server)
+{
+	struct file *file;
+	struct inode *inode;
+
+	if (server == NULL)
+		return NULL;
+	if ((file = server->sock_file) == NULL)
+		return NULL;
+	if ((inode = file->f_inode) == NULL)
+		return NULL;
+	return &(inode->u.socket_i);
 }
 
 /*
  * smb_receive
- * fs points to the correct segment, server != NULL, sock!=NULL
+ * fs points to the correct segment
  */
 static int
-smb_receive(struct smb_server *server, struct socket *sock)
+smb_receive(struct smb_server *server)
 {
-        int result;
-
-        result = smb_receive_raw(sock, server->packet,
-                                 server->max_xmit - 4, /* max_xmit in server
-                                                          includes NB header */
-                                 1); /* We want the header */
+	struct socket *sock = server_sock(server);
+	int len;
+	int result;
+	unsigned char peek_buf[4];
 
-        if (result < 0) {
-                printk("smb_receive: receive error: %d\n", result);
-                return result;
-        }
+	len = smb_get_length(sock, peek_buf);
 
-        server->rcls = *((unsigned char *)(server->packet+9));
-        server->err  = *((unsigned short *)(server->packet+11));
+	if (len < 0)
+	{
+		return len;
+	}
+	if (len + 4 > server->packet_size)
+	{
+		/* Some servers do not care about our max_xmit. They
+		   send larger packets */
+		DPRINTK("smb_receive: Increase packet size from %d to %d\n",
+			server->packet_size, len + 4);
+		smb_vfree(server->packet);
+		server->packet_size = 0;
+		server->packet = smb_vmalloc(len + 4);
+		if (server->packet == NULL)
+		{
+			return -ENOMEM;
+		}
+		server->packet_size = len + 4;
+	}
+	memcpy(server->packet, peek_buf, 4);
+	result = smb_receive_raw(sock, server->packet + 4, len);
 
-        if (server->rcls != 0) {
-                DPRINTK("smb_receive: rcls=%d, err=%d\n",
-                        server->rcls, server->err);
-        }
+	if (result < 0)
+	{
+		printk("smb_receive: receive error: %d\n", result);
+		return result;
+	}
+	server->rcls = BVAL(server->packet, 9);
+	server->err = WVAL(server->packet, 11);
 
-        return result;
+	if (server->rcls != 0)
+	{
+		DPRINTK("smb_receive: rcls=%d, err=%d\n",
+			server->rcls, server->err);
+	}
+	return result;
 }
 
-
-/*
- * smb_receive's preconditions also apply here.
- */
 static int
-smb_receive_trans2(struct smb_server *server, struct socket *sock,
-                   int *data_len, int *param_len,
-                   char **data, char **param)
-{
-        int total_data=0;
-        int total_param=0;
-        int result;
-        unsigned char *inbuf = server->packet;
-
-        *data_len = *param_len = 0;
-
-        DDPRINTK("smb_receive_trans2: enter\n");
-        
-        if ((result = smb_receive(server, sock)) < 0) {
-                return result;
-        }
-
-        if (server->rcls != 0) {
-                return result;
-        }
-
-        /* parse out the lengths */
-        total_data = WVAL(inbuf,smb_tdrcnt);
-        total_param = WVAL(inbuf,smb_tprcnt);
-
-        if (   (total_data  > TRANS2_MAX_TRANSFER)
-            || (total_param > TRANS2_MAX_TRANSFER)) {
-                printk("smb_receive_trans2: data/param too long\n");
-                return -EIO;
-        }
-
-        /* allocate it */
-        if ((*data  = smb_kmalloc(total_data, GFP_KERNEL)) == NULL) {
-                printk("smb_receive_trans2: could not alloc data area\n");
-                return -ENOMEM;
-        }
-
-        if ((*param = smb_kmalloc(total_param, GFP_KERNEL)) == NULL) {
-                printk("smb_receive_trans2: could not alloc param area\n");
-                smb_kfree_s(*data, total_data);
-                return -ENOMEM;
-        }
-
-        DDPRINTK("smb_rec_trans2: total_data/param: %d/%d\n",
-                 total_data, total_param);
-
-        while (1)
-        {
-                if (WVAL(inbuf,smb_prdisp)+WVAL(inbuf, smb_prcnt)
-                    > total_param) {
-                        printk("smb_receive_trans2: invalid parameters\n");
-                        result = -EIO;
-                        goto fail;
-                }
-                memcpy(*param + WVAL(inbuf,smb_prdisp),
-                       smb_base(inbuf) + WVAL(inbuf,smb_proff),
-                       WVAL(inbuf,smb_prcnt));
-                *param_len += WVAL(inbuf,smb_prcnt);
-
-
-                if (WVAL(inbuf,smb_drdisp)+WVAL(inbuf, smb_drcnt)>total_data) {
-                        printk("smb_receive_trans2: invalid data block\n");
-                        result = -EIO;
-                        goto fail;
-                }
-                memcpy(*data + WVAL(inbuf,smb_drdisp),
-                       smb_base(inbuf) + WVAL(inbuf,smb_droff),
-                       WVAL(inbuf,smb_drcnt));
-                *data_len += WVAL(inbuf,smb_drcnt);
-
-                DDPRINTK("smb_rec_trans2: drcnt/prcnt: %d/%d\n",
-                         WVAL(inbuf, smb_drcnt), WVAL(inbuf, smb_prcnt));
-
-                /* parse out the total lengths again - they can shrink! */
-
-                if (   (WVAL(inbuf,smb_tdrcnt) > total_data)
-                    || (WVAL(inbuf,smb_tprcnt) > total_param)) {
-                        printk("smb_receive_trans2: data/params grew!\n");
-                        result = -EIO;
-                        goto fail;
-                }
-
-                total_data = WVAL(inbuf,smb_tdrcnt);
-                total_param = WVAL(inbuf,smb_tprcnt);
-
-                if (total_data <= *data_len && total_param <= *param_len)
-                        break;
-
-                if ((result = smb_receive(server, sock)) < 0) {
-                        goto fail;
-                }
-                if (server->rcls != 0) {
-                        result = -EIO;
-                        goto fail;
-                }
-        }
-
-        DDPRINTK("smb_receive_trans2: normal exit\n");
-
-        return 0;
-
- fail:
-        DPRINTK("smb_receive_trans2: failed exit\n");
-
-        smb_kfree_s(*param, 0); *param = NULL;
-        smb_kfree_s(*data, 0);  *data = NULL;
-        return result;
-}
+smb_receive_trans2(struct smb_server *server,
+		   int *ldata, unsigned char **data,
+		   int *lparam, unsigned char **param)
+{
+	int total_data = 0;
+	int total_param = 0;
+	int result;
+	unsigned char *inbuf = server->packet;
+	unsigned char *rcv_buf;
+	int buf_len;
+	int data_len = 0;
+	int param_len = 0;
 
-static inline struct socket *
-server_sock(struct smb_server *server)
-{
-        struct file *file;
-        struct inode *inode;
+	if ((result = smb_receive(server)) < 0)
+	{
+		return result;
+	}
+	if (server->rcls != 0)
+	{
+		*param = *data = server->packet;
+		*ldata = *lparam = 0;
+		return 0;
+	}
+	total_data = WVAL(inbuf, smb_tdrcnt);
+	total_param = WVAL(inbuf, smb_tprcnt);
+
+	DDPRINTK("smb_receive_trans2: td=%d,tp=%d\n", total_data, total_param);
+
+	if ((total_data > TRANS2_MAX_TRANSFER)
+	    || (total_param > TRANS2_MAX_TRANSFER))
+	{
+		DPRINTK("smb_receive_trans2: data/param too long\n");
+		return -EIO;
+	}
+	buf_len = total_data + total_param;
+	if (server->packet_size > buf_len)
+	{
+		buf_len = server->packet_size;
+	}
+	if ((rcv_buf = smb_vmalloc(buf_len)) == NULL)
+	{
+		DPRINTK("smb_receive_trans2: could not alloc data area\n");
+		return -ENOMEM;
+	}
+	*param = rcv_buf;
+	*data = rcv_buf + total_param;
+
+	while (1)
+	{
+		if (WVAL(inbuf, smb_prdisp) + WVAL(inbuf, smb_prcnt)
+		    > total_param)
+		{
+			DPRINTK("smb_receive_trans2: invalid parameters\n");
+			result = -EIO;
+			goto fail;
+		}
+		memcpy(*param + WVAL(inbuf, smb_prdisp),
+		       smb_base(inbuf) + WVAL(inbuf, smb_proff),
+		       WVAL(inbuf, smb_prcnt));
+		param_len += WVAL(inbuf, smb_prcnt);
+
+		if (WVAL(inbuf, smb_drdisp) + WVAL(inbuf, smb_drcnt)
+		    > total_data)
+		{
+			DPRINTK("smb_receive_trans2: invalid data block\n");
+			result = -EIO;
+			goto fail;
+		}
+		DDPRINTK("target: %X\n", *data + WVAL(inbuf, smb_drdisp));
+		DDPRINTK("source: %X\n",
+			 smb_base(inbuf) + WVAL(inbuf, smb_droff));
+		DDPRINTK("disp: %d, off: %d, cnt: %d\n",
+			 WVAL(inbuf, smb_drdisp), WVAL(inbuf, smb_droff),
+			 WVAL(inbuf, smb_drcnt));
+
+		memcpy(*data + WVAL(inbuf, smb_drdisp),
+		       smb_base(inbuf) + WVAL(inbuf, smb_droff),
+		       WVAL(inbuf, smb_drcnt));
+		data_len += WVAL(inbuf, smb_drcnt);
+
+		if ((WVAL(inbuf, smb_tdrcnt) > total_data)
+		    || (WVAL(inbuf, smb_tprcnt) > total_param))
+		{
+			printk("smb_receive_trans2: data/params grew!\n");
+			result = -EIO;
+			goto fail;
+		}
+		/* the total lengths might shrink! */
+		total_data = WVAL(inbuf, smb_tdrcnt);
+		total_param = WVAL(inbuf, smb_tprcnt);
+
+		if ((data_len >= total_data) && (param_len >= total_param))
+		{
+			break;
+		}
+		if ((result = smb_receive(server)) < 0)
+		{
+			goto fail;
+		}
+		if (server->rcls != 0)
+		{
+			result = -EIO;
+			goto fail;
+		}
+	}
+	*ldata = data_len;
+	*lparam = param_len;
 
-        if (server == NULL)
-                return NULL;
-        if ((file = server->sock_file) == NULL)
-                return NULL;
-        if ((inode = file->f_inode) == NULL)
-                return NULL;
-        return &(inode->u.socket_i);
+	smb_vfree(server->packet);
+	server->packet = rcv_buf;
+	server->packet_size = buf_len;
+	return 0;
+
+      fail:
+	smb_vfree(rcv_buf);
+	return result;
 }
 
 int
 smb_release(struct smb_server *server)
 {
-        struct socket *sock = server_sock(server);
-        int result;
+	struct socket *sock = server_sock(server);
+	int result;
 
-        if (sock == NULL)
-                return -EINVAL;
+	if (sock == NULL)
+	{
+		return -EINVAL;
+	}
+	result = sock->ops->release(sock, NULL);
+	DPRINTK("smb_release: sock->ops->release = %d\n", result);
 
-        result = sock->ops->release(sock, NULL);
-        DPRINTK("smb_release: sock->ops->release = %d\n", result);
+	/* inet_release does not set sock->state.  Maybe someone is
+	   confused about sock->state being SS_CONNECTED while there
+	   is nothing behind it, so I set it to SS_UNCONNECTED. */
+	sock->state = SS_UNCONNECTED;
 
-        /* inet_release does not set sock->state.  Maybe someone is
-           confused about sock->state being SS_CONNECTED while there
-           is nothing behind it, so I set it to SS_UNCONNECTED.*/
-        sock->state = SS_UNCONNECTED;
-
-        result = sock->ops->create(sock, 0);
-        DPRINTK("smb_release: sock->ops->create = %d\n", result);
-        return result;
+	result = sock->ops->create(sock, 0);
+	DPRINTK("smb_release: sock->ops->create = %d\n", result);
+	return result;
 }
 
 int
 smb_connect(struct smb_server *server)
 {
-        struct socket *sock = server_sock(server);
-        if (sock == NULL)
-                return -EINVAL;
-        if (sock->state != SS_UNCONNECTED) {
-                DPRINTK("smb_connect: socket is not unconnected: %d\n",
-                        sock->state);
-        }
-        return sock->ops->connect(sock, (struct sockaddr *)&(server->m.addr),
-                                  sizeof(struct sockaddr_in), 0);
-}
-        
-/*****************************************************************************/
-/*                                                                           */
-/*  This routine was once taken from nfs, which is for udp. Here TCP does     */
-/*  most of the ugly stuff for us (thanks, Alan!)                            */
-/*                                                                           */
-/*****************************************************************************/
-int
-smb_request(struct smb_server *server)
-{
-	unsigned long old_mask;
-	unsigned short fs;      /* We fool the kernel to believe
-                                   we call from user space. */
-	int len, result, result2;
-
 	struct socket *sock = server_sock(server);
-	unsigned char *buffer = (server == NULL) ? NULL : server->packet;
-
-	if ((sock == NULL) || (buffer == NULL)) {
-		printk("smb_request: Bad server!\n");
-		return -EBADF;
+	if (sock == NULL)
+	{
+		return -EINVAL;
 	}
-
-        if (server->state != CONN_VALID)
-                return -EIO;
-        	
-        if ((result = smb_dont_catch_keepalive(server)) != 0) {
-                server->state = CONN_INVALID;
-                smb_invalidate_all_inodes(server);
-                return result;
-        }
-
-        len = smb_len(buffer) + 4;
-
-        DDPRINTK("smb_request: len = %d cmd = 0x%X\n", len, buffer[8]);
-
-	old_mask = current->blocked;
-	current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP));
-	fs = get_fs();
-	set_fs(get_ds());
-
-        result = _send(sock, (void *)buffer, len, 0, 0);
-        if (result < 0) {
-                printk("smb_request: send error = %d\n", result);
-        }
-        else {
-                result = smb_receive(server, sock);
-        }
-
-        /* read/write errors are handled by errno */
-        current->signal &= ~_S(SIGPIPE);
-
-	current->blocked = old_mask;
-	set_fs(fs);
-
-        if ((result2 = smb_catch_keepalive(server)) < 0) {
-                result = result2;
-        }
-
-        if (result < 0) {
-                server->state = CONN_INVALID;
-                smb_invalidate_all_inodes(server);
-        }
-        
-        DDPRINTK("smb_request: result = %d\n", result);
-
-	return result;
+	if (sock->state != SS_UNCONNECTED)
+	{
+		DPRINTK("smb_connect: socket is not unconnected: %d\n",
+			sock->state);
+	}
+	return sock->ops->connect(sock, (struct sockaddr *) &(server->m.addr),
+				  sizeof(struct sockaddr_in), 0);
 }
 
-/*
- * This is not really a trans2 request, we assume that you only have
- * one packet to send.
- */ 
 int
-smb_trans2_request(struct smb_server *server,
-                   int *data_len, int *param_len,
-                   char **data, char **param)
+smb_request(struct smb_server *server)
 {
 	unsigned long old_mask;
-	unsigned short fs;      /* We fool the kernel to believe
-                                   we call from user space. */
-	int len, result, result2;
+	unsigned short fs;
+	int len, result;
 
-	struct socket *sock = server_sock(server);
 	unsigned char *buffer = (server == NULL) ? NULL : server->packet;
 
-	if ((sock == NULL) || (buffer == NULL)) {
-		printk("smb_trans2_request: Bad server!\n");
+	if (buffer == NULL)
+	{
+		printk("smb_request: Bad server!\n");
 		return -EBADF;
 	}
+	if (server->state != CONN_VALID)
+	{
+		return -EIO;
+	}
+	if ((result = smb_dont_catch_keepalive(server)) != 0)
+	{
+		server->state = CONN_INVALID;
+		smb_invalidate_all_inodes(server);
+		return result;
+	}
+	len = smb_len(buffer) + 4;
 
-        if (server->state != CONN_VALID)
-                return -EIO;
-        	
-        if ((result = smb_dont_catch_keepalive(server)) != 0) {
-                server->state = CONN_INVALID;
-                smb_invalidate_all_inodes(server);
-                return result;
-        }
-
-        len = smb_len(buffer) + 4;
+	DDPRINTK("smb_request: len = %d cmd = 0x%X\n", len, buffer[8]);
 
 	old_mask = current->blocked;
 	current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP));
 	fs = get_fs();
 	set_fs(get_ds());
 
-        DDPRINTK("smb_request: len = %d cmd = 0x%X\n", len, buffer[8]);
-
-        result = _send(sock, (void *)buffer, len, 0, 0);
-        if (result < 0) {
-                printk("smb_trans2_request: send error = %d\n", result);
-        }
-        else {
-                result = smb_receive_trans2(server, sock,
-                                            data_len, param_len,
-                                            data, param);
-        }
-
-        /* read/write errors are handled by errno */
-        current->signal &= ~_S(SIGPIPE);
-
+	result = smb_send_raw(server_sock(server), (void *) buffer, len);
+	if (result > 0)
+	{
+		result = smb_receive(server);
+	}
+	/* read/write errors are handled by errno */
+	current->signal &= ~_S(SIGPIPE);
 	current->blocked = old_mask;
 	set_fs(fs);
 
-        if ((result2 = smb_catch_keepalive(server)) < 0) {
-                result = result2;
-        }
-
-        if (result < 0) {
-                server->state = CONN_INVALID;
-                smb_invalidate_all_inodes(server);
-        }
-        
-        DDPRINTK("smb_trans2_request: result = %d\n", result);
+	if (result >= 0)
+	{
+		int result2 = smb_catch_keepalive(server);
+		if (result2 < 0)
+		{
+			result = result2;
+		}
+	}
+	if (result < 0)
+	{
+		server->state = CONN_INVALID;
+		smb_invalidate_all_inodes(server);
+	}
+	DDPRINTK("smb_request: result = %d\n", result);
 
 	return result;
 }
 
-/* target must be in user space */
-int
-smb_request_read_raw(struct smb_server *server,
-                     unsigned char *target, int max_len)
+#define ROUND_UP(x) (((x)+3) & ~3)
+static int
+smb_send_trans2(struct smb_server *server, __u16 trans2_command,
+		int ldata, unsigned char *data,
+		int lparam, unsigned char *param)
 {
-	unsigned long old_mask;
-	int len, result, result2;
-	unsigned short fs;      /* We fool the kernel to believe
-                                   we call from user space. */
-
 	struct socket *sock = server_sock(server);
-	unsigned char *buffer = (server == NULL) ? NULL : server->packet;
 
-	if ((sock == NULL) || (buffer == NULL)) {
-		printk("smb_request_read_raw: Bad server!\n");
-		return -EBADF;
-	}
+	/* I know the following is very ugly, but I want to build the
+	   smb packet as efficiently as possible. */
 
-        if (server->state != CONN_VALID)
-                return -EIO;
-        	
-        if ((result = smb_dont_catch_keepalive(server)) != 0) {
-                server->state = CONN_INVALID;
-                smb_invalidate_all_inodes(server);
-                return result;
-        }
+	const int smb_parameters = 15;
+	const int oparam =
+	ROUND_UP(SMB_HEADER_LEN + 2 * smb_parameters + 2 + 3);
+	const int odata =
+	ROUND_UP(oparam + lparam);
+	const int bcc =
+	odata + ldata - (SMB_HEADER_LEN + 2 * smb_parameters + 2);
+	const int packet_length =
+	SMB_HEADER_LEN + 2 * smb_parameters + bcc + 2;
+
+	unsigned char padding[4] =
+	{0,};
+	char *p;
 
-        len = smb_len(buffer) + 4;
+	struct iovec iov[4];
+	struct msghdr msg;
 
-	old_mask = current->blocked;
-	current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP));
-	fs = get_fs();
-	set_fs(get_ds());
-
-        DPRINTK("smb_request_read_raw: len = %d cmd = 0x%X\n",
-                len, buffer[8]);
-        DPRINTK("smb_request_read_raw: target=%X, max_len=%d\n",
-                (unsigned int)target, max_len);
-        DPRINTK("smb_request_read_raw: buffer=%X, sock=%X\n",
-                (unsigned int)buffer, (unsigned int)sock);
-
-        result = _send(sock, (void *)buffer, len, 0, 0);
-
-        DPRINTK("smb_request_read_raw: send returned %d\n", result);
-
-	set_fs(fs);             /* We recv into user space */
-
-        if (result < 0) {
-                printk("smb_request_read_raw: send error = %d\n", result);
-        }
-        else {
-                result = smb_receive_raw(sock, target, max_len, 0);
-        }
-
-        /* read/write errors are handled by errno */
-        current->signal &= ~_S(SIGPIPE);
-	current->blocked = old_mask;
+	if ((bcc + oparam) > server->max_xmit)
+	{
+		return -ENOMEM;
+	}
+	p = smb_setup_header(server, SMBtrans2, smb_parameters, bcc);
 
-        if ((result2 = smb_catch_keepalive(server)) < 0) {
-                result = result2;
-        }
-
-        if (result < 0) {
-                server->state = CONN_INVALID;
-                smb_invalidate_all_inodes(server);
-        }
-        
-        DPRINTK("smb_request_read_raw: result = %d\n", result);
+	WSET(server->packet, smb_tpscnt, lparam);
+	WSET(server->packet, smb_tdscnt, ldata);
+	WSET(server->packet, smb_mprcnt, TRANS2_MAX_TRANSFER);
+	WSET(server->packet, smb_mdrcnt, TRANS2_MAX_TRANSFER);
+	WSET(server->packet, smb_msrcnt, 0);
+	WSET(server->packet, smb_flags, 0);
+	DSET(server->packet, smb_timeout, 0);
+	WSET(server->packet, smb_pscnt, lparam);
+	WSET(server->packet, smb_psoff, oparam - 4);
+	WSET(server->packet, smb_dscnt, ldata);
+	WSET(server->packet, smb_dsoff, odata - 4);
+	WSET(server->packet, smb_suwcnt, 1);
+	WSET(server->packet, smb_setup0, trans2_command);
+	*p++ = 0;		/* null smb_name for trans2 */
+	*p++ = 'D';		/* this was added because OS/2 does it */
+	*p++ = ' ';
+
+	iov[0].iov_base = (void *) server->packet;
+	iov[0].iov_len = oparam;
+	iov[1].iov_base = (param == NULL) ? padding : param;
+	iov[1].iov_len = lparam;
+	iov[2].iov_base = padding;
+	iov[2].iov_len = odata - oparam - lparam;
+	iov[3].iov_base = (data == NULL) ? padding : data;
+	iov[3].iov_len = ldata;
+
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+	msg.msg_control = NULL;
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 4;
 
-	return result;
+	return sock->ops->sendmsg(sock, &msg, packet_length, 0, 0);
 }
 
-/* Source must be in user space. smb_request_write_raw assumes that
- * the request SMBwriteBraw has been completed successfully, so that
- * we can send the raw data now.  */
+/*
+ * This is not really a trans2 request, we assume that you only have
+ * one packet to send.
+ */
 int
-smb_request_write_raw(struct smb_server *server,
-                      unsigned const char *source, int length)
+smb_trans2_request(struct smb_server *server, __u16 trans2_command,
+		   int ldata, unsigned char *data,
+		   int lparam, unsigned char *param,
+		   int *lrdata, unsigned char **rdata,
+		   int *lrparam, unsigned char **rparam)
 {
 	unsigned long old_mask;
-	int result, result2;
-	unsigned short fs;      /* We fool the kernel to believe
-                                   we call from user space. */
-        byte nb_header[4];
+	unsigned short fs;
+	int result;
 
-	struct socket *sock = server_sock(server);
-	unsigned char *buffer = (server == NULL) ? NULL : server->packet;
+	DDPRINTK("smb_trans2_request: com=%d, ld=%d, lp=%d\n",
+		 trans2_command, ldata, lparam);
 
-	if ((sock == NULL) || (buffer == NULL)) {
-                printk("smb_request_write_raw: Bad server!\n");
-		return -EBADF;
+	if (server->state != CONN_VALID)
+	{
+		return -EIO;
+	}
+	if ((result = smb_dont_catch_keepalive(server)) != 0)
+	{
+		server->state = CONN_INVALID;
+		smb_invalidate_all_inodes(server);
+		return result;
 	}
-
-        if (server->state != CONN_VALID)
-                return -EIO;
-        	
-        if ((result = smb_dont_catch_keepalive(server)) != 0) {
-                server->state = CONN_INVALID;
-                smb_invalidate_all_inodes(server);
-                return result;
-        }
-
 	old_mask = current->blocked;
 	current->blocked |= ~(_S(SIGKILL) | _S(SIGSTOP));
 	fs = get_fs();
 	set_fs(get_ds());
 
-        smb_encode_smb_length(nb_header, length);
-
-        result = _send(sock, (void *)nb_header, 4, 0, 0);
-
-        if (result == 4) {
-                set_fs(fs);     /* source is in user-land */
-                result = _send(sock, (void *)source, length, 0, 0);
-                set_fs(get_ds());
-        } else {
-                result = -EIO;
-        }
-
-        DPRINTK("smb_request_write_raw: send returned %d\n", result);
-
-        if (result == length) {
-                result = smb_receive(server, sock);
-        } else {
-                result = -EIO;
-        }
-
-        /* read/write errors are handled by errno */
-        current->signal &= ~_S(SIGPIPE);
+	result = smb_send_trans2(server, trans2_command,
+				 ldata, data, lparam, param);
+	if (result >= 0)
+	{
+		result = smb_receive_trans2(server,
+					    lrdata, rdata, lrparam, rparam);
+	}
+	/* read/write errors are handled by errno */
+	current->signal &= ~_S(SIGPIPE);
 	current->blocked = old_mask;
 	set_fs(fs);
 
-        if ((result2 = smb_catch_keepalive(server)) < 0) {
-                result = result2;
-        }
-
-        if (result < 0) {
-                server->state = CONN_INVALID;
-                smb_invalidate_all_inodes(server);
-        }
-
-        if (result > 0) {
-                result = length;
-        }
-        
-        DPRINTK("smb_request_write_raw: result = %d\n", result);
+	if (result >= 0)
+	{
+		int result2 = smb_catch_keepalive(server);
+		if (result2 < 0)
+		{
+			result = result2;
+		}
+	}
+	if (result < 0)
+	{
+		server->state = CONN_INVALID;
+		smb_invalidate_all_inodes(server);
+	}
+	DDPRINTK("smb_trans2_request: result = %d\n", result);
 
 	return result;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov