Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/fs/smbfs/sock.c b/fs/smbfs/sock.c
new file mode 100644
index 0000000..93f3cd2
--- /dev/null
+++ b/fs/smbfs/sock.c
@@ -0,0 +1,388 @@
+/*
+ *  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/tcp.h>
+#include <linux/mm.h>
+#include <linux/netdevice.h>
+#include <linux/smp_lock.h>
+#include <linux/workqueue.h>
+#include <net/scm.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_dentry->d_inode))
+			PARANOIA("bad socket!\n");
+#endif
+		return SOCKET_I(file->f_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 = smb_move_iov(&p, &num, iov, req->rq_bytes_recvd);
+	if (req->rq_rlen < rlen)
+		rlen = req->rq_rlen;
+
+	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;
+}