diff options
| author | Petr Vandrovec <vandrove@vc.cvut.cz> | 2002-09-21 02:45:59 +0200 |
|---|---|---|
| committer | Petr Vandrovec <vandrove@vc.cvut.cz> | 2002-09-21 02:45:59 +0200 |
| commit | 3034ecfc30ae92197bcd170e573875da01d0511d (patch) | |
| tree | aa1063b19544f0f1c19e059563262f7957a67c9d | |
| parent | 18277e88a38adc9ddc25343e61ed8e7476862bb2 (diff) | |
| parent | d29d5c4add2166c6acdf4bb3688695b46ce77a31 (diff) | |
Merge bk://linux.bkbits.net/linux-2.5
into vc.cvut.cz:/mnt2/usr/src/bk/linus/ncpfs
| -rw-r--r-- | fs/ncpfs/Makefile | 2 | ||||
| -rw-r--r-- | fs/ncpfs/getopt.c | 75 | ||||
| -rw-r--r-- | fs/ncpfs/getopt.h | 16 | ||||
| -rw-r--r-- | fs/ncpfs/inode.c | 165 | ||||
| -rw-r--r-- | fs/ncpfs/ioctl.c | 2 | ||||
| -rw-r--r-- | fs/ncpfs/ncplib_kernel.c | 3 | ||||
| -rw-r--r-- | fs/ncpfs/ncpsign_kernel.c | 36 | ||||
| -rw-r--r-- | fs/ncpfs/ncpsign_kernel.h | 15 | ||||
| -rw-r--r-- | fs/ncpfs/sock.c | 977 | ||||
| -rw-r--r-- | include/linux/ncp.h | 1 | ||||
| -rw-r--r-- | include/linux/ncp_fs_sb.h | 51 | ||||
| -rw-r--r-- | include/linux/ncp_mount.h | 7 |
12 files changed, 980 insertions, 370 deletions
diff --git a/fs/ncpfs/Makefile b/fs/ncpfs/Makefile index f28ef2850982..60bcf9e2a528 100644 --- a/fs/ncpfs/Makefile +++ b/fs/ncpfs/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_NCP_FS) += ncpfs.o ncpfs-objs := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o \ - ncpsign_kernel.o + ncpsign_kernel.o getopt.o ifeq ($(CONFIG_NCPFS_EXTRAS),y) ncpfs-objs += symlink.o endif diff --git a/fs/ncpfs/getopt.c b/fs/ncpfs/getopt.c new file mode 100644 index 000000000000..335b003dddf9 --- /dev/null +++ b/fs/ncpfs/getopt.c @@ -0,0 +1,75 @@ +/* + * getopt.c + */ + +#include <linux/kernel.h> +#include <linux/string.h> + +#include <asm/errno.h> + +#include "getopt.h" + +/** + * ncp_getopt - option parser + * @caller: name of the caller, for error messages + * @options: the options string + * @opts: an array of &struct option entries controlling parser operations + * @optopt: output; will contain the current option + * @optarg: output; will contain the value (if one exists) + * @flag: output; may be NULL; should point to a long for or'ing flags + * @value: output; may be NULL; will be overwritten with the integer value + * of the current argument. + * + * Helper to parse options on the format used by mount ("a=b,c=d,e,f"). + * Returns opts->val if a matching entry in the 'opts' array is found, + * 0 when no more tokens are found, -1 if an error is encountered. + */ +int ncp_getopt(const char *caller, char **options, const struct ncp_option *opts, + char **optopt, char **optarg, unsigned long *value) +{ + char *token; + char *val; + + do { + if ((token = strsep(options, ",")) == NULL) + return 0; + } while (*token == '\0'); + if (optopt) + *optopt = token; + + if ((val = strchr (token, '=')) != NULL) { + *val++ = 0; + } + *optarg = val; + for (; opts->name; opts++) { + if (!strcmp(opts->name, token)) { + if (!val) { + if (opts->has_arg & OPT_NOPARAM) { + return opts->val; + } + printk(KERN_INFO "%s: the %s option requires an argument\n", + caller, token); + return -EINVAL; + } + if (opts->has_arg & OPT_INT) { + char* v; + + *value = simple_strtoul(val, &v, 0); + if (!*v) { + return opts->val; + } + printk(KERN_INFO "%s: invalid numeric value in %s=%s\n", + caller, token, val); + return -EDOM; + } + if (opts->has_arg & OPT_STRING) { + return opts->val; + } + printk(KERN_INFO "%s: unexpected argument %s to the %s option\n", + caller, val, token); + return -EINVAL; + } + } + printk(KERN_INFO "%s: Unrecognized mount option %s\n", caller, token); + return -EOPNOTSUPP; +} diff --git a/fs/ncpfs/getopt.h b/fs/ncpfs/getopt.h new file mode 100644 index 000000000000..cccc007dcaf9 --- /dev/null +++ b/fs/ncpfs/getopt.h @@ -0,0 +1,16 @@ +#ifndef _LINUX_GETOPT_H +#define _LINUX_GETOPT_H + +#define OPT_NOPARAM 1 +#define OPT_INT 2 +#define OPT_STRING 4 +struct ncp_option { + const char *name; + unsigned int has_arg; + int val; +}; + +extern int ncp_getopt(const char *caller, char **options, const struct ncp_option *opts, + char **optopt, char **optarg, unsigned long *value); + +#endif /* _LINUX_GETOPT_H */ diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index 0a49d6a48d5f..048f4e0f4b53 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -31,7 +31,10 @@ #include <linux/ncp_fs.h> +#include <net/sock.h> + #include "ncplib_kernel.h" +#include "getopt.h" static void ncp_delete_inode(struct inode *); static void ncp_put_super(struct super_block *); @@ -283,6 +286,103 @@ ncp_delete_inode(struct inode *inode) clear_inode(inode); } +static void ncp_stop_tasks(struct ncp_server *server) { + struct sock* sk = server->ncp_sock->sk; + + sk->error_report = server->error_report; + sk->data_ready = server->data_ready; + sk->write_space = server->write_space; + del_timer_sync(&server->timeout_tm); + flush_scheduled_tasks(); +} + +static const struct ncp_option ncp_opts[] = { + { "uid", OPT_INT, 'u' }, + { "gid", OPT_INT, 'g' }, + { "owner", OPT_INT, 'o' }, + { "mode", OPT_INT, 'm' }, + { "dirmode", OPT_INT, 'd' }, + { "timeout", OPT_INT, 't' }, + { "retry", OPT_INT, 'r' }, + { "flags", OPT_INT, 'f' }, + { "wdogpid", OPT_INT, 'w' }, + { "ncpfd", OPT_INT, 'n' }, + { "infofd", OPT_INT, 'i' }, /* v5 */ + { "version", OPT_INT, 'v' }, + { NULL, 0, 0 } }; + +static int ncp_parse_options(struct ncp_mount_data_kernel *data, char *options) { + int optval; + char *optarg; + unsigned long optint; + int version = 0; + + data->flags = 0; + data->int_flags = 0; + data->mounted_uid = 0; + data->wdog_pid = -1; + data->ncp_fd = ~0; + data->time_out = 10; + data->retry_count = 20; + data->uid = 0; + data->gid = 0; + data->file_mode = 0600; + data->dir_mode = 0700; + data->info_fd = -1; + data->mounted_vol[0] = 0; + + while ((optval = ncp_getopt("ncpfs", &options, ncp_opts, NULL, &optarg, &optint)) != 0) { + if (optval < 0) + return optval; + switch (optval) { + case 'u': + data->uid = optint; + break; + case 'g': + data->gid = optint; + break; + case 'o': + data->mounted_uid = optint; + break; + case 'm': + data->file_mode = optint; + break; + case 'd': + data->dir_mode = optint; + break; + case 't': + data->time_out = optint; + break; + case 'r': + data->retry_count = optint; + break; + case 'f': + data->flags = optint; + break; + case 'w': + data->wdog_pid = optint; + break; + case 'n': + data->ncp_fd = optint; + break; + case 'i': + data->info_fd = optint; + break; + case 'v': + if (optint < NCP_MOUNT_VERSION_V4) { + return -ECHRNG; + } + if (optint > NCP_MOUNT_VERSION_V5) { + return -ECHRNG; + } + version = optint; + break; + + } + } + return 0; +} + static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) { struct ncp_mount_data_kernel data; @@ -323,6 +423,7 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) data.gid = md->gid; data.file_mode = md->file_mode; data.dir_mode = md->dir_mode; + data.info_fd = -1; memcpy(data.mounted_vol, md->mounted_vol, NCP_VOLNAME_LEN+1); } @@ -342,12 +443,18 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) data.gid = md->gid; data.file_mode = md->file_mode; data.dir_mode = md->dir_mode; + data.info_fd = -1; data.mounted_vol[0] = 0; } break; default: error = -ECHRNG; - goto out; + if (*(__u32*)raw_data == cpu_to_be32(0x76657273)) { + error = ncp_parse_options(&data, raw_data); + } + if (error) + goto out; + break; } error = -EBADF; ncp_filp = fget(data.ncp_fd); @@ -376,6 +483,28 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) memset(server, 0, sizeof(*server)); server->ncp_filp = ncp_filp; + server->ncp_sock = sock; + + if (data.info_fd != -1) { + struct socket *info_sock; + + error = -EBADF; + server->info_filp = fget(data.info_fd); + if (!server->info_filp) + goto out_fput; + error = -ENOTSOCK; + sock_inode = server->info_filp->f_dentry->d_inode; + if (!S_ISSOCK(sock_inode->i_mode)) + goto out_fput2; + info_sock = SOCKET_I(sock_inode); + if (!info_sock) + goto out_fput2; + error = -EBADFD; + if (info_sock->type != SOCK_STREAM) + goto out_fput2; + server->info_sock = info_sock; + } + /* server->lock = 0; */ init_MUTEX(&server->sem); server->packet = NULL; @@ -413,6 +542,16 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) server->dentry_ttl = 0; /* no caching */ + INIT_LIST_HEAD(&server->tx.requests); + init_MUTEX(&server->rcv.creq_sem); + server->tx.creq = NULL; + server->rcv.creq = NULL; + server->data_ready = sock->sk->data_ready; + server->write_space = sock->sk->write_space; + server->error_report = sock->sk->error_report; + sock->sk->user_data = server; + + init_timer(&server->timeout_tm); #undef NCP_PACKET_SIZE #define NCP_PACKET_SIZE 131072 error = -ENOMEM; @@ -421,6 +560,22 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) if (server->packet == NULL) goto out_nls; + sock->sk->data_ready = ncp_tcp_data_ready; + sock->sk->error_report = ncp_tcp_error_report; + if (sock->type == SOCK_STREAM) { + server->rcv.ptr = (unsigned char*)&server->rcv.buf; + server->rcv.len = 10; + server->rcv.state = 0; + INIT_TQUEUE(&server->rcv.tq, ncp_tcp_rcv_proc, server); + INIT_TQUEUE(&server->tx.tq, ncp_tcp_tx_proc, server); + sock->sk->write_space = ncp_tcp_write_space; + } else { + INIT_TQUEUE(&server->rcv.tq, ncpdgram_rcv_proc, server); + INIT_TQUEUE(&server->timeout_tq, ncpdgram_timeout_proc, server); + server->timeout_tm.data = (unsigned long)server; + server->timeout_tm.function = ncpdgram_timeout_call; + } + ncp_lock_server(server); error = ncp_connect(server); ncp_unlock_server(server); @@ -495,12 +650,16 @@ out_disconnect: ncp_disconnect(server); ncp_unlock_server(server); out_packet: + ncp_stop_tasks(server); vfree(server->packet); out_nls: #ifdef CONFIG_NCPFS_NLS unload_nls(server->nls_io); unload_nls(server->nls_vol); #endif +out_fput2: + if (server->info_filp) + fput(server->info_filp); out_fput: /* 23/12/1998 Marcin Dalecki <dalecki@cs.net.pl>: * @@ -522,6 +681,8 @@ static void ncp_put_super(struct super_block *sb) ncp_disconnect(server); ncp_unlock_server(server); + ncp_stop_tasks(server); + #ifdef CONFIG_NCPFS_NLS /* unload the NLS charsets */ if (server->nls_vol) @@ -536,6 +697,8 @@ static void ncp_put_super(struct super_block *sb) } #endif /* CONFIG_NCPFS_NLS */ + if (server->info_filp) + fput(server->info_filp); fput(server->ncp_filp); kill_proc(server->m.wdog_pid, SIGTERM, 1); diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index 8a0369da6893..f34e38b9dd8b 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -424,7 +424,7 @@ outrel: if (user.object_name_len) { newname = ncp_kmalloc(user.object_name_len, GFP_USER); if (!newname) return -ENOMEM; - if (copy_from_user(newname, user.object_name, sizeof(user))) { + if (copy_from_user(newname, user.object_name, user.object_name_len)) { ncp_kfree_s(newname, user.object_name_len); return -EFAULT; } diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c index c32e5c653af2..711424f12609 100644 --- a/fs/ncpfs/ncplib_kernel.c +++ b/fs/ncpfs/ncplib_kernel.c @@ -341,6 +341,7 @@ void ncp_extract_file_info(void *structure, struct nw_info_struct *target) target->nameLen = *name_len; memcpy(target->entryName, name_len + 1, *name_len); target->entryName[*name_len] = '\0'; + target->volNumber = le32_to_cpu(target->volNumber); return; } @@ -475,7 +476,7 @@ ncp_get_known_namespace(struct ncp_server *server, __u8 volume) } result = NW_NS_DOS; - no_namespaces = ncp_reply_word(server, 0); + no_namespaces = le16_to_cpu(ncp_reply_word(server, 0)); namespace = ncp_reply_data(server, 2); while (no_namespaces > 0) { diff --git a/fs/ncpfs/ncpsign_kernel.c b/fs/ncpfs/ncpsign_kernel.c index 7183ba4bcb92..dcac251c14ff 100644 --- a/fs/ncpfs/ncpsign_kernel.c +++ b/fs/ncpfs/ncpsign_kernel.c @@ -93,19 +93,35 @@ static void nwsign(char *r_data1, char *r_data2, char *outdata) { /* Make a signature for the current packet and add it at the end of the */ /* packet. */ -void sign_packet(struct ncp_server *server, int *size) { - char data[64]; +void __sign_packet(struct ncp_server *server, const char *packet, size_t size, __u32 totalsize, void *sign_buff) { + unsigned char data[64]; - memset(data,0,64); - memcpy(data,server->sign_root,8); - PUT_LE32(data+8,(*size)); - memcpy(data+12,server->packet+sizeof(struct ncp_request_header)-1, - min_t(unsigned int,(*size)-sizeof(struct ncp_request_header)+1,52)); + memcpy(data, server->sign_root, 8); + *(__u32*)(data + 8) = totalsize; + if (size < 52) { + memcpy(data + 12, packet, size); + memset(data + 12 + size, 0, 52 - size); + } else { + memcpy(data + 12, packet, 52); + } + nwsign(server->sign_last, data, server->sign_last); + memcpy(sign_buff, server->sign_last, 8); +} - nwsign(server->sign_last,data,server->sign_last); +int sign_verify_reply(struct ncp_server *server, const char *packet, size_t size, __u32 totalsize, const void *sign_buff) { + unsigned char data[64]; + unsigned char hash[16]; - memcpy(server->packet+(*size),server->sign_last,8); - (*size)+=8; + memcpy(data, server->sign_root, 8); + *(__u32*)(data + 8) = totalsize; + if (size < 52) { + memcpy(data + 12, packet, size); + memset(data + 12 + size, 0, 52 - size); + } else { + memcpy(data + 12, packet, 52); + } + nwsign(server->sign_last, data, hash); + return memcmp(sign_buff, hash, 8); } #endif /* CONFIG_NCPFS_PACKET_SIGNING */ diff --git a/fs/ncpfs/ncpsign_kernel.h b/fs/ncpfs/ncpsign_kernel.h index 141338fb5ad2..6451a68381cc 100644 --- a/fs/ncpfs/ncpsign_kernel.h +++ b/fs/ncpfs/ncpsign_kernel.h @@ -10,6 +10,19 @@ #include <linux/ncp_fs.h> -void sign_packet(struct ncp_server *server, int *size); +#ifdef CONFIG_NCPFS_PACKET_SIGNING +void __sign_packet(struct ncp_server *server, const char *data, size_t size, __u32 totalsize, void *sign_buff); +int sign_verify_reply(struct ncp_server *server, const char *data, size_t size, __u32 totalsize, const void *sign_buff); +#endif + +static inline size_t sign_packet(struct ncp_server *server, const char *data, size_t size, __u32 totalsize, void *sign_buff) { +#ifdef CONFIG_NCPFS_PACKET_SIGNING + if (server->sign_active) { + __sign_packet(server, data, size, totalsize, sign_buff); + return 8; + } +#endif + return 0; +} #endif diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c index 59753377c7f2..f399d7065dc3 100644 --- a/fs/ncpfs/sock.c +++ b/fs/ncpfs/sock.c @@ -29,18 +29,13 @@ #include <linux/ncp_fs.h> -#ifdef CONFIG_NCPFS_PACKET_SIGNING #include "ncpsign_kernel.h" -#endif static int _recv(struct socket *sock, unsigned char *ubuf, int size, unsigned flags) { struct iovec iov; struct msghdr msg; - struct scm_cookie scm; - - memset(&scm, 0, sizeof(scm)); iov.iov_base = ubuf; iov.iov_len = size; @@ -50,15 +45,14 @@ static int _recv(struct socket *sock, unsigned char *ubuf, int size, msg.msg_control = NULL; msg.msg_iov = &iov; msg.msg_iovlen = 1; - return sock->ops->recvmsg(sock, &msg, size, flags, &scm); + + return sock_recvmsg(sock, &msg, size, flags); } -static int _send(struct socket *sock, const void *buff, int len) +static inline int _send(struct socket *sock, const void *buff, int len) { struct iovec iov; struct msghdr msg; - struct scm_cookie scm; - int err; iov.iov_base = (void *) buff; iov.iov_len = len; @@ -70,353 +64,661 @@ static int _send(struct socket *sock, const void *buff, int len) msg.msg_iovlen = 1; msg.msg_flags = 0; - err = scm_send(sock, &msg, &scm); - if (err < 0) { - return err; - } - err = sock->ops->sendmsg(sock, &msg, len, &scm); - scm_destroy(&scm); - return err; + return sock_sendmsg(sock, &msg, len); } -static int do_ncp_rpc_call(struct ncp_server *server, int size, - struct ncp_reply_header* reply_buf, int max_reply_size) -{ - struct file *file; - struct socket *sock; +struct ncp_request_reply { + struct list_head req; + wait_queue_head_t wq; + struct ncp_reply_header* reply_buf; + size_t datalen; int result; - char *start = server->packet; - poll_table wait_table; - int init_timeout, max_timeout; - int timeout; - int retrans; - int major_timeout_seen; - int acknowledge_seen; - int n; + enum { RQ_DONE, RQ_INPROGRESS, RQ_QUEUED, RQ_IDLE } status; + struct iovec* tx_ciov; + size_t tx_totallen; + size_t tx_iovlen; + struct iovec tx_iov[3]; + u_int16_t tx_type; + u_int32_t sign[6]; +}; + +void ncp_tcp_data_ready(struct sock *sk, int len) { + struct ncp_server *server = sk->user_data; + + server->data_ready(sk, len); + schedule_task(&server->rcv.tq); +} - /* 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; - sock = SOCKET_I(file->f_dentry->d_inode); - - init_timeout = server->m.time_out; - max_timeout = NCP_MAX_RPC_TIMEOUT; - retrans = server->m.retry_count; - major_timeout_seen = 0; - acknowledge_seen = 0; - - 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 = _send(sock, (void *) start, size); - if (result < 0) { - printk(KERN_ERR "ncp_rpc_call: send error = %d\n", result); - return result; +void ncp_tcp_error_report(struct sock *sk) { + struct ncp_server *server = sk->user_data; + + server->error_report(sk); + schedule_task(&server->rcv.tq); +} + +void ncp_tcp_write_space(struct sock *sk) { + struct ncp_server *server = sk->user_data; + + /* We do not need any locking: we first set tx.creq, and then we do sendmsg, + not vice versa... */ + server->write_space(sk); + if (server->tx.creq) { + schedule_task(&server->tx.tq); + } +} + +void ncpdgram_timeout_call(unsigned long v) { + struct ncp_server *server = (void*)v; + + schedule_task(&server->timeout_tq); +} + +static inline void ncp_finish_request(struct ncp_request_reply *req, int result) { + req->result = result; + req->status = RQ_DONE; + wake_up_all(&req->wq); +} + +static void __abort_ncp_connection(struct ncp_server *server, struct ncp_request_reply *aborted, int err) { + struct ncp_request_reply *req; + + ncp_invalidate_conn(server); + del_timer(&server->timeout_tm); + while (!list_empty(&server->tx.requests)) { + req = list_entry(server->tx.requests.next, struct ncp_request_reply, req); + + list_del_init(&req->req); + if (req == aborted) { + ncp_finish_request(req, err); + } else { + ncp_finish_request(req, -EIO); } - re_select: - poll_initwait(&wait_table); - /* mb() is not necessary because ->poll() will serialize - instructions adding the wait_table waitqueues in the - waitqueue-head before going to calculate the mask-retval. */ - __set_current_state(TASK_INTERRUPTIBLE); - if (!(sock->ops->poll(file, sock, &wait_table) & POLLIN)) { - int timed_out; - if (timeout > max_timeout) { - /* JEJB/JSP 2/7/94 - * This is useful to see if the system is - * hanging */ - if (acknowledge_seen == 0) { - printk(KERN_WARNING "NCP max timeout\n"); - } - timeout = max_timeout; - } - timed_out = !schedule_timeout(timeout); - poll_freewait(&wait_table); - current->state = TASK_RUNNING; - if (signal_pending(current)) { - result = -ERESTARTSYS; - break; - } - if(wait_table.error) { - result = wait_table.error; - break; - } - if (timed_out) { - if (n < retrans) - continue; - if (server->m.flags & NCP_MOUNT_SOFT) { - printk(KERN_WARNING "NCP server not responding\n"); - result = -EIO; - break; - } - n = 0; - timeout = init_timeout; - if (init_timeout < max_timeout) - init_timeout <<= 1; - if (!major_timeout_seen) { - printk(KERN_WARNING "NCP server not responding\n"); - } - major_timeout_seen = 1; - continue; - } + } + req = server->rcv.creq; + if (req) { + server->rcv.creq = NULL; + if (req == aborted) { + ncp_finish_request(req, err); } else { - poll_freewait(&wait_table); + ncp_finish_request(req, -EIO); } - current->state = TASK_RUNNING; + server->rcv.ptr = NULL; + server->rcv.state = 0; + } + req = server->tx.creq; + if (req) { + server->tx.creq = NULL; + if (req == aborted) { + ncp_finish_request(req, err); + } else { + ncp_finish_request(req, -EIO); + } + } +} - /* 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 = _recv(sock, (void *) &reply, sizeof(reply), - MSG_PEEK | MSG_DONTWAIT); - if (result < 0) { - if (result == -EAGAIN) { - DDPRINTK("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(KERN_ERR "ncp_rpc_call: recv error = %d\n", - -result); - } +static inline int get_conn_number(struct ncp_reply_header *rp) { + return rp->conn_low | (rp->conn_high << 8); +} + +static inline void __ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err) { + /* If req is done, we got signal, but we also received answer... */ + switch (req->status) { + case RQ_IDLE: + case RQ_DONE: break; - } - if ((result == sizeof(reply)) - && (reply.type == NCP_POSITIVE_ACK)) { - /* Throw away the packet */ - DPRINTK("ncp_rpc_call: got positive acknowledge\n"); - _recv(sock, (void *) &reply, sizeof(reply), - MSG_DONTWAIT); - 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(KERN_NOTICE "NCP server OK\n"); + case RQ_QUEUED: + list_del_init(&req->req); + ncp_finish_request(req, err); + break; + case RQ_INPROGRESS: + __abort_ncp_connection(server, req, err); 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. */ - _recv(sock, (void *) &reply, sizeof(reply), MSG_DONTWAIT); - - 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 = _recv(sock, (void *)reply_buf, max_reply_size, MSG_DONTWAIT); +} + +static inline void ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err) { + down(&server->rcv.creq_sem); + __ncp_abort_request(server, req, err); + up(&server->rcv.creq_sem); +} + +static inline void __ncptcp_abort(struct ncp_server *server) { + __abort_ncp_connection(server, NULL, 0); +} + +static int ncpdgram_send(struct socket *sock, struct ncp_request_reply *req) { + struct msghdr msg; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + msg.msg_iov = req->tx_ciov; + msg.msg_iovlen = req->tx_iovlen; + msg.msg_flags = MSG_DONTWAIT; + return sock_sendmsg(sock, &msg, req->tx_totallen); +} + +static void __ncptcp_try_send(struct ncp_server *server) { + struct ncp_request_reply *rq; + struct msghdr msg; + struct iovec* iov; + int result; + + rq = server->tx.creq; + if (!rq) { + return; + } + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + msg.msg_iov = rq->tx_ciov; + msg.msg_iovlen = rq->tx_iovlen; + msg.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT; + result = sock_sendmsg(server->ncp_sock, &msg, rq->tx_totallen); + if (result == -EAGAIN) { + return; + } if (result < 0) { - printk(KERN_WARNING "NCP: notice message: result=%d\n", result); - } else if (result < sizeof(struct ncp_reply_header)) { - printk(KERN_ERR "NCP: just caught a too small read memory size..., " - "email to NET channel\n"); - printk(KERN_ERR "NCP: result=%d\n", result); - result = -EIO; + printk(KERN_ERR "ncpfs: tcp: Send failed: %d\n", result); + __ncp_abort_request(server, rq, result); + return; + } + if (result >= rq->tx_totallen) { + server->rcv.creq = rq; + server->tx.creq = NULL; + return; } + rq->tx_totallen -= result; + iov = rq->tx_ciov; + while (iov->iov_len <= result) { + result -= iov->iov_len; + iov++; + rq->tx_iovlen--; + } + iov->iov_len -= result; + rq->tx_ciov = iov; +} - return result; +static inline void ncp_init_header(struct ncp_server *server, struct ncp_request_reply *req, struct ncp_request_header *h) { + req->status = RQ_INPROGRESS; + h->conn_low = server->connection; + h->conn_high = server->connection >> 8; + h->sequence = ++server->sequence; +} + +static void ncpdgram_start_request(struct ncp_server *server, struct ncp_request_reply *req) { + size_t signlen; + struct ncp_request_header* h; + + req->tx_ciov = req->tx_iov + 1; + + h = req->tx_iov[1].iov_base; + ncp_init_header(server, req, h); + signlen = sign_packet(server, req->tx_iov[1].iov_base + sizeof(struct ncp_request_header) - 1, + req->tx_iov[1].iov_len - sizeof(struct ncp_request_header) + 1, + cpu_to_le32(req->tx_totallen), req->sign); + if (signlen) { + req->tx_ciov[1].iov_base = req->sign; + req->tx_ciov[1].iov_len = signlen; + req->tx_iovlen += 1; + req->tx_totallen += signlen; + } + server->rcv.creq = req; + server->timeout_last = server->m.time_out; + server->timeout_retries = server->m.retry_count; + ncpdgram_send(server->ncp_sock, req); + mod_timer(&server->timeout_tm, jiffies + server->m.time_out); } -static int do_tcp_rcv(struct ncp_server *server, void *buffer, size_t len) { - poll_table wait_table; - struct file *file; - struct socket *sock; - int init_timeout; - size_t dataread; - int result = 0; +#define NCP_TCP_XMIT_MAGIC (0x446D6454) +#define NCP_TCP_XMIT_VERSION (1) +#define NCP_TCP_RCVD_MAGIC (0x744E6350) + +static void ncptcp_start_request(struct ncp_server *server, struct ncp_request_reply *req) { + size_t signlen; + struct ncp_request_header* h; + + req->tx_ciov = req->tx_iov; + h = req->tx_iov[1].iov_base; + ncp_init_header(server, req, h); + signlen = sign_packet(server, req->tx_iov[1].iov_base + sizeof(struct ncp_request_header) - 1, + req->tx_iov[1].iov_len - sizeof(struct ncp_request_header) + 1, + cpu_to_be32(req->tx_totallen + 24), req->sign + 4) + 16; + + req->sign[0] = htonl(NCP_TCP_XMIT_MAGIC); + req->sign[1] = htonl(req->tx_totallen + signlen); + req->sign[2] = htonl(NCP_TCP_XMIT_VERSION); + req->sign[3] = htonl(req->datalen + 8); + req->tx_iov[0].iov_base = req->sign; + req->tx_iov[0].iov_len = signlen; + req->tx_iovlen += 1; + req->tx_totallen += signlen; + + server->tx.creq = req; + __ncptcp_try_send(server); +} + +static inline void __ncp_start_request(struct ncp_server *server, struct ncp_request_reply *req) { + if (server->ncp_sock->type == SOCK_STREAM) + ncptcp_start_request(server, req); + else + ncpdgram_start_request(server, req); +} + +static int ncp_add_request(struct ncp_server *server, struct ncp_request_reply *req) { + down(&server->rcv.creq_sem); + if (!ncp_conn_valid(server)) { + up(&server->rcv.creq_sem); + printk(KERN_ERR "ncpfs: tcp: Server died\n"); + return -EIO; + } + if (server->tx.creq || server->rcv.creq) { + req->status = RQ_QUEUED; + list_add_tail(&req->req, &server->tx.requests); + up(&server->rcv.creq_sem); + return 0; + } + __ncp_start_request(server, req); + up(&server->rcv.creq_sem); + return 0; +} + +static void __ncp_next_request(struct ncp_server *server) { + struct ncp_request_reply *req; + + server->rcv.creq = NULL; + if (list_empty(&server->tx.requests)) { + return; + } + req = list_entry(server->tx.requests.next, struct ncp_request_reply, req); + list_del_init(&req->req); + __ncp_start_request(server, req); +} + +static void info_server(struct ncp_server *server, unsigned int id, const void * data, size_t len) { + if (server->info_sock) { + struct iovec iov[2]; + struct msghdr msg; + __u32 hdr[2]; - file = server->ncp_filp; - sock = SOCKET_I(file->f_dentry->d_inode); + hdr[0] = cpu_to_be32(len + 8); + hdr[1] = cpu_to_be32(id); - dataread = 0; + iov[0].iov_base = hdr; + iov[0].iov_len = 8; + iov[1].iov_base = (void *) data; + iov[1].iov_len = len; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + msg.msg_iov = iov; + msg.msg_iovlen = 2; + msg.msg_flags = MSG_NOSIGNAL; + + sock_sendmsg(server->info_sock, &msg, len + 8); + } +} - init_timeout = server->m.time_out * 20; +static void __ncpdgram_rcv_proc(void *s) { + struct ncp_server *server = s; + struct socket* sock; + + sock = server->ncp_sock; - /* hard-mounted volumes have no timeout, except connection close... */ - if (!(server->m.flags & NCP_MOUNT_SOFT)) - init_timeout = 0x7FFF0000; - - while (len) { - poll_initwait(&wait_table); - /* mb() is not necessary because ->poll() will serialize - instructions adding the wait_table waitqueues in the - waitqueue-head before going to calculate the mask-retval. */ - __set_current_state(TASK_INTERRUPTIBLE); - if (!(sock->ops->poll(file, sock, &wait_table) & POLLIN)) { - init_timeout = schedule_timeout(init_timeout); - poll_freewait(&wait_table); - current->state = TASK_RUNNING; - if (signal_pending(current)) { - return -ERESTARTSYS; + while (1) { + struct ncp_reply_header reply; + int result; + + result = _recv(sock, (void*)&reply, sizeof(reply), MSG_PEEK | MSG_DONTWAIT); + if (result < 0) { + break; + } + if (result >= sizeof(reply)) { + struct ncp_request_reply *req; + + if (reply.type == NCP_WATCHDOG) { + unsigned char buf[10]; + + if (server->connection != get_conn_number(&reply)) { + goto drop; + } + result = _recv(sock, buf, sizeof(buf), MSG_DONTWAIT); + if (result < 0) { + DPRINTK("recv failed with %d\n", result); + continue; + } + if (result < 10) { + DPRINTK("too short (%u) watchdog packet\n", result); + continue; + } + if (buf[9] != '?') { + DPRINTK("bad signature (%02X) in watchdog packet\n", buf[9]); + continue; + } + buf[9] = 'Y'; + _send(sock, buf, sizeof(buf)); + continue; } - if (!init_timeout) { - return -EIO; + if (reply.type != NCP_POSITIVE_ACK && reply.type != NCP_REPLY) { + result = _recv(sock, server->unexpected_packet.data, sizeof(server->unexpected_packet.data), MSG_DONTWAIT); + if (result < 0) { + continue; + } + info_server(server, 0, server->unexpected_packet.data, result); + continue; } - if(wait_table.error) { - return wait_table.error; + down(&server->rcv.creq_sem); + req = server->rcv.creq; + if (req && (req->tx_type == NCP_ALLOC_SLOT_REQUEST || (server->sequence == reply.sequence && + server->connection == get_conn_number(&reply)))) { + if (reply.type == NCP_POSITIVE_ACK) { + server->timeout_retries = server->m.retry_count; + server->timeout_last = NCP_MAX_RPC_TIMEOUT; + mod_timer(&server->timeout_tm, jiffies + NCP_MAX_RPC_TIMEOUT); + } else if (reply.type == NCP_REPLY) { + result = _recv(sock, (void*)req->reply_buf, req->datalen, MSG_DONTWAIT); +#ifdef CONFIG_NCPFS_PACKET_SIGNING + if (result >= 0 && server->sign_active && req->tx_type != NCP_DEALLOC_SLOT_REQUEST) { + if (result < 8 + 8) { + result = -EIO; + } else { + unsigned int hdrl; + + result -= 8; + hdrl = sock->sk->family == AF_INET ? 8 : 6; + if (sign_verify_reply(server, ((char*)req->reply_buf) + hdrl, result - hdrl, cpu_to_le32(result), ((char*)req->reply_buf) + result)) { + printk(KERN_INFO "ncpfs: Signature violation\n"); + result = -EIO; + } + } + } +#endif + del_timer(&server->timeout_tm); + server->rcv.creq = NULL; + ncp_finish_request(req, result); + __ncp_next_request(server); + up(&server->rcv.creq_sem); + continue; + } } - } else { - poll_freewait(&wait_table); + up(&server->rcv.creq_sem); } - current->state = TASK_RUNNING; +drop:; + _recv(sock, (void*)&reply, sizeof(reply), MSG_DONTWAIT); + } +} - result = _recv(sock, buffer, len, MSG_DONTWAIT); - if (result < 0) { - if (result == -EAGAIN) { - DDPRINTK("ncpfs: tcp: bad select ready\n"); - continue; +void ncpdgram_rcv_proc(void *s) { + mm_segment_t fs; + struct ncp_server *server = s; + + fs = get_fs(); + set_fs(get_ds()); + __ncpdgram_rcv_proc(server); + set_fs(fs); +} + +static void __ncpdgram_timeout_proc(struct ncp_server *server) { + /* If timer is pending, we are processing another request... */ + if (!timer_pending(&server->timeout_tm)) { + struct ncp_request_reply* req; + + req = server->rcv.creq; + if (req) { + int timeout; + + if (server->m.flags & NCP_MOUNT_SOFT) { + if (server->timeout_retries-- == 0) { + __ncp_abort_request(server, req, -ETIMEDOUT); + return; + } } - return result; - } - if (result == 0) { - printk(KERN_ERR "ncpfs: tcp: EOF on socket\n"); - return -EIO; - } - if (result > len) { - printk(KERN_ERR "ncpfs: tcp: bug in recvmsg\n"); - return -EIO; + /* Ignore errors */ + ncpdgram_send(server->ncp_sock, req); + timeout = server->timeout_last << 1; + if (timeout > NCP_MAX_RPC_TIMEOUT) { + timeout = NCP_MAX_RPC_TIMEOUT; + } + server->timeout_last = timeout; + mod_timer(&server->timeout_tm, jiffies + timeout); } - dataread += result; - buffer += result; - len -= result; } - return 0; -} - -#define NCP_TCP_XMIT_MAGIC (0x446D6454) -#define NCP_TCP_XMIT_VERSION (1) -#define NCP_TCP_RCVD_MAGIC (0x744E6350) +} -static int do_ncp_tcp_rpc_call(struct ncp_server *server, int size, - struct ncp_reply_header* reply_buf, int max_reply_size) -{ - struct file *file; - struct socket *sock; - int result; - struct iovec iov[2]; - struct msghdr msg; - struct scm_cookie scm; - __u32 ncptcp_rcvd_hdr[2]; - __u32 ncptcp_xmit_hdr[4]; - int datalen; +void ncpdgram_timeout_proc(void *s) { + mm_segment_t fs; + struct ncp_server *server = s; + + fs = get_fs(); + set_fs(get_ds()); + down(&server->rcv.creq_sem); + __ncpdgram_timeout_proc(server); + up(&server->rcv.creq_sem); + set_fs(fs); +} - /* We have to check the result, so store the complete header */ - struct ncp_request_header request = - *((struct ncp_request_header *) (server->packet)); +static inline void ncp_init_req(struct ncp_request_reply* req) { + init_waitqueue_head(&req->wq); + req->status = RQ_IDLE; +} - file = server->ncp_filp; - sock = SOCKET_I(file->f_dentry->d_inode); +static int do_tcp_rcv(struct ncp_server *server, void *buffer, size_t len) { + int result; - ncptcp_xmit_hdr[0] = htonl(NCP_TCP_XMIT_MAGIC); - ncptcp_xmit_hdr[1] = htonl(size + 16); - ncptcp_xmit_hdr[2] = htonl(NCP_TCP_XMIT_VERSION); - ncptcp_xmit_hdr[3] = htonl(max_reply_size + 8); - - 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); - - iov[1].iov_base = (void *) server->packet; - iov[1].iov_len = size; - iov[0].iov_base = ncptcp_xmit_hdr; - iov[0].iov_len = 16; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_control = NULL; - msg.msg_iov = iov; - msg.msg_iovlen = 2; - msg.msg_flags = MSG_NOSIGNAL; - - result = scm_send(sock, &msg, &scm); - if (result < 0) { - return result; + if (buffer) { + result = _recv(server->ncp_sock, buffer, len, MSG_DONTWAIT); + } else { + static unsigned char dummy[1024]; + + if (len > sizeof(dummy)) { + len = sizeof(dummy); + } + result = _recv(server->ncp_sock, dummy, len, MSG_DONTWAIT); } - result = sock->ops->sendmsg(sock, &msg, size + 16, &scm); - scm_destroy(&scm); if (result < 0) { - printk(KERN_ERR "ncpfs: tcp: Send failed: %d\n", result); return result; } -rstrcv: - result = do_tcp_rcv(server, ncptcp_rcvd_hdr, 8); - if (result) - return result; - if (ncptcp_rcvd_hdr[0] != htonl(NCP_TCP_RCVD_MAGIC)) { - printk(KERN_ERR "ncpfs: tcp: Unexpected reply type %08X\n", ntohl(ncptcp_rcvd_hdr[0])); - return -EIO; + if (result > len) { + printk(KERN_ERR "ncpfs: tcp: bug in recvmsg (%u > %u)\n", result, len); + return -EIO; } - datalen = ntohl(ncptcp_rcvd_hdr[1]); - if (datalen < 8 + sizeof(*reply_buf) || datalen > max_reply_size + 8) { - printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d\n", datalen); - return -EIO; + return result; +} + +static int __ncptcp_rcv_proc(struct ncp_server *server) { + /* We have to check the result, so store the complete header */ + while (1) { + int result; + struct ncp_request_reply *req; + int datalen; + int type; + + while (server->rcv.len) { + result = do_tcp_rcv(server, server->rcv.ptr, server->rcv.len); + if (result == -EAGAIN) { + return 0; + } + if (result <= 0) { + req = server->rcv.creq; + if (req) { + __ncp_abort_request(server, req, -EIO); + } else { + __ncptcp_abort(server); + } + if (result < 0) { + printk(KERN_ERR "ncpfs: tcp: error in recvmsg: %d\n", result); + } else { + DPRINTK(KERN_ERR "ncpfs: tcp: EOF\n"); + } + return -EIO; + } + if (server->rcv.ptr) { + server->rcv.ptr += result; + } + server->rcv.len -= result; + } + switch (server->rcv.state) { + case 0: + if (server->rcv.buf.magic != htonl(NCP_TCP_RCVD_MAGIC)) { + printk(KERN_ERR "ncpfs: tcp: Unexpected reply type %08X\n", ntohl(server->rcv.buf.magic)); + __ncptcp_abort(server); + return -EIO; + } + datalen = ntohl(server->rcv.buf.len) & 0x0FFFFFFF; + if (datalen < 10) { + printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d\n", datalen); + __ncptcp_abort(server); + return -EIO; + } +#ifdef CONFIG_NCPFS_PACKET_SIGNING + if (server->sign_active) { + if (datalen < 18) { + printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d\n", datalen); + __ncptcp_abort(server); + return -EIO; + } + server->rcv.buf.len = datalen - 8; + server->rcv.ptr = (unsigned char*)&server->rcv.buf.p1; + server->rcv.len = 8; + server->rcv.state = 4; + break; + } +#endif + type = ntohs(server->rcv.buf.type); +cont:; + if (type != NCP_REPLY) { + if (datalen - 8 <= sizeof(server->unexpected_packet.data)) { + *(__u16*)(server->unexpected_packet.data) = htons(type); + server->unexpected_packet.len = datalen - 8; + + server->rcv.state = 5; + server->rcv.ptr = server->unexpected_packet.data + 2; + server->rcv.len = datalen - 10; + break; + } + DPRINTK("ncpfs: tcp: Unexpected NCP type %02X\n", type); +skipdata2:; + server->rcv.state = 2; +skipdata:; + server->rcv.ptr = NULL; + server->rcv.len = datalen - 10; + break; + } + req = server->rcv.creq; + if (!req) { + DPRINTK(KERN_ERR "ncpfs: Reply without appropriate request\n"); + goto skipdata2; + } + if (datalen > req->datalen + 8) { + printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d (expected at most %d)\n", datalen, req->datalen + 8); + server->rcv.state = 3; + goto skipdata; + } + req->datalen = datalen - 8; + req->reply_buf->type = NCP_REPLY; + server->rcv.ptr = (unsigned char*)(req->reply_buf) + 2; + server->rcv.len = datalen - 10; + server->rcv.state = 1; + break; +#ifdef CONFIG_NCPFS_PACKET_SIGNING + case 4: + datalen = server->rcv.buf.len; + type = ntohs(server->rcv.buf.type2); + goto cont; +#endif + case 1: + req = server->rcv.creq; + if (req->tx_type != NCP_ALLOC_SLOT_REQUEST) { + if (req->reply_buf->sequence != server->sequence) { + printk(KERN_ERR "ncpfs: tcp: Bad sequence number\n"); + __ncp_abort_request(server, req, -EIO); + return -EIO; + } + if ((req->reply_buf->conn_low | (req->reply_buf->conn_high << 8)) != server->connection) { + printk(KERN_ERR "ncpfs: tcp: Connection number mismatch\n"); + __ncp_abort_request(server, req, -EIO); + return -EIO; + } + } +#ifdef CONFIG_NCPFS_PACKET_SIGNING + if (server->sign_active && req->tx_type != NCP_DEALLOC_SLOT_REQUEST) { + if (sign_verify_reply(server, (unsigned char*)(req->reply_buf) + 6, req->datalen - 6, cpu_to_be32(req->datalen + 16), &server->rcv.buf.type)) { + printk(KERN_ERR "ncpfs: tcp: Signature violation\n"); + __ncp_abort_request(server, req, -EIO); + return -EIO; + } + } +#endif + ncp_finish_request(req, req->datalen); + nextreq:; + __ncp_next_request(server); + case 2: + next:; + server->rcv.ptr = (unsigned char*)&server->rcv.buf; + server->rcv.len = 10; + server->rcv.state = 0; + break; + case 3: + ncp_finish_request(server->rcv.creq, -EIO); + goto nextreq; + case 5: + info_server(server, 0, server->unexpected_packet.data, server->unexpected_packet.len); + goto next; + } } - datalen -= 8; - result = do_tcp_rcv(server, reply_buf, datalen); - if (result) +} + +void ncp_tcp_rcv_proc(void *s) { + mm_segment_t fs; + struct ncp_server *server = s; + + fs = get_fs(); + set_fs(get_ds()); + down(&server->rcv.creq_sem); + __ncptcp_rcv_proc(server); + up(&server->rcv.creq_sem); + set_fs(fs); + return; +} + +void ncp_tcp_tx_proc(void *s) { + mm_segment_t fs; + struct ncp_server *server = s; + + fs = get_fs(); + set_fs(get_ds()); + down(&server->rcv.creq_sem); + __ncptcp_try_send(server); + up(&server->rcv.creq_sem); + set_fs(fs); + return; +} + +static int do_ncp_rpc_call(struct ncp_server *server, int size, + struct ncp_reply_header* reply_buf, int max_reply_size) +{ + int result; + struct ncp_request_reply req; + + ncp_init_req(&req); + req.reply_buf = reply_buf; + req.datalen = max_reply_size; + req.tx_iov[1].iov_base = (void *) server->packet; + req.tx_iov[1].iov_len = size; + req.tx_iovlen = 1; + req.tx_totallen = size; + req.tx_type = *(u_int16_t*)server->packet; + + result = ncp_add_request(server, &req); + if (result < 0) { return result; - if (reply_buf->type != NCP_REPLY) { - DDPRINTK("ncpfs: tcp: Unexpected NCP type %02X\n", reply_buf->type); - goto rstrcv; } - if (request.type == NCP_ALLOC_SLOT_REQUEST) - return datalen; - if (reply_buf->sequence != request.sequence) { - printk(KERN_ERR "ncpfs: tcp: Bad sequence number\n"); - return -EIO; + if (wait_event_interruptible(req.wq, req.status == RQ_DONE)) { + ncp_abort_request(server, &req, -EIO); } - if ((reply_buf->conn_low != request.conn_low) || - (reply_buf->conn_high != request.conn_high)) { - printk(KERN_ERR "ncpfs: tcp: Connection number mismatch\n"); - return -EIO; - } - return datalen; + return req.result; } /* @@ -426,8 +728,6 @@ rstrcv: static int ncp_do_request(struct ncp_server *server, int size, void* reply, int max_reply_size) { - struct file *file; - struct socket *sock; int result; if (server->lock == 0) { @@ -435,21 +735,10 @@ static int ncp_do_request(struct ncp_server *server, int size, return -EIO; } if (!ncp_conn_valid(server)) { + printk(KERN_ERR "ncpfs: Connection invalid!\n"); return -EIO; } -#ifdef CONFIG_NCPFS_PACKET_SIGNING - if (server->sign_active) { - sign_packet(server, &size); - } -#endif /* CONFIG_NCPFS_PACKET_SIGNING */ - file = server->ncp_filp; - sock = SOCKET_I(file->f_dentry->d_inode); - /* N.B. this isn't needed ... check socket type? */ - if (!sock) { - printk(KERN_ERR "ncp_rpc_call: socki_lookup failed\n"); - result = -EBADF; - } else { mm_segment_t fs; sigset_t old_set; unsigned long mask, flags; @@ -478,10 +767,7 @@ static int ncp_do_request(struct ncp_server *server, int size, fs = get_fs(); set_fs(get_ds()); - if (sock->type == SOCK_STREAM) - result = do_ncp_tcp_rpc_call(server, size, reply, max_reply_size); - else - result = do_ncp_rpc_call(server, size, reply, max_reply_size); + result = do_ncp_rpc_call(server, size, reply, max_reply_size); set_fs(fs); @@ -510,20 +796,13 @@ int ncp_request2(struct ncp_server *server, int function, { struct ncp_request_header *h; struct ncp_reply_header* reply = rpl; - int request_size = server->current_size - - sizeof(struct ncp_request_header); int result; h = (struct ncp_request_header *) (server->packet); if (server->has_subfunction != 0) { - *(__u16 *) & (h->data[0]) = htons(request_size - 2); + *(__u16 *) & (h->data[0]) = htons(server->current_size - sizeof(*h) - 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; /* * The server shouldn't know or care what task is making a * request, so we always use the same task number. @@ -531,7 +810,7 @@ int ncp_request2(struct ncp_server *server, int function, h->task = 2; /* (current->pid) & 0xff; */ h->function = function; - result = ncp_do_request(server, request_size + sizeof(*h), reply, size); + result = ncp_do_request(server, server->current_size, reply, size); if (result < 0) { DPRINTK("ncp_request_error: %d\n", result); goto out; @@ -554,20 +833,17 @@ int ncp_connect(struct ncp_server *server) struct ncp_request_header *h; int result; + server->connection = 0xFFFF; + server->sequence = 255; + h = (struct ncp_request_header *) (server->packet); h->type = NCP_ALLOC_SLOT_REQUEST; - - server->sequence = 0; - h->sequence = server->sequence; - h->conn_low = 0xff; - h->conn_high = 0xff; h->task = 2; /* see above */ h->function = 0; result = ncp_do_request(server, sizeof(*h), server->packet, server->packet_size); if (result < 0) goto out; - server->sequence = 0; server->connection = h->conn_low + (h->conn_high * 256); result = 0; out: @@ -580,11 +856,6 @@ int ncp_disconnect(struct ncp_server *server) 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; /* see above */ h->function = 0; diff --git a/include/linux/ncp.h b/include/linux/ncp.h index fa20708267b9..92062440abb0 100644 --- a/include/linux/ncp.h +++ b/include/linux/ncp.h @@ -30,6 +30,7 @@ struct ncp_request_header { }; #define NCP_REPLY (0x3333) +#define NCP_WATCHDOG (0x3E3E) #define NCP_POSITIVE_ACK (0x9999) struct ncp_reply_header { diff --git a/include/linux/ncp_fs_sb.h b/include/linux/ncp_fs_sb.h index 7bc6b250cdd5..02ef9bb50b51 100644 --- a/include/linux/ncp_fs_sb.h +++ b/include/linux/ncp_fs_sb.h @@ -13,6 +13,8 @@ #ifdef __KERNEL__ +#include <linux/tqueue.h> + #define NCP_DEFAULT_OPTIONS 0 /* 2 for packet signatures */ struct ncp_server { @@ -24,6 +26,9 @@ struct ncp_server { __u8 name_space[NCP_NUMBER_OF_VOLUMES + 2]; struct file *ncp_filp; /* File pointer to ncp socket */ + struct socket *ncp_sock;/* ncp socket */ + struct file *info_filp; + struct socket *info_sock; u8 sequence; u8 task; @@ -79,8 +84,54 @@ struct ncp_server { /* miscellaneous */ unsigned int flags; + + spinlock_t requests_lock; /* Lock accesses to tx.requests, tx.creq and rcv.creq when STREAM mode */ + + void (*data_ready)(struct sock* sk, int len); + void (*error_report)(struct sock* sk); + void (*write_space)(struct sock* sk); /* STREAM mode only */ + struct { + struct tq_struct tq; /* STREAM/DGRAM: data/error ready */ + struct ncp_request_reply* creq; /* STREAM/DGRAM: awaiting reply from this request */ + struct semaphore creq_sem; /* DGRAM only: lock accesses to rcv.creq */ + + unsigned int state; /* STREAM only: receiver state */ + struct { + __u32 magic __attribute__((packed)); + __u32 len __attribute__((packed)); + __u16 type __attribute__((packed)); + __u16 p1 __attribute__((packed)); + __u16 p2 __attribute__((packed)); + __u16 p3 __attribute__((packed)); + __u16 type2 __attribute__((packed)); + } buf; /* STREAM only: temporary buffer */ + unsigned char* ptr; /* STREAM only: pointer to data */ + size_t len; /* STREAM only: length of data to receive */ + } rcv; + struct { + struct list_head requests; /* STREAM only: queued requests */ + struct tq_struct tq; /* STREAM only: transmitter ready */ + struct ncp_request_reply* creq; /* STREAM only: currently transmitted entry */ + } tx; + struct timer_list timeout_tm; /* DGRAM only: timeout timer */ + struct tq_struct timeout_tq; /* DGRAM only: associated queue, we run timers from process context */ + int timeout_last; /* DGRAM only: current timeout length */ + int timeout_retries; /* DGRAM only: retries left */ + struct { + size_t len; + __u8 data[128]; + } unexpected_packet; }; +extern void ncp_tcp_rcv_proc(void *server); +extern void ncp_tcp_tx_proc(void *server); +extern void ncpdgram_rcv_proc(void *server); +extern void ncpdgram_timeout_proc(void *server); +extern void ncpdgram_timeout_call(unsigned long server); +extern void ncp_tcp_data_ready(struct sock* sk, int len); +extern void ncp_tcp_write_space(struct sock* sk); +extern void ncp_tcp_error_report(struct sock* sk); + #define NCP_FLAG_UTF8 1 #define NCP_CLR_FLAG(server, flag) ((server)->flags &= ~(flag)) diff --git a/include/linux/ncp_mount.h b/include/linux/ncp_mount.h index 4bdcfff1f79e..f46bddcdbd3b 100644 --- a/include/linux/ncp_mount.h +++ b/include/linux/ncp_mount.h @@ -11,7 +11,7 @@ #include <linux/types.h> #include <linux/ncp.h> -#define NCP_MOUNT_VERSION 3 +#define NCP_MOUNT_VERSION 3 /* Binary */ /* Values for flags */ #define NCP_MOUNT_SOFT 0x0001 @@ -41,7 +41,7 @@ struct ncp_mount_data { __kernel_mode_t dir_mode; }; -#define NCP_MOUNT_VERSION_V4 (4) +#define NCP_MOUNT_VERSION_V4 (4) /* Binary or text */ struct ncp_mount_data_v4 { int version; @@ -66,6 +66,8 @@ struct ncp_mount_data_v4 { unsigned long dir_mode; }; +#define NCP_MOUNT_VERSION_V5 (5) /* Text only */ + #ifdef __KERNEL__ struct ncp_mount_data_kernel { @@ -83,6 +85,7 @@ struct ncp_mount_data_kernel { __kernel_gid32_t gid; __kernel_mode_t file_mode; __kernel_mode_t dir_mode; + int info_fd; }; #endif /* __KERNEL__ */ |
