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 @@
#include <linux/ncp_fs.h>
#include <net/sock.h>
#include "ncplib_kernel.h"
#include "getopt.h"
......@@ -284,6 +286,16 @@ 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' },
......@@ -464,6 +476,8 @@ 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;
/* server->lock = 0; */
init_MUTEX(&server->sem);
server->packet = NULL;
......@@ -501,6 +515,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;
......@@ -509,6 +533,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);
......@@ -583,6 +623,7 @@ static int ncp_fill_super(struct super_block *sb, void *raw_data, int silent)
ncp_disconnect(server);
ncp_unlock_server(server);
out_packet:
ncp_stop_tasks(server);
vfree(server->packet);
out_nls:
#ifdef CONFIG_NCPFS_NLS
......@@ -610,6 +651,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)
......
......@@ -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 */
/* 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));
nwsign(server->sign_last,data,server->sign_last);
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, server->sign_last);
memcpy(sign_buff, server->sign_last, 8);
}
#endif /* CONFIG_NCPFS_PACKET_SIGNING */
......
......@@ -10,6 +10,18 @@
#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
......@@ -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,599 @@ 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));
void ncp_tcp_error_report(struct sock *sk) {
struct ncp_server *server = sk->user_data;
struct ncp_reply_header reply;
server->error_report(sk);
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;
retrans = server->m.retry_count;
major_timeout_seen = 0;
acknowledge_seen = 0;
/* 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);
}
}
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 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;
req = server->rcv.creq;
if (req) {
server->rcv.creq = NULL;
if (req == aborted) {
ncp_finish_request(req, err);
} else {
ncp_finish_request(req, -EIO);
}
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;
server->rcv.ptr = NULL;
server->rcv.state = 0;
}
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");
req = server->tx.creq;
if (req) {
server->tx.creq = NULL;
if (req == aborted) {
ncp_finish_request(req, err);
} 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
* 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) {
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) {
DDPRINTK("ncp_rpc_call: bad select ready\n");
goto re_select;
return;
}
if (result == -ECONNREFUSED) {
DPRINTK("ncp_rpc_call: server playing coy\n");
goto re_select;
if (result < 0) {
printk(KERN_ERR "ncpfs: tcp: Send failed: %d\n", result);
__ncp_abort_request(server, rq, result);
return;
}
if (result != -ERESTARTSYS) {
printk(KERN_ERR "ncp_rpc_call: recv error = %d\n",
-result);
if (result >= rq->tx_totallen) {
server->rcv.creq = rq;
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))
&& (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");
break;
iov->iov_len -= result;
rq->tx_ciov = iov;
}
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);
}
#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
* 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);
__ncp_start_request(server, req);
up(&server->rcv.creq_sem);
return 0;
}
DPRINTK("ncp_rpc_call: reply mismatch\n");
goto re_select;
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;
}
/*
* 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);
req = list_entry(server->tx.requests.next, struct ncp_request_reply, req);
list_del_init(&req->req);
__ncp_start_request(server, req);
}
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) {
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;
break;
}
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) {
poll_table wait_table;
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 (server->connection != get_conn_number(&reply)) {
goto drop;
}
if(wait_table.error) {
return wait_table.error;
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;
}
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 {
poll_freewait(&wait_table);
result -= 8;
}
current->state = TASK_RUNNING;
result = _recv(sock, buffer, len, MSG_DONTWAIT);
if (result < 0) {
if (result == -EAGAIN) {
DDPRINTK("ncpfs: tcp: bad select ready\n");
}
#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;
}
return result;
}
if (result == 0) {
printk(KERN_ERR "ncpfs: tcp: EOF on socket\n");
return -EIO;
up(&server->rcv.creq_sem);
}
if (result > len) {
printk(KERN_ERR "ncpfs: tcp: bug in recvmsg\n");
return -EIO;
drop:;
_recv(sock, (void*)&reply, sizeof(reply), MSG_DONTWAIT);
}
}
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)
#define NCP_TCP_XMIT_VERSION (1)
#define NCP_TCP_RCVD_MAGIC (0x744E6350)
void ncpdgram_timeout_proc(void *s) {
mm_segment_t fs;
struct ncp_server *server = s;
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;
fs = get_fs();
set_fs(get_ds());
down(&server->rcv.creq_sem);
__ncpdgram_timeout_proc(server);
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;
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 */
struct ncp_request_header request =
*((struct ncp_request_header *) (server->packet));
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;
if (buffer) {
result = _recv(server->ncp_sock, buffer, len, MSG_DONTWAIT);
} else {
static unsigned char dummy[1024];
result = scm_send(sock, &msg, &scm);
if (result < 0) {
return result;
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)
if (result > len) {
printk(KERN_ERR "ncpfs: tcp: bug in recvmsg (%u > %u)\n", result, len);
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]));
}
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;
}
datalen = ntohl(ncptcp_rcvd_hdr[1]);
if (datalen < 8 + sizeof(*reply_buf) || datalen > max_reply_size + 8) {
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;
}
datalen -= 8;
result = do_tcp_rcv(server, reply_buf, datalen);
if (result)
return result;
if (reply_buf->type != NCP_REPLY) {
DDPRINTK("ncpfs: tcp: Unexpected NCP type %02X\n", reply_buf->type);
goto rstrcv;
#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;
}
if (request.type == NCP_ALLOC_SLOT_REQUEST)
return datalen;
if (reply_buf->sequence != request.sequence) {
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) {
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 ((reply_buf->conn_low != request.conn_low) ||
(reply_buf->conn_high != request.conn_high)) {
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;
}
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,
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 +673,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,9 +705,6 @@ 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);
set_fs(fs);
......@@ -510,20 +734,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 +748,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 +771,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 +794,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;
......
......@@ -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 {
......
......@@ -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,7 @@ 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 */
u8 sequence;
u8 task;
......@@ -79,8 +82,50 @@ 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 */
};
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))
......
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