URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [fs/] [ncpfs/] [sock.c] - Rev 1765
Compare with Previous | Blame | View Log
/* * linux/fs/ncpfs/sock.c * * Copyright (C) 1992, 1993 Rick Sladkey * * Modified 1995, 1996 by Volker Lendecke to be usable for ncp * */ #include <linux/config.h> #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> #include "ncpsign_kernel.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; } #ifdef CONFIG_NCPFS_PACKET_SIGNING if (server->sign_active) { sign_packet(server, &size); } #endif /* CONFIG_NCPFS_PACKET_SIGNING */ 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 = 2; 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 = 2; 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 = 2; 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); }