Commit aaf390a1 authored by Linus Torvalds's avatar Linus Torvalds

Merge http://ncpfs.bkbits.net/linux-2.5

into penguin.transmeta.com:/home/penguin/torvalds/repositories/kernel/linux
parents 985dcc74 3034ecfc
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
obj-$(CONFIG_NCP_FS) += ncpfs.o obj-$(CONFIG_NCP_FS) += ncpfs.o
ncpfs-objs := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.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) ifeq ($(CONFIG_NCPFS_EXTRAS),y)
ncpfs-objs += symlink.o ncpfs-objs += symlink.o
endif endif
......
/*
* 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;
}
#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 */
...@@ -31,7 +31,10 @@ ...@@ -31,7 +31,10 @@
#include <linux/ncp_fs.h> #include <linux/ncp_fs.h>
#include <net/sock.h>
#include "ncplib_kernel.h" #include "ncplib_kernel.h"
#include "getopt.h"
static void ncp_delete_inode(struct inode *); static void ncp_delete_inode(struct inode *);
static void ncp_put_super(struct super_block *); static void ncp_put_super(struct super_block *);
...@@ -283,6 +286,103 @@ ncp_delete_inode(struct inode *inode) ...@@ -283,6 +286,103 @@ ncp_delete_inode(struct inode *inode)
clear_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) static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
{ {
struct ncp_mount_data_kernel data; struct ncp_mount_data_kernel data;
...@@ -323,6 +423,7 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) ...@@ -323,6 +423,7 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
data.gid = md->gid; data.gid = md->gid;
data.file_mode = md->file_mode; data.file_mode = md->file_mode;
data.dir_mode = md->dir_mode; data.dir_mode = md->dir_mode;
data.info_fd = -1;
memcpy(data.mounted_vol, md->mounted_vol, memcpy(data.mounted_vol, md->mounted_vol,
NCP_VOLNAME_LEN+1); NCP_VOLNAME_LEN+1);
} }
...@@ -342,12 +443,18 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) ...@@ -342,12 +443,18 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
data.gid = md->gid; data.gid = md->gid;
data.file_mode = md->file_mode; data.file_mode = md->file_mode;
data.dir_mode = md->dir_mode; data.dir_mode = md->dir_mode;
data.info_fd = -1;
data.mounted_vol[0] = 0; data.mounted_vol[0] = 0;
} }
break; break;
default: default:
error = -ECHRNG; 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; error = -EBADF;
ncp_filp = fget(data.ncp_fd); ncp_filp = fget(data.ncp_fd);
...@@ -376,6 +483,28 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) ...@@ -376,6 +483,28 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
memset(server, 0, sizeof(*server)); memset(server, 0, sizeof(*server));
server->ncp_filp = ncp_filp; 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; */ /* server->lock = 0; */
init_MUTEX(&server->sem); init_MUTEX(&server->sem);
server->packet = NULL; server->packet = NULL;
...@@ -413,6 +542,16 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) ...@@ -413,6 +542,16 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
server->dentry_ttl = 0; /* no caching */ 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 #undef NCP_PACKET_SIZE
#define NCP_PACKET_SIZE 131072 #define NCP_PACKET_SIZE 131072
error = -ENOMEM; error = -ENOMEM;
...@@ -421,6 +560,22 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) ...@@ -421,6 +560,22 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
if (server->packet == NULL) if (server->packet == NULL)
goto out_nls; 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); ncp_lock_server(server);
error = ncp_connect(server); error = ncp_connect(server);
ncp_unlock_server(server); ncp_unlock_server(server);
...@@ -495,12 +650,16 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) ...@@ -495,12 +650,16 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
ncp_disconnect(server); ncp_disconnect(server);
ncp_unlock_server(server); ncp_unlock_server(server);
out_packet: out_packet:
ncp_stop_tasks(server);
vfree(server->packet); vfree(server->packet);
out_nls: out_nls:
#ifdef CONFIG_NCPFS_NLS #ifdef CONFIG_NCPFS_NLS
unload_nls(server->nls_io); unload_nls(server->nls_io);
unload_nls(server->nls_vol); unload_nls(server->nls_vol);
#endif #endif
out_fput2:
if (server->info_filp)
fput(server->info_filp);
out_fput: out_fput:
/* 23/12/1998 Marcin Dalecki <dalecki@cs.net.pl>: /* 23/12/1998 Marcin Dalecki <dalecki@cs.net.pl>:
* *
...@@ -522,6 +681,8 @@ static void ncp_put_super(struct super_block *sb) ...@@ -522,6 +681,8 @@ static void ncp_put_super(struct super_block *sb)
ncp_disconnect(server); ncp_disconnect(server);
ncp_unlock_server(server); ncp_unlock_server(server);
ncp_stop_tasks(server);
#ifdef CONFIG_NCPFS_NLS #ifdef CONFIG_NCPFS_NLS
/* unload the NLS charsets */ /* unload the NLS charsets */
if (server->nls_vol) if (server->nls_vol)
...@@ -536,6 +697,8 @@ static void ncp_put_super(struct super_block *sb) ...@@ -536,6 +697,8 @@ static void ncp_put_super(struct super_block *sb)
} }
#endif /* CONFIG_NCPFS_NLS */ #endif /* CONFIG_NCPFS_NLS */
if (server->info_filp)
fput(server->info_filp);
fput(server->ncp_filp); fput(server->ncp_filp);
kill_proc(server->m.wdog_pid, SIGTERM, 1); kill_proc(server->m.wdog_pid, SIGTERM, 1);
......
...@@ -424,7 +424,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp, ...@@ -424,7 +424,7 @@ int ncp_ioctl(struct inode *inode, struct file *filp,
if (user.object_name_len) { if (user.object_name_len) {
newname = ncp_kmalloc(user.object_name_len, GFP_USER); newname = ncp_kmalloc(user.object_name_len, GFP_USER);
if (!newname) return -ENOMEM; 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); ncp_kfree_s(newname, user.object_name_len);
return -EFAULT; return -EFAULT;
} }
......
...@@ -341,6 +341,7 @@ void ncp_extract_file_info(void *structure, struct nw_info_struct *target) ...@@ -341,6 +341,7 @@ void ncp_extract_file_info(void *structure, struct nw_info_struct *target)
target->nameLen = *name_len; target->nameLen = *name_len;
memcpy(target->entryName, name_len + 1, *name_len); memcpy(target->entryName, name_len + 1, *name_len);
target->entryName[*name_len] = '\0'; target->entryName[*name_len] = '\0';
target->volNumber = le32_to_cpu(target->volNumber);
return; return;
} }
...@@ -475,7 +476,7 @@ ncp_get_known_namespace(struct ncp_server *server, __u8 volume) ...@@ -475,7 +476,7 @@ ncp_get_known_namespace(struct ncp_server *server, __u8 volume)
} }
result = NW_NS_DOS; 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); namespace = ncp_reply_data(server, 2);
while (no_namespaces > 0) { while (no_namespaces > 0) {
......
...@@ -93,19 +93,35 @@ static void nwsign(char *r_data1, char *r_data2, char *outdata) { ...@@ -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 */ /* Make a signature for the current packet and add it at the end of the */
/* packet. */ /* packet. */
void sign_packet(struct ncp_server *server, int *size) { void __sign_packet(struct ncp_server *server, const char *packet, size_t size, __u32 totalsize, void *sign_buff) {
char data[64]; unsigned char data[64];
memset(data,0,64); memcpy(data, server->sign_root, 8);
memcpy(data,server->sign_root,8); *(__u32*)(data + 8) = totalsize;
PUT_LE32(data+8,(*size)); if (size < 52) {
memcpy(data+12,server->packet+sizeof(struct ncp_request_header)-1, memcpy(data + 12, packet, size);
min_t(unsigned int,(*size)-sizeof(struct ncp_request_header)+1,52)); 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); memcpy(data, server->sign_root, 8);
(*size)+=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 */ #endif /* CONFIG_NCPFS_PACKET_SIGNING */
......
...@@ -10,6 +10,19 @@ ...@@ -10,6 +10,19 @@
#include <linux/ncp_fs.h> #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 #endif
...@@ -29,18 +29,13 @@ ...@@ -29,18 +29,13 @@
#include <linux/ncp_fs.h> #include <linux/ncp_fs.h>
#ifdef CONFIG_NCPFS_PACKET_SIGNING
#include "ncpsign_kernel.h" #include "ncpsign_kernel.h"
#endif
static int _recv(struct socket *sock, unsigned char *ubuf, int size, static int _recv(struct socket *sock, unsigned char *ubuf, int size,
unsigned flags) unsigned flags)
{ {
struct iovec iov; struct iovec iov;
struct msghdr msg; struct msghdr msg;
struct scm_cookie scm;
memset(&scm, 0, sizeof(scm));
iov.iov_base = ubuf; iov.iov_base = ubuf;
iov.iov_len = size; iov.iov_len = size;
...@@ -50,15 +45,14 @@ static int _recv(struct socket *sock, unsigned char *ubuf, int size, ...@@ -50,15 +45,14 @@ static int _recv(struct socket *sock, unsigned char *ubuf, int size,
msg.msg_control = NULL; msg.msg_control = NULL;
msg.msg_iov = &iov; msg.msg_iov = &iov;
msg.msg_iovlen = 1; 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 iovec iov;
struct msghdr msg; struct msghdr msg;
struct scm_cookie scm;
int err;
iov.iov_base = (void *) buff; iov.iov_base = (void *) buff;
iov.iov_len = len; iov.iov_len = len;
...@@ -70,353 +64,661 @@ static int _send(struct socket *sock, const void *buff, int len) ...@@ -70,353 +64,661 @@ static int _send(struct socket *sock, const void *buff, int len)
msg.msg_iovlen = 1; msg.msg_iovlen = 1;
msg.msg_flags = 0; msg.msg_flags = 0;
err = scm_send(sock, &msg, &scm); return sock_sendmsg(sock, &msg, len);
if (err < 0) {
return err;
}
err = sock->ops->sendmsg(sock, &msg, len, &scm);
scm_destroy(&scm);
return err;
} }
static int do_ncp_rpc_call(struct ncp_server *server, int size, struct ncp_request_reply {
struct ncp_reply_header* reply_buf, int max_reply_size) struct list_head req;
{ wait_queue_head_t wq;
struct file *file; struct ncp_reply_header* reply_buf;
struct socket *sock; size_t datalen;
int result; int result;
char *start = server->packet; enum { RQ_DONE, RQ_INPROGRESS, RQ_QUEUED, RQ_IDLE } status;
poll_table wait_table; struct iovec* tx_ciov;
int init_timeout, max_timeout; size_t tx_totallen;
int timeout; size_t tx_iovlen;
int retrans; struct iovec tx_iov[3];
int major_timeout_seen; u_int16_t tx_type;
int acknowledge_seen; u_int32_t sign[6];
int n; };
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 */ void ncp_tcp_error_report(struct sock *sk) {
struct ncp_request_header request = struct ncp_server *server = sk->user_data;
*((struct ncp_request_header *) (server->packet));
server->error_report(sk);
struct ncp_reply_header reply; schedule_task(&server->rcv.tq);
}
file = server->ncp_filp;
sock = SOCKET_I(file->f_dentry->d_inode); void ncp_tcp_write_space(struct sock *sk) {
struct ncp_server *server = sk->user_data;
init_timeout = server->m.time_out;
max_timeout = NCP_MAX_RPC_TIMEOUT; /* We do not need any locking: we first set tx.creq, and then we do sendmsg,
retrans = server->m.retry_count; not vice versa... */
major_timeout_seen = 0; server->write_space(sk);
acknowledge_seen = 0; if (server->tx.creq) {
schedule_task(&server->tx.tq);
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), void ncpdgram_timeout_call(unsigned long v) {
server->m.serv_addr.sipx_node[0], struct ncp_server *server = (void*)v;
server->m.serv_addr.sipx_node[1],
server->m.serv_addr.sipx_node[2], schedule_task(&server->timeout_tq);
server->m.serv_addr.sipx_node[3], }
server->m.serv_addr.sipx_node[4],
server->m.serv_addr.sipx_node[5], static inline void ncp_finish_request(struct ncp_request_reply *req, int result) {
ntohs(server->m.serv_addr.sipx_port)); req->result = result;
*/ req->status = RQ_DONE;
DDPRINTK("ncpfs: req.typ: %04X, con: %d, " wake_up_all(&req->wq);
"seq: %d", }
request.type,
(request.conn_high << 8) + request.conn_low, static void __abort_ncp_connection(struct ncp_server *server, struct ncp_request_reply *aborted, int err) {
request.sequence); struct ncp_request_reply *req;
DDPRINTK(" func: %d\n",
request.function); ncp_invalidate_conn(server);
del_timer(&server->timeout_tm);
result = _send(sock, (void *) start, size); while (!list_empty(&server->tx.requests)) {
if (result < 0) { req = list_entry(server->tx.requests.next, struct ncp_request_reply, req);
printk(KERN_ERR "ncp_rpc_call: send error = %d\n", result);
return result; 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); req = server->rcv.creq;
/* mb() is not necessary because ->poll() will serialize if (req) {
instructions adding the wait_table waitqueues in the server->rcv.creq = NULL;
waitqueue-head before going to calculate the mask-retval. */ if (req == aborted) {
__set_current_state(TASK_INTERRUPTIBLE); ncp_finish_request(req, err);
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;
}
} else { } 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 static inline int get_conn_number(struct ncp_reply_header *rp) {
* on the recv queue. If it is wrong, it will be some reply return rp->conn_low | (rp->conn_high << 8);
* we don't now need, so discard it */ }
result = _recv(sock, (void *) &reply, sizeof(reply),
MSG_PEEK | MSG_DONTWAIT); static inline void __ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err) {
if (result < 0) { /* If req is done, we got signal, but we also received answer... */
if (result == -EAGAIN) { switch (req->status) {
DDPRINTK("ncp_rpc_call: bad select ready\n"); case RQ_IDLE:
goto re_select; case RQ_DONE:
}
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);
}
break; break;
} case RQ_QUEUED:
if ((result == sizeof(reply)) list_del_init(&req->req);
&& (reply.type == NCP_POSITIVE_ACK)) { ncp_finish_request(req, err);
/* Throw away the packet */ break;
DPRINTK("ncp_rpc_call: got positive acknowledge\n"); case RQ_INPROGRESS:
_recv(sock, (void *) &reply, sizeof(reply), __abort_ncp_connection(server, req, err);
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");
break; 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 static inline void ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err) {
*/ down(&server->rcv.creq_sem);
result = _recv(sock, (void *)reply_buf, max_reply_size, MSG_DONTWAIT); __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) { if (result < 0) {
printk(KERN_WARNING "NCP: notice message: result=%d\n", result); printk(KERN_ERR "ncpfs: tcp: Send failed: %d\n", result);
} else if (result < sizeof(struct ncp_reply_header)) { __ncp_abort_request(server, rq, result);
printk(KERN_ERR "NCP: just caught a too small read memory size..., " return;
"email to NET channel\n"); }
printk(KERN_ERR "NCP: result=%d\n", result); if (result >= rq->tx_totallen) {
result = -EIO; 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) { #define NCP_TCP_XMIT_MAGIC (0x446D6454)
poll_table wait_table; #define NCP_TCP_XMIT_VERSION (1)
struct file *file; #define NCP_TCP_RCVD_MAGIC (0x744E6350)
struct socket *sock;
int init_timeout; static void ncptcp_start_request(struct ncp_server *server, struct ncp_request_reply *req) {
size_t dataread; size_t signlen;
int result = 0; 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; hdr[0] = cpu_to_be32(len + 8);
sock = SOCKET_I(file->f_dentry->d_inode); 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... */ while (1) {
if (!(server->m.flags & NCP_MOUNT_SOFT)) struct ncp_reply_header reply;
init_timeout = 0x7FFF0000; int result;
while (len) { result = _recv(sock, (void*)&reply, sizeof(reply), MSG_PEEK | MSG_DONTWAIT);
poll_initwait(&wait_table); if (result < 0) {
/* mb() is not necessary because ->poll() will serialize break;
instructions adding the wait_table waitqueues in the }
waitqueue-head before going to calculate the mask-retval. */ if (result >= sizeof(reply)) {
__set_current_state(TASK_INTERRUPTIBLE); struct ncp_request_reply *req;
if (!(sock->ops->poll(file, sock, &wait_table) & POLLIN)) {
init_timeout = schedule_timeout(init_timeout); if (reply.type == NCP_WATCHDOG) {
poll_freewait(&wait_table); unsigned char buf[10];
current->state = TASK_RUNNING;
if (signal_pending(current)) { if (server->connection != get_conn_number(&reply)) {
return -ERESTARTSYS; 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) { if (reply.type != NCP_POSITIVE_ACK && reply.type != NCP_REPLY) {
return -EIO; 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) { down(&server->rcv.creq_sem);
return wait_table.error; 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 { up(&server->rcv.creq_sem);
poll_freewait(&wait_table);
} }
current->state = TASK_RUNNING; drop:;
_recv(sock, (void*)&reply, sizeof(reply), MSG_DONTWAIT);
}
}
result = _recv(sock, buffer, len, MSG_DONTWAIT); void ncpdgram_rcv_proc(void *s) {
if (result < 0) { mm_segment_t fs;
if (result == -EAGAIN) { struct ncp_server *server = s;
DDPRINTK("ncpfs: tcp: bad select ready\n");
continue; 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; /* Ignore errors */
} ncpdgram_send(server->ncp_sock, req);
if (result == 0) { timeout = server->timeout_last << 1;
printk(KERN_ERR "ncpfs: tcp: EOF on socket\n"); if (timeout > NCP_MAX_RPC_TIMEOUT) {
return -EIO; timeout = NCP_MAX_RPC_TIMEOUT;
} }
if (result > len) { server->timeout_last = timeout;
printk(KERN_ERR "ncpfs: tcp: bug in recvmsg\n"); mod_timer(&server->timeout_tm, jiffies + timeout);
return -EIO;
} }
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, void ncpdgram_timeout_proc(void *s) {
struct ncp_reply_header* reply_buf, int max_reply_size) mm_segment_t fs;
{ struct ncp_server *server = s;
struct file *file;
struct socket *sock; fs = get_fs();
int result; set_fs(get_ds());
struct iovec iov[2]; down(&server->rcv.creq_sem);
struct msghdr msg; __ncpdgram_timeout_proc(server);
struct scm_cookie scm; up(&server->rcv.creq_sem);
__u32 ncptcp_rcvd_hdr[2]; set_fs(fs);
__u32 ncptcp_xmit_hdr[4]; }
int datalen;
/* We have to check the result, so store the complete header */ static inline void ncp_init_req(struct ncp_request_reply* req) {
struct ncp_request_header request = init_waitqueue_head(&req->wq);
*((struct ncp_request_header *) (server->packet)); req->status = RQ_IDLE;
}
file = server->ncp_filp; static int do_tcp_rcv(struct ncp_server *server, void *buffer, size_t len) {
sock = SOCKET_I(file->f_dentry->d_inode); int result;
ncptcp_xmit_hdr[0] = htonl(NCP_TCP_XMIT_MAGIC); if (buffer) {
ncptcp_xmit_hdr[1] = htonl(size + 16); result = _recv(server->ncp_sock, buffer, len, MSG_DONTWAIT);
ncptcp_xmit_hdr[2] = htonl(NCP_TCP_XMIT_VERSION); } else {
ncptcp_xmit_hdr[3] = htonl(max_reply_size + 8); static unsigned char dummy[1024];
DDPRINTK("ncpfs: req.typ: %04X, con: %d, " if (len > sizeof(dummy)) {
"seq: %d", len = sizeof(dummy);
request.type, }
(request.conn_high << 8) + request.conn_low, result = _recv(server->ncp_sock, dummy, len, MSG_DONTWAIT);
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;
} }
result = sock->ops->sendmsg(sock, &msg, size + 16, &scm);
scm_destroy(&scm);
if (result < 0) { if (result < 0) {
printk(KERN_ERR "ncpfs: tcp: Send failed: %d\n", result);
return result; return result;
} }
rstrcv: if (result > len) {
result = do_tcp_rcv(server, ncptcp_rcvd_hdr, 8); printk(KERN_ERR "ncpfs: tcp: bug in recvmsg (%u > %u)\n", result, len);
if (result) return -EIO;
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;
} }
datalen = ntohl(ncptcp_rcvd_hdr[1]); return result;
if (datalen < 8 + sizeof(*reply_buf) || datalen > max_reply_size + 8) { }
printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d\n", datalen);
return -EIO; 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; 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) if (wait_event_interruptible(req.wq, req.status == RQ_DONE)) {
return datalen; ncp_abort_request(server, &req, -EIO);
if (reply_buf->sequence != request.sequence) {
printk(KERN_ERR "ncpfs: tcp: Bad sequence number\n");
return -EIO;
} }
if ((reply_buf->conn_low != request.conn_low) || return req.result;
(reply_buf->conn_high != request.conn_high)) {
printk(KERN_ERR "ncpfs: tcp: Connection number mismatch\n");
return -EIO;
}
return datalen;
} }
/* /*
...@@ -426,8 +728,6 @@ static int do_ncp_tcp_rpc_call(struct ncp_server *server, int size, ...@@ -426,8 +728,6 @@ static int do_ncp_tcp_rpc_call(struct ncp_server *server, int size,
static int ncp_do_request(struct ncp_server *server, int size, static int ncp_do_request(struct ncp_server *server, int size,
void* reply, int max_reply_size) void* reply, int max_reply_size)
{ {
struct file *file;
struct socket *sock;
int result; int result;
if (server->lock == 0) { if (server->lock == 0) {
...@@ -435,21 +735,10 @@ static int ncp_do_request(struct ncp_server *server, int size, ...@@ -435,21 +735,10 @@ static int ncp_do_request(struct ncp_server *server, int size,
return -EIO; return -EIO;
} }
if (!ncp_conn_valid(server)) { if (!ncp_conn_valid(server)) {
printk(KERN_ERR "ncpfs: Connection invalid!\n");
return -EIO; 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; mm_segment_t fs;
sigset_t old_set; sigset_t old_set;
unsigned long mask, flags; unsigned long mask, flags;
...@@ -478,10 +767,7 @@ static int ncp_do_request(struct ncp_server *server, int size, ...@@ -478,10 +767,7 @@ static int ncp_do_request(struct ncp_server *server, int size,
fs = get_fs(); fs = get_fs();
set_fs(get_ds()); set_fs(get_ds());
if (sock->type == SOCK_STREAM) result = do_ncp_rpc_call(server, size, reply, max_reply_size);
result = do_ncp_tcp_rpc_call(server, size, reply, max_reply_size);
else
result = do_ncp_rpc_call(server, size, reply, max_reply_size);
set_fs(fs); set_fs(fs);
...@@ -510,20 +796,13 @@ int ncp_request2(struct ncp_server *server, int function, ...@@ -510,20 +796,13 @@ int ncp_request2(struct ncp_server *server, int function,
{ {
struct ncp_request_header *h; struct ncp_request_header *h;
struct ncp_reply_header* reply = rpl; struct ncp_reply_header* reply = rpl;
int request_size = server->current_size
- sizeof(struct ncp_request_header);
int result; int result;
h = (struct ncp_request_header *) (server->packet); h = (struct ncp_request_header *) (server->packet);
if (server->has_subfunction != 0) { 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; 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 * The server shouldn't know or care what task is making a
* request, so we always use the same task number. * request, so we always use the same task number.
...@@ -531,7 +810,7 @@ int ncp_request2(struct ncp_server *server, int function, ...@@ -531,7 +810,7 @@ int ncp_request2(struct ncp_server *server, int function,
h->task = 2; /* (current->pid) & 0xff; */ h->task = 2; /* (current->pid) & 0xff; */
h->function = function; 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) { if (result < 0) {
DPRINTK("ncp_request_error: %d\n", result); DPRINTK("ncp_request_error: %d\n", result);
goto out; goto out;
...@@ -554,20 +833,17 @@ int ncp_connect(struct ncp_server *server) ...@@ -554,20 +833,17 @@ int ncp_connect(struct ncp_server *server)
struct ncp_request_header *h; struct ncp_request_header *h;
int result; int result;
server->connection = 0xFFFF;
server->sequence = 255;
h = (struct ncp_request_header *) (server->packet); h = (struct ncp_request_header *) (server->packet);
h->type = NCP_ALLOC_SLOT_REQUEST; 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->task = 2; /* see above */
h->function = 0; h->function = 0;
result = ncp_do_request(server, sizeof(*h), server->packet, server->packet_size); result = ncp_do_request(server, sizeof(*h), server->packet, server->packet_size);
if (result < 0) if (result < 0)
goto out; goto out;
server->sequence = 0;
server->connection = h->conn_low + (h->conn_high * 256); server->connection = h->conn_low + (h->conn_high * 256);
result = 0; result = 0;
out: out:
...@@ -580,11 +856,6 @@ int ncp_disconnect(struct ncp_server *server) ...@@ -580,11 +856,6 @@ int ncp_disconnect(struct ncp_server *server)
h = (struct ncp_request_header *) (server->packet); h = (struct ncp_request_header *) (server->packet);
h->type = NCP_DEALLOC_SLOT_REQUEST; 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->task = 2; /* see above */
h->function = 0; h->function = 0;
......
...@@ -30,6 +30,7 @@ struct ncp_request_header { ...@@ -30,6 +30,7 @@ struct ncp_request_header {
}; };
#define NCP_REPLY (0x3333) #define NCP_REPLY (0x3333)
#define NCP_WATCHDOG (0x3E3E)
#define NCP_POSITIVE_ACK (0x9999) #define NCP_POSITIVE_ACK (0x9999)
struct ncp_reply_header { struct ncp_reply_header {
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/tqueue.h>
#define NCP_DEFAULT_OPTIONS 0 /* 2 for packet signatures */ #define NCP_DEFAULT_OPTIONS 0 /* 2 for packet signatures */
struct ncp_server { struct ncp_server {
...@@ -24,6 +26,9 @@ struct ncp_server { ...@@ -24,6 +26,9 @@ struct ncp_server {
__u8 name_space[NCP_NUMBER_OF_VOLUMES + 2]; __u8 name_space[NCP_NUMBER_OF_VOLUMES + 2];
struct file *ncp_filp; /* File pointer to ncp socket */ 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 sequence;
u8 task; u8 task;
...@@ -79,8 +84,54 @@ struct ncp_server { ...@@ -79,8 +84,54 @@ struct ncp_server {
/* miscellaneous */ /* miscellaneous */
unsigned int flags; 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_FLAG_UTF8 1
#define NCP_CLR_FLAG(server, flag) ((server)->flags &= ~(flag)) #define NCP_CLR_FLAG(server, flag) ((server)->flags &= ~(flag))
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/ncp.h> #include <linux/ncp.h>
#define NCP_MOUNT_VERSION 3 #define NCP_MOUNT_VERSION 3 /* Binary */
/* Values for flags */ /* Values for flags */
#define NCP_MOUNT_SOFT 0x0001 #define NCP_MOUNT_SOFT 0x0001
...@@ -41,7 +41,7 @@ struct ncp_mount_data { ...@@ -41,7 +41,7 @@ struct ncp_mount_data {
__kernel_mode_t dir_mode; __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 { struct ncp_mount_data_v4 {
int version; int version;
...@@ -66,6 +66,8 @@ struct ncp_mount_data_v4 { ...@@ -66,6 +66,8 @@ struct ncp_mount_data_v4 {
unsigned long dir_mode; unsigned long dir_mode;
}; };
#define NCP_MOUNT_VERSION_V5 (5) /* Text only */
#ifdef __KERNEL__ #ifdef __KERNEL__
struct ncp_mount_data_kernel { struct ncp_mount_data_kernel {
...@@ -83,6 +85,7 @@ struct ncp_mount_data_kernel { ...@@ -83,6 +85,7 @@ struct ncp_mount_data_kernel {
__kernel_gid32_t gid; __kernel_gid32_t gid;
__kernel_mode_t file_mode; __kernel_mode_t file_mode;
__kernel_mode_t dir_mode; __kernel_mode_t dir_mode;
int info_fd;
}; };
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment