patch-1.3.53 linux/fs/ncpfs/sock.c

Next file: linux/fs/proc/array.c
Previous file: linux/fs/ncpfs/ncplib_kernel.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.52/linux/fs/ncpfs/sock.c linux/fs/ncpfs/sock.c
@@ -0,0 +1,606 @@
+/*
+ *  linux/fs/ncp/sock.c
+ *
+ *  Copyright (C) 1992, 1993  Rick Sladkey
+ *
+ *  Modified 1995 by Volker Lendecke to be usable for ncp
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/ncp_fs.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <asm/segment.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/mm.h>
+#include <linux/netdevice.h>
+#include <linux/ipx.h>
+
+#include <linux/ncp.h>
+#include <linux/ncp_fs.h>
+#include <linux/ncp_fs_sb.h>
+#include <net/sock.h>
+
+
+#define _S(nr) (1<<((nr)-1))
+static int _recvfrom(struct socket *sock, unsigned char *ubuf,
+		     int size, int noblock, unsigned flags,
+		     struct sockaddr_ipx *sa, int *addr_len)
+{
+        struct iovec iov;
+        struct msghdr msg;
+
+        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_accrights = NULL;
+        msg.msg_iov       = &iov;
+        msg.msg_iovlen    = 1;
+
+        return sock->ops->recvmsg(sock, &msg, size, noblock, flags, addr_len);
+}
+
+static int _sendto(struct socket *sock, const void *buff,
+		   int len, int nonblock, unsigned flags,
+		   struct sockaddr_ipx *sa, int addr_len)
+
+{
+        struct iovec iov;
+        struct msghdr msg;
+
+        iov.iov_base = (void *)buff;
+        iov.iov_len  = len;
+
+        msg.msg_name      = (void *)sa;
+        msg.msg_namelen   = addr_len;
+        msg.msg_accrights = NULL;
+        msg.msg_iov       = &iov;
+        msg.msg_iovlen    = 1;
+
+        return sock->ops->sendmsg(sock, &msg, len, nonblock, flags);
+}
+
+
+static void
+ncp_wdog_data_ready(struct sock *sk, int len)
+{
+	struct socket *sock = sk->socket;
+
+	if (!sk->dead)
+	{
+		unsigned char packet_buf[2];
+		struct sockaddr_ipx sender;
+		int addr_len = sizeof(struct sockaddr_ipx);
+		int result;
+		unsigned short fs;
+
+		fs = get_fs();
+		set_fs(get_ds());
+
+		result = _recvfrom(sock, (void *)packet_buf, 2, 1, 0,
+				   &sender, &addr_len);
+
+		if (   (result != 2)
+		    || (packet_buf[1] != '?')
+		    /* How to check connection number here? */
+		    )
+		{
+			/* Error, throw away the complete packet */
+			_recvfrom(sock, (void *)packet_buf, 2, 1, 0,
+				  &sender, &addr_len);
+
+			printk("ncpfs: got strange packet on watchdog "
+			       "socket\n");
+			
+		}
+		else
+		{
+			int result;
+			DDPRINTK("ncpfs: got watchdog from:\n");
+			DDPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X,"
+				" conn:%02X,type:%c\n",
+				htonl(sender.sipx_network),
+				sender.sipx_node[0], sender.sipx_node[1],
+				sender.sipx_node[2], sender.sipx_node[3],
+				sender.sipx_node[4], sender.sipx_node[5],
+				ntohs(sender.sipx_port),
+				packet_buf[0], packet_buf[1]);
+
+			packet_buf[1] = 'Y';
+			result = _sendto(sock, (void *)packet_buf, 2, 1, 0,
+					 &sender, sizeof(sender));
+			DDPRINTK("send result: %d\n", result);
+		}
+		set_fs(fs);
+	}
+}
+
+
+int
+ncp_catch_watchdog(struct ncp_server *server)
+{
+        struct file   *file;
+        struct inode  *inode;
+        struct socket *sock;
+        struct sock   *sk;
+
+        if (   (server == NULL)
+            || ((file  = server->wdog_filp) == NULL)
+            || ((inode = file->f_inode) == NULL)
+            || (!S_ISSOCK(inode->i_mode)))
+	{
+                printk("ncp_catch_watchdog: did not get valid server!\n");
+                server->data_ready = NULL;
+                return -EINVAL;
+        }
+
+        sock = &(inode->u.socket_i);
+
+        if (sock->type != SOCK_DGRAM)
+	{
+                printk("ncp_catch_watchdog: did not get SOCK_STREAM\n");
+                server->data_ready = NULL;
+                return -EINVAL;
+        }
+
+        sk   = (struct sock *)(sock->data);
+
+        if (sk == NULL)
+	{
+                printk("ncp_catch_watchdog: sk == NULL");
+                server->data_ready = NULL;
+                return -EINVAL;
+        }
+
+        DDPRINTK("ncp_catch_watchdog.: sk->d_r = %x, server->d_r = %x\n",
+                 (unsigned int)(sk->data_ready),
+                 (unsigned int)(server->data_ready));
+
+        if (sk->data_ready == ncp_wdog_data_ready)
+	{
+                printk("ncp_catch_watchdog: already done\n");
+                return -EINVAL;
+        }
+
+        server->data_ready = sk->data_ready;
+        sk->data_ready = ncp_wdog_data_ready;
+	sk->allocation = GFP_ATOMIC;
+        return 0;
+}
+                
+int
+ncp_dont_catch_watchdog(struct ncp_server *server)
+{
+        struct file   *file;
+        struct inode  *inode;
+        struct socket *sock;
+        struct sock   *sk;
+
+        if (   (server == NULL)
+            || ((file  = server->wdog_filp) == NULL)
+            || ((inode = file->f_inode) == NULL)
+            || (!S_ISSOCK(inode->i_mode)))
+	{
+                printk("ncp_dont_catch_watchdog: "
+                       "did not get valid server!\n");
+                return -EINVAL;
+        }
+
+        sock = &(inode->u.socket_i);
+
+        if (sock->type != SOCK_DGRAM)
+	{
+                printk("ncp_dont_catch_watchdog: did not get SOCK_STREAM\n");
+                return -EINVAL;
+        }
+
+        sk   = (struct sock *)(sock->data);
+
+        if (sk == NULL)
+	{
+                printk("ncp_dont_catch_watchdog: sk == NULL");
+                return -EINVAL;
+        }
+
+        if (server->data_ready == NULL)
+	{
+                printk("ncp_dont_catch_watchdog: "
+                       "server->data_ready == NULL\n");
+                return -EINVAL;
+        }
+
+        if (sk->data_ready != ncp_wdog_data_ready)
+	{
+                printk("ncp_dont_catch_watchdog: "
+                       "sk->data_callback != ncp_data_callback\n");
+                return -EINVAL;
+        }
+
+        DDPRINTK("ncp_dont_catch_watchdog: 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;
+	sk->allocation = GFP_KERNEL;
+        server->data_ready = NULL;
+        return 0;
+}
+
+
+
+#define NCP_SLACK_SPACE 1024
+
+#define _S(nr) (1<<((nr)-1))
+
+static int
+do_ncp_rpc_call(struct ncp_server *server, int size)
+{
+	struct file *file;
+	struct inode *inode;
+	struct socket *sock;
+	unsigned short fs;
+	int result;
+	char *start = server->packet;
+	select_table wait_table;
+	struct select_table_entry entry;
+	int (*select) (struct inode *, struct file *, int, select_table *);
+	int init_timeout, max_timeout;
+	int timeout;
+	int retrans;
+	int major_timeout_seen;
+	char *server_name;
+	int n;
+	int addrlen;
+	unsigned long old_mask;
+
+	/* We have to check the result, so store the complete header */
+	struct ncp_request_header request =
+		*((struct ncp_request_header *)(server->packet));
+
+	struct ncp_reply_header reply;	
+
+
+	file = server->ncp_filp;
+	inode = file->f_inode;
+	select = file->f_op->select;
+	sock = &inode->u.socket_i;
+	if (!sock)
+	{
+		printk("ncp_rpc_call: socki_lookup failed\n");
+		return -EBADF;
+	}
+	init_timeout = server->m.time_out;
+	max_timeout = NCP_MAX_RPC_TIMEOUT*HZ/10;
+	retrans = server->m.retry_count;
+	major_timeout_seen = 0;
+	server_name = server->m.server_name;
+	old_mask = current->blocked;
+	current->blocked |= ~(_S(SIGKILL)
+#if 0
+		| _S(SIGSTOP)
+#endif
+		| ((server->m.flags & NCP_MOUNT_INTR)
+		? ((current->sig->action[SIGINT - 1].sa_handler == SIG_DFL
+			? _S(SIGINT) : 0)
+		| (current->sig->action[SIGQUIT - 1].sa_handler == SIG_DFL
+			? _S(SIGQUIT) : 0))
+		: 0));
+	fs = get_fs();
+	set_fs(get_ds());
+	for (n = 0, timeout = init_timeout; ; n++, timeout <<= 1)
+	{
+		DDPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X\n",
+			htonl(server->m.serv_addr.sipx_network),
+			server->m.serv_addr.sipx_node[0],
+			server->m.serv_addr.sipx_node[1],
+			server->m.serv_addr.sipx_node[2],
+			server->m.serv_addr.sipx_node[3],
+			server->m.serv_addr.sipx_node[4],
+			server->m.serv_addr.sipx_node[5],
+			ntohs(server->m.serv_addr.sipx_port));
+		DDPRINTK("ncpfs: req.typ: %04X, con: %d, "
+			"seq: %d",
+			request.type,
+			(request.conn_high << 8) + request.conn_low,
+			request.sequence);
+		DDPRINTK(" func: %d\n",
+			 request.function);
+
+		result = _sendto(sock, (void *) start, size, 0, 0,
+				 &(server->m.serv_addr),
+				 sizeof(server->m.serv_addr));
+		if (result < 0)
+		{
+			printk("ncp_rpc_call: send error = %d\n", result);
+			break;
+		}
+	re_select:
+		wait_table.nr = 0;
+		wait_table.entry = &entry;
+		current->state = TASK_INTERRUPTIBLE;
+		if (   !select(inode, file, SEL_IN, &wait_table)
+		    && !select(inode, file, SEL_IN, NULL))
+		{
+			if (timeout > max_timeout)
+			{
+			  /* JEJB/JSP 2/7/94
+			   * This is useful to see if the system is
+			   * hanging */
+			  printk("NCP max timeout reached on %s\n",
+				 server_name);
+			  timeout = max_timeout;
+			}
+			current->timeout = jiffies + timeout;
+			schedule();
+			remove_wait_queue(entry.wait_address, &entry.wait);
+			current->state = TASK_RUNNING;
+			if (current->signal & ~current->blocked)
+			{
+				current->timeout = 0;
+				result = -ERESTARTSYS;
+				break;
+			}
+			if (!current->timeout)
+			{
+				if (n < retrans)
+					continue;
+				if (server->m.flags & NCP_MOUNT_SOFT)
+				{
+					printk("NCP server %s not responding, "
+						"timed out\n", server_name);
+					result = -EIO;
+					break;
+				}
+				n = 0;
+				timeout = init_timeout;
+				init_timeout <<= 1;
+				if (!major_timeout_seen)
+				{
+				  printk("NCP server %s not responding, "
+					 "still trying\n", server_name);
+				}
+				major_timeout_seen = 1;
+				continue;
+			}
+			else
+				current->timeout = 0;
+		}
+		else if (wait_table.nr)
+			remove_wait_queue(entry.wait_address, &entry.wait);
+		current->state = TASK_RUNNING;
+		addrlen = 0;
+
+		/* Get the header from the next packet using a peek, so keep it
+		 * on the recv queue.  If it is wrong, it will be some reply
+		 * we don't now need, so discard it */
+		result = _recvfrom(sock, (void *)&reply,
+				   sizeof(reply), 1, MSG_PEEK,
+				   NULL, &addrlen);
+		if (result < 0)
+		{
+			if (result == -EAGAIN)
+			{
+				DPRINTK("ncp_rpc_call: bad select ready\n");
+				goto re_select;
+			}
+			if (result == -ECONNREFUSED)
+			{
+				DPRINTK("ncp_rpc_call: server playing coy\n");
+				goto re_select;
+			}
+			if (result != -ERESTARTSYS)
+			{
+				printk("ncp_rpc_call: recv error = %d\n",
+					-result);
+			}
+			break;
+		}
+		if (   (result     == sizeof(reply))
+		    && (reply.type == NCP_POSITIVE_ACK))
+		{
+			/* Throw away the packet */
+			DPRINTK("ncp_rpc_call: got positive acknowledge\n");
+			_recvfrom(sock, (void *)&reply, sizeof(reply), 1, 0,
+				  NULL, &addrlen);
+			goto re_select;
+		}
+
+		DDPRINTK("ncpfs: rep.typ: %04X, con: %d, tsk: %d,"
+			"seq: %d\n",
+			reply.type,
+			(reply.conn_high << 8) + reply.conn_low,
+			reply.task,
+			reply.sequence);
+		
+		if (   (result          >= sizeof(reply))
+		    && (reply.type      == NCP_REPLY)
+		    && (   (request.type    == NCP_ALLOC_SLOT_REQUEST)
+			|| (   (reply.sequence  == request.sequence)
+			    && (reply.conn_low  == request.conn_low)
+/* seem to get wrong task from NW311 && (reply.task      == request.task)*/
+			    && (reply.conn_high == request.conn_high))))
+		{
+			if (major_timeout_seen)
+				printk("NCP server %s OK\n", server_name);
+			break;
+		}
+		/* JEJB/JSP 2/7/94
+		 * we have xid mismatch, so discard the packet and start
+		 * again.  What a hack! but I can't call recvfrom with
+		 * a null buffer yet. */
+		_recvfrom(sock, (void *)&reply, sizeof(reply), 1, 0, NULL,
+			  &addrlen);
+#if 1
+		printk("ncp_rpc_call: reply mismatch\n");
+#endif
+		goto re_select;
+	}
+	/* 
+	 * we have the correct reply, so read into the correct place and
+	 * return it
+	 */
+	result = _recvfrom(sock, (void *)start, server->packet_size,
+			   1, 0, NULL, &addrlen);
+	if (result < 0)
+	{
+		printk("NCP: notice message: result=%d\n", result);
+	}
+	else if (result < sizeof(struct ncp_reply_header))
+	{
+		printk("NCP: just caught a too small read memory size..., "
+		       "email to NET channel\n");
+		printk("NCP: result=%d,addrlen=%d\n", result, addrlen);
+		result = -EIO;
+	}
+
+	current->blocked = old_mask;
+	set_fs(fs);
+	return result;
+}
+
+
+/*
+ * We need the server to be locked here, so check!
+ */
+
+static int
+ncp_do_request(struct ncp_server *server, int size)
+{
+	if (server->lock == 0)
+	{
+		printk("ncpfs: Server not locked!\n");
+		return -EIO;
+	}
+
+	return do_ncp_rpc_call(server, size);
+}
+
+/* ncp_do_request assures that at least a complete reply header is
+ * received. It assumes that server->current_size contains the ncp
+ * request size */
+int
+ncp_request(struct ncp_server *server, int function)
+{
+	struct ncp_request_header *h
+		= (struct ncp_request_header *)(server->packet);
+	struct ncp_reply_header *reply
+		= (struct ncp_reply_header *)(server->packet);
+
+	int request_size = server->current_size
+		- sizeof(struct ncp_request_header);
+
+	int result;
+
+	if (server->has_subfunction != 0)
+	{
+		*(__u16 *)&(h->data[0]) = request_size - 2;
+	}
+
+	h->type = NCP_REQUEST;
+	
+	server->sequence += 1;
+	h->sequence  = server->sequence;
+	h->conn_low  = (server->connection) & 0xff;
+	h->conn_high = ((server->connection) & 0xff00) >> 8;
+	h->task      = (current->pid) & 0xff;
+	h->function  = function;
+
+	if ((result = ncp_do_request(server, request_size + sizeof(*h))) < 0)
+	{
+		DPRINTK("ncp_request_error: %d\n", result);
+		return result;
+	}
+
+	server->completion  = reply->completion_code;
+	server->conn_status = reply->connection_state;
+	server->reply_size  = result;
+	server->ncp_reply_size = result - sizeof(struct ncp_reply_header);
+
+	result = reply->completion_code;
+
+	if (result != 0)
+	{
+		DPRINTK("ncp_completion_code: %d\n", result);
+	}
+	return result;	
+}
+
+int
+ncp_connect(struct ncp_server *server)
+{
+	struct ncp_request_header *h
+		= (struct ncp_request_header *)(server->packet);
+	int result;
+
+	h->type = NCP_ALLOC_SLOT_REQUEST;
+	
+	server->sequence = 0;
+	h->sequence  = server->sequence;
+	h->conn_low  = 0xff;
+	h->conn_high = 0xff;
+	h->task      = (current->pid) & 0xff;
+	h->function  = 0;
+
+	if ((result = ncp_do_request(server, sizeof(*h))) < 0)
+	{
+		return result;
+	}
+
+	server->sequence = 0;
+	server->connection = h->conn_low + (h->conn_high * 256);
+	return 0;
+}
+	
+int
+ncp_disconnect(struct ncp_server *server)
+{
+	struct ncp_request_header *h
+		= (struct ncp_request_header *)(server->packet);
+
+	h->type = NCP_DEALLOC_SLOT_REQUEST;
+	
+	server->sequence += 1;
+	h->sequence  = server->sequence;
+	h->conn_low  = (server->connection) & 0xff;
+	h->conn_high = ((server->connection) & 0xff00) >> 8;
+	h->task      = (current->pid) & 0xff;
+	h->function  = 0;
+
+	return ncp_do_request(server, sizeof(*h));
+}
+
+void
+ncp_lock_server(struct ncp_server *server)
+{
+#if 0
+	/* For testing, only 1 process */
+	if (server->lock != 0)
+	{
+		DPRINTK("ncpfs: server locked!!!\n");
+	}
+#endif
+        while (server->lock)
+		sleep_on(&server->wait);
+	server->lock = 1;
+}
+
+void
+ncp_unlock_server(struct ncp_server *server)
+{
+        if (server->lock != 1)
+	{
+                printk("ncp_unlock_server: was not locked!\n");
+        }
+
+        server->lock = 0;
+        wake_up(&server->wait);
+}
+        

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this