summaryrefslogtreecommitdiffstats
path: root/fs/ncpfs/sock.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
committer <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
commitbeb116954b9b7f3bb56412b2494b562f02b864b1 (patch)
tree120e997879884e1b9d93b265221b939d2ef1ade1 /fs/ncpfs/sock.c
parent908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff)
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'fs/ncpfs/sock.c')
-rw-r--r--fs/ncpfs/sock.c704
1 files changed, 704 insertions, 0 deletions
diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c
new file mode 100644
index 000000000..b8017a9d2
--- /dev/null
+++ b/fs/ncpfs/sock.c
@@ -0,0 +1,704 @@
+/*
+ * linux/fs/ncpfs/sock.c
+ *
+ * Copyright (C) 1992, 1993 Rick Sladkey
+ *
+ * Modified 1995, 1996 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/uaccess.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_control = 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_control = 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? */
+ )
+ {
+ 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_DGRAM\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_DGRAM\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;
+}
+
+static void
+ncp_msg_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);
+
+ DPRINTK("ncpfs: got message of size %d from:\n", result);
+ DPRINTK("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]);
+
+ ncp_trigger_message(sk->protinfo.af_ipx.ncp_server);
+
+ set_fs(fs);
+ }
+}
+
+int
+ncp_catch_message(struct ncp_server *server)
+{
+ struct file *file;
+ struct inode *inode;
+ struct socket *sock;
+ struct sock *sk;
+
+ if ( (server == NULL)
+ || ((file = server->msg_filp) == NULL)
+ || ((inode = file->f_inode) == NULL)
+ || (!S_ISSOCK(inode->i_mode)))
+ {
+ printk("ncp_catch_message: did not get valid server!\n");
+ return -EINVAL;
+ }
+
+ sock = &(inode->u.socket_i);
+
+ if (sock->type != SOCK_DGRAM)
+ {
+ printk("ncp_catch_message: did not get SOCK_DGRAM\n");
+ return -EINVAL;
+ }
+
+ sk = (struct sock *)(sock->data);
+
+ if (sk == NULL)
+ {
+ printk("ncp_catch_message: sk == NULL");
+ return -EINVAL;
+ }
+
+ DDPRINTK("ncp_catch_message: sk->d_r = %x\n",
+ (unsigned int)(sk->data_ready));
+
+ if (sk->data_ready == ncp_msg_data_ready)
+ {
+ printk("ncp_catch_message: already done\n");
+ return -EINVAL;
+ }
+
+ sk->data_ready = ncp_msg_data_ready;
+ sk->protinfo.af_ipx.ncp_server = server;
+ 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;
+ int acknowledge_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;
+ retrans = server->m.retry_count;
+ major_timeout_seen = 0;
+ acknowledge_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 */
+ if (acknowledge_seen == 0)
+ {
+ 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);
+ n = 0;
+ timeout = max_timeout;
+ acknowledge_seen = 1;
+ 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);
+
+ DPRINTK("ncp_rpc_call: reply mismatch\n");
+ 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)
+{
+ int result;
+
+ if (server->lock == 0)
+ {
+ printk("ncpfs: Server not locked!\n");
+ return -EIO;
+ }
+
+ if (!ncp_conn_valid(server))
+ {
+ return -EIO;
+ }
+
+ result = do_ncp_rpc_call(server, size);
+
+ DDPRINTK("do_ncp_rpc_call returned %d\n", result);
+
+ if (result < 0)
+ {
+ /* There was a problem with I/O, so the connections is
+ * no longer usable. */
+ ncp_invalidate_conn(server);
+ }
+ return result;
+}
+
+/* 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]) = htons(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: %x\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);
+}
+