Commit 32758c3b authored by Petr Vandrovec's avatar Petr Vandrovec

ncpfs: Proper handling of watchdog packets.

ncpfs: Add support for packet signatures when using TCP transport.
parent 8612e088
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#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" #include "getopt.h"
...@@ -284,6 +286,16 @@ ncp_delete_inode(struct inode *inode) ...@@ -284,6 +286,16 @@ 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[] = { static const struct ncp_option ncp_opts[] = {
{ "uid", OPT_INT, 'u' }, { "uid", OPT_INT, 'u' },
{ "gid", OPT_INT, 'g' }, { "gid", OPT_INT, 'g' },
...@@ -464,6 +476,8 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) ...@@ -464,6 +476,8 @@ 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;
/* server->lock = 0; */ /* server->lock = 0; */
init_MUTEX(&server->sem); init_MUTEX(&server->sem);
server->packet = NULL; server->packet = NULL;
...@@ -501,6 +515,16 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) ...@@ -501,6 +515,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;
...@@ -509,6 +533,22 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) ...@@ -509,6 +533,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);
...@@ -583,6 +623,7 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent) ...@@ -583,6 +623,7 @@ 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
...@@ -610,6 +651,8 @@ static void ncp_put_super(struct super_block *sb) ...@@ -610,6 +651,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)
......
...@@ -93,19 +93,19 @@ static void nwsign(char *r_data1, char *r_data2, char *outdata) { ...@@ -93,19 +93,19 @@ 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 {
nwsign(server->sign_last,data,server->sign_last); memcpy(data + 12, packet, 52);
}
memcpy(server->packet+(*size),server->sign_last,8); nwsign(server->sign_last, data, server->sign_last);
(*size)+=8; memcpy(sign_buff, server->sign_last, 8);
} }
#endif /* CONFIG_NCPFS_PACKET_SIGNING */ #endif /* CONFIG_NCPFS_PACKET_SIGNING */
......
...@@ -10,6 +10,18 @@ ...@@ -10,6 +10,18 @@
#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);
#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,599 @@ static int _send(struct socket *sock, const void *buff, int len) ...@@ -70,353 +64,599 @@ 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));
struct ncp_reply_header reply; server->error_report(sk);
schedule_task(&server->rcv.tq);
}
file = server->ncp_filp; void ncp_tcp_write_space(struct sock *sk) {
sock = SOCKET_I(file->f_dentry->d_inode); struct ncp_server *server = sk->user_data;
init_timeout = server->m.time_out; /* We do not need any locking: we first set tx.creq, and then we do sendmsg,
max_timeout = NCP_MAX_RPC_TIMEOUT; not vice versa... */
retrans = server->m.retry_count; server->write_space(sk);
major_timeout_seen = 0; if (server->tx.creq) {
acknowledge_seen = 0; schedule_task(&server->tx.tq);
}
}
for (n = 0, timeout = init_timeout;; n++, timeout <<= 1) { void ncpdgram_timeout_call(unsigned long v) {
/* struct ncp_server *server = (void*)v;
DDPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X\n",
htonl(server->m.serv_addr.sipx_network), schedule_task(&server->timeout_tq);
server->m.serv_addr.sipx_node[0], }
server->m.serv_addr.sipx_node[1],
server->m.serv_addr.sipx_node[2], static inline void ncp_finish_request(struct ncp_request_reply *req, int result) {
server->m.serv_addr.sipx_node[3], req->result = result;
server->m.serv_addr.sipx_node[4], req->status = RQ_DONE;
server->m.serv_addr.sipx_node[5], wake_up_all(&req->wq);
ntohs(server->m.serv_addr.sipx_port)); }
*/
DDPRINTK("ncpfs: req.typ: %04X, con: %d, " static void __abort_ncp_connection(struct ncp_server *server, struct ncp_request_reply *aborted, int err) {
"seq: %d", struct ncp_request_reply *req;
request.type,
(request.conn_high << 8) + request.conn_low, ncp_invalidate_conn(server);
request.sequence); del_timer(&server->timeout_tm);
DDPRINTK(" func: %d\n", while (!list_empty(&server->tx.requests)) {
request.function); req = list_entry(server->tx.requests.next, struct ncp_request_reply, req);
result = _send(sock, (void *) start, size); list_del_init(&req->req);
if (result < 0) { if (req == aborted) {
printk(KERN_ERR "ncp_rpc_call: send error = %d\n", result); ncp_finish_request(req, err);
return result; } 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) { req = server->rcv.creq;
result = wait_table.error; if (req) {
break; server->rcv.creq = NULL;
if (req == aborted) {
ncp_finish_request(req, err);
} else {
ncp_finish_request(req, -EIO);
} }
if (timed_out) { server->rcv.ptr = NULL;
if (n < retrans) server->rcv.state = 0;
continue;
if (server->m.flags & NCP_MOUNT_SOFT) {
printk(KERN_WARNING "NCP server not responding\n");
result = -EIO;
break;
} }
n = 0; req = server->tx.creq;
timeout = init_timeout; if (req) {
if (init_timeout < max_timeout) server->tx.creq = NULL;
init_timeout <<= 1; if (req == aborted) {
if (!major_timeout_seen) { ncp_finish_request(req, err);
printk(KERN_WARNING "NCP server not responding\n"); } else {
ncp_finish_request(req, -EIO);
} }
major_timeout_seen = 1;
continue;
} }
} else { }
poll_freewait(&wait_table);
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;
case RQ_QUEUED:
list_del_init(&req->req);
ncp_finish_request(req, err);
break;
case RQ_INPROGRESS:
__abort_ncp_connection(server, req, err);
break;
} }
current->state = TASK_RUNNING; }
/* Get the header from the next packet using a peek, so keep it static inline void ncp_abort_request(struct ncp_server *server, struct ncp_request_reply *req, int err) {
* on the recv queue. If it is wrong, it will be some reply down(&server->rcv.creq_sem);
* we don't now need, so discard it */ __ncp_abort_request(server, req, err);
result = _recv(sock, (void *) &reply, sizeof(reply), up(&server->rcv.creq_sem);
MSG_PEEK | MSG_DONTWAIT); }
if (result < 0) {
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) { if (result == -EAGAIN) {
DDPRINTK("ncp_rpc_call: bad select ready\n"); return;
goto re_select;
} }
if (result == -ECONNREFUSED) { if (result < 0) {
DPRINTK("ncp_rpc_call: server playing coy\n"); printk(KERN_ERR "ncpfs: tcp: Send failed: %d\n", result);
goto re_select; __ncp_abort_request(server, rq, result);
return;
} }
if (result != -ERESTARTSYS) { if (result >= rq->tx_totallen) {
printk(KERN_ERR "ncp_rpc_call: recv error = %d\n", server->rcv.creq = rq;
-result); server->tx.creq = NULL;
return;
} }
break; rq->tx_totallen -= result;
iov = rq->tx_ciov;
while (iov->iov_len <= result) {
result -= iov->iov_len;
iov++;
rq->tx_iovlen--;
} }
if ((result == sizeof(reply)) iov->iov_len -= result;
&& (reply.type == NCP_POSITIVE_ACK)) { rq->tx_ciov = iov;
/* Throw away the packet */ }
DPRINTK("ncp_rpc_call: got positive acknowledge\n");
_recv(sock, (void *) &reply, sizeof(reply), static inline void ncp_init_header(struct ncp_server *server, struct ncp_request_reply *req, struct ncp_request_header *h) {
MSG_DONTWAIT); req->status = RQ_INPROGRESS;
n = 0; h->conn_low = server->connection;
timeout = max_timeout; h->conn_high = server->connection >> 8;
acknowledge_seen = 1; h->sequence = ++server->sequence;
goto re_select; }
}
DDPRINTK("ncpfs: rep.typ: %04X, con: %d, tsk: %d," static void ncpdgram_start_request(struct ncp_server *server, struct ncp_request_reply *req) {
"seq: %d\n", size_t signlen;
reply.type, struct ncp_request_header* h;
(reply.conn_high << 8) + reply.conn_low,
reply.task, req->tx_ciov = req->tx_iov + 1;
reply.sequence);
h = req->tx_iov[1].iov_base;
if ((result >= sizeof(reply)) ncp_init_header(server, req, h);
&& (reply.type == NCP_REPLY) signlen = sign_packet(server, req->tx_iov[1].iov_base + sizeof(struct ncp_request_header) - 1,
&& ((request.type == NCP_ALLOC_SLOT_REQUEST) req->tx_iov[1].iov_len - sizeof(struct ncp_request_header) + 1,
|| ((reply.sequence == request.sequence) cpu_to_le32(req->tx_totallen), req->sign);
&& (reply.conn_low == request.conn_low) if (signlen) {
/* seem to get wrong task from NW311 && (reply.task == request.task) */ req->tx_ciov[1].iov_base = req->sign;
&& (reply.conn_high == request.conn_high)))) { req->tx_ciov[1].iov_len = signlen;
if (major_timeout_seen) req->tx_iovlen += 1;
printk(KERN_NOTICE "NCP server OK\n"); req->tx_totallen += signlen;
break; }
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);
}
#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;
} }
/* JEJB/JSP 2/7/94 __ncp_start_request(server, req);
* we have xid mismatch, so discard the packet and start up(&server->rcv.creq_sem);
* again. What a hack! but I can't call recvfrom with return 0;
* a null buffer yet. */ }
_recv(sock, (void *) &reply, sizeof(reply), MSG_DONTWAIT);
DPRINTK("ncp_rpc_call: reply mismatch\n"); static void __ncp_next_request(struct ncp_server *server) {
goto re_select; 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);
* we have the correct reply, so read into the correct place and list_del_init(&req->req);
* return it __ncp_start_request(server, req);
*/ }
result = _recv(sock, (void *)reply_buf, max_reply_size, MSG_DONTWAIT);
static void __ncpdgram_rcv_proc(void *s) {
struct ncp_server *server = s;
struct socket* sock;
sock = server->ncp_sock;
while (1) {
struct ncp_reply_header reply;
int result;
result = _recv(sock, (void*)&reply, sizeof(reply), MSG_PEEK | MSG_DONTWAIT);
if (result < 0) { if (result < 0) {
printk(KERN_WARNING "NCP: notice message: result=%d\n", result); break;
} 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;
} }
if (result >= sizeof(reply)) {
struct ncp_request_reply *req;
return result; if (reply.type == NCP_WATCHDOG) {
} unsigned char buf[10];
static int do_tcp_rcv(struct ncp_server *server, void *buffer, size_t len) { if (server->connection != get_conn_number(&reply)) {
poll_table wait_table; goto drop;
struct file *file;
struct socket *sock;
int init_timeout;
size_t dataread;
int result = 0;
file = server->ncp_filp;
sock = SOCKET_I(file->f_dentry->d_inode);
dataread = 0;
init_timeout = server->m.time_out * 20;
/* 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;
}
if (!init_timeout) {
return -EIO;
} }
if(wait_table.error) { result = _recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
return wait_table.error; 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;
}
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 { } else {
poll_freewait(&wait_table); result -= 8;
} }
current->state = TASK_RUNNING; }
#endif
result = _recv(sock, buffer, len, MSG_DONTWAIT); del_timer(&server->timeout_tm);
if (result < 0) { server->rcv.creq = NULL;
if (result == -EAGAIN) { ncp_finish_request(req, result);
DDPRINTK("ncpfs: tcp: bad select ready\n"); __ncp_next_request(server);
up(&server->rcv.creq_sem);
continue; continue;
} }
return result;
} }
if (result == 0) { up(&server->rcv.creq_sem);
printk(KERN_ERR "ncpfs: tcp: EOF on socket\n");
return -EIO;
} }
if (result > len) { drop:;
printk(KERN_ERR "ncpfs: tcp: bug in recvmsg\n"); _recv(sock, (void*)&reply, sizeof(reply), MSG_DONTWAIT);
return -EIO; }
}
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;
}
}
/* 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) void ncpdgram_timeout_proc(void *s) {
#define NCP_TCP_XMIT_VERSION (1) mm_segment_t fs;
#define NCP_TCP_RCVD_MAGIC (0x744E6350) struct ncp_server *server = s;
static int do_ncp_tcp_rpc_call(struct ncp_server *server, int size, fs = get_fs();
struct ncp_reply_header* reply_buf, int max_reply_size) set_fs(get_ds());
{ down(&server->rcv.creq_sem);
struct file *file; __ncpdgram_timeout_proc(server);
struct socket *sock; up(&server->rcv.creq_sem);
set_fs(fs);
}
static inline void ncp_init_req(struct ncp_request_reply* req) {
init_waitqueue_head(&req->wq);
req->status = RQ_IDLE;
}
static int do_tcp_rcv(struct ncp_server *server, void *buffer, size_t len) {
int result; 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;
/* We have to check the result, so store the complete header */ if (buffer) {
struct ncp_request_header request = result = _recv(server->ncp_sock, buffer, len, MSG_DONTWAIT);
*((struct ncp_request_header *) (server->packet)); } else {
static unsigned char dummy[1024];
file = server->ncp_filp;
sock = SOCKET_I(file->f_dentry->d_inode);
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 (len > sizeof(dummy)) {
if (result < 0) { len = sizeof(dummy);
return result; }
result = _recv(server->ncp_sock, dummy, len, MSG_DONTWAIT);
} }
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; 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]));
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; return -EIO;
} }
datalen = ntohl(ncptcp_rcvd_hdr[1]); if (server->rcv.ptr) {
if (datalen < 8 + sizeof(*reply_buf) || datalen > max_reply_size + 8) { 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); printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d\n", datalen);
__ncptcp_abort(server);
return -EIO; return -EIO;
} }
datalen -= 8; #ifdef CONFIG_NCPFS_PACKET_SIGNING
result = do_tcp_rcv(server, reply_buf, datalen); if (server->sign_active) {
if (result) if (datalen < 18) {
return result; printk(KERN_ERR "ncpfs: tcp: Unexpected reply len %d\n", datalen);
if (reply_buf->type != NCP_REPLY) { __ncptcp_abort(server);
DDPRINTK("ncpfs: tcp: Unexpected NCP type %02X\n", reply_buf->type); return -EIO;
goto rstrcv;
} }
if (request.type == NCP_ALLOC_SLOT_REQUEST) server->rcv.buf.len = datalen - 8;
return datalen; server->rcv.ptr = (unsigned char*)&server->rcv.buf.p1;
if (reply_buf->sequence != request.sequence) { server->rcv.len = 8;
server->rcv.state = 4;
break;
}
#endif
type = ntohs(server->rcv.buf.type);
cont:;
if (type != NCP_REPLY) {
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"); printk(KERN_ERR "ncpfs: tcp: Bad sequence number\n");
__ncp_abort_request(server, req, -EIO);
return -EIO; return -EIO;
} }
if ((reply_buf->conn_low != request.conn_low) || if ((req->reply_buf->conn_low | (req->reply_buf->conn_high << 8)) != server->connection) {
(reply_buf->conn_high != request.conn_high)) {
printk(KERN_ERR "ncpfs: tcp: Connection number mismatch\n"); printk(KERN_ERR "ncpfs: tcp: Connection number mismatch\n");
__ncp_abort_request(server, req, -EIO);
return -EIO; return -EIO;
} }
return datalen; }
ncp_finish_request(req, req->datalen);
nextreq:;
__ncp_next_request(server);
case 2:
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;
}
}
}
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 (wait_event_interruptible(req.wq, req.status == RQ_DONE)) {
ncp_abort_request(server, &req, -EIO);
}
return req.result;
} }
/* /*
...@@ -426,8 +666,6 @@ static int do_ncp_tcp_rpc_call(struct ncp_server *server, int size, ...@@ -426,8 +666,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 +673,10 @@ static int ncp_do_request(struct ncp_server *server, int size, ...@@ -435,21 +673,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,9 +705,6 @@ static int ncp_do_request(struct ncp_server *server, int size, ...@@ -478,9 +705,6 @@ 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_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); set_fs(fs);
...@@ -510,20 +734,13 @@ int ncp_request2(struct ncp_server *server, int function, ...@@ -510,20 +734,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 +748,7 @@ int ncp_request2(struct ncp_server *server, int function, ...@@ -531,7 +748,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 +771,17 @@ int ncp_connect(struct ncp_server *server) ...@@ -554,20 +771,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 +794,6 @@ int ncp_disconnect(struct ncp_server *server) ...@@ -580,11 +794,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,7 @@ struct ncp_server { ...@@ -24,6 +26,7 @@ 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 */
u8 sequence; u8 sequence;
u8 task; u8 task;
...@@ -79,8 +82,50 @@ struct ncp_server { ...@@ -79,8 +82,50 @@ 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 */
}; };
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))
......
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