diff options
author | Pierre Ossman <ossman@cendio.se> | 2007-02-19 05:34:43 -0500 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2007-03-06 07:26:27 -0500 |
commit | c5f93cf19df633a8dbd7adf8130d604eec96e145 (patch) | |
tree | e58a062b40b9ed1c9be9bcaac4302da98ae6082f /fs/ncpfs/sock.c | |
parent | c3442e296517aee733d62fc3fe03211598902c7d (diff) |
ncpfs: make sure server connection survives a kill
Use internal buffers instead of the ones supplied by the caller
so that a caller can be interrupted without having to abort the
entire ncp connection.
Signed-off-by: Pierre Ossman <ossman@cendio.se>
Acked-by: Petr Vandrovec <petr@vandrovec.name>
Diffstat (limited to 'fs/ncpfs/sock.c')
-rw-r--r-- | fs/ncpfs/sock.c | 151 |
1 files changed, 89 insertions, 62 deletions
diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c index e496d8b65e92..e37df8d5fe70 100644 --- a/fs/ncpfs/sock.c +++ b/fs/ncpfs/sock.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/socket.h> | 14 | #include <linux/socket.h> |
15 | #include <linux/fcntl.h> | 15 | #include <linux/fcntl.h> |
16 | #include <linux/stat.h> | 16 | #include <linux/stat.h> |
17 | #include <linux/string.h> | ||
17 | #include <asm/uaccess.h> | 18 | #include <asm/uaccess.h> |
18 | #include <linux/in.h> | 19 | #include <linux/in.h> |
19 | #include <linux/net.h> | 20 | #include <linux/net.h> |
@@ -55,10 +56,11 @@ static int _send(struct socket *sock, const void *buff, int len) | |||
55 | struct ncp_request_reply { | 56 | struct ncp_request_reply { |
56 | struct list_head req; | 57 | struct list_head req; |
57 | wait_queue_head_t wq; | 58 | wait_queue_head_t wq; |
58 | struct ncp_reply_header* reply_buf; | 59 | atomic_t refs; |
60 | unsigned char* reply_buf; | ||
59 | size_t datalen; | 61 | size_t datalen; |
60 | int result; | 62 | int result; |
61 | enum { RQ_DONE, RQ_INPROGRESS, RQ_QUEUED, RQ_IDLE } status; | 63 | enum { RQ_DONE, RQ_INPROGRESS, RQ_QUEUED, RQ_IDLE, RQ_ABANDONED } status; |
62 | struct kvec* tx_ciov; | 64 | struct kvec* tx_ciov; |
63 | size_t tx_totallen; | 65 | size_t tx_totallen; |
64 | size_t tx_iovlen; | 66 | size_t tx_iovlen; |
@@ -67,6 +69,32 @@ struct ncp_request_reply { | |||
67 | u_int32_t sign[6]; | 69 | u_int32_t sign[6]; |
68 | }; | 70 | }; |
69 | 71 | ||
72 | static inline struct ncp_request_reply* ncp_alloc_req(void) | ||
73 | { | ||
74 | struct ncp_request_reply *req; | ||
75 | |||
76 | req = kmalloc(sizeof(struct ncp_request_reply), GFP_KERNEL); | ||
77 | if (!req) | ||
78 | return NULL; | ||
79 | |||
80 | init_waitqueue_head(&req->wq); | ||
81 | atomic_set(&req->refs, (1)); | ||
82 | req->status = RQ_IDLE; | ||
83 | |||
84 | return req; | ||
85 | } | ||
86 | |||
87 | static void ncp_req_get(struct ncp_request_reply *req) | ||
88 | { | ||
89 | atomic_inc(&req->refs); | ||
90 | } | ||
91 | |||
92 | static void ncp_req_put(struct ncp_request_reply *req) | ||
93 | { | ||
94 | if (atomic_dec_and_test(&req->refs)) | ||
95 | kfree(req); | ||
96 | } | ||
97 | |||
70 | void ncp_tcp_data_ready(struct sock *sk, int len) | 98 | void ncp_tcp_data_ready(struct sock *sk, int len) |
71 | { | 99 | { |
72 | struct ncp_server *server = sk->sk_user_data; | 100 | struct ncp_server *server = sk->sk_user_data; |
@@ -101,14 +129,17 @@ void ncpdgram_timeout_call(unsigned long v) | |||
101 | schedule_work(&server->timeout_tq); | 129 | schedule_work(&server->timeout_tq); |
102 | } | 130 | } |
103 | 131 | ||
104 | static inline void ncp_finish_request(struct ncp_request_reply *req, int result) | 132 | static inline void ncp_finish_request(struct ncp_server *server, struct ncp_request_reply *req, int result) |
105 | { | 133 | { |
106 | req->result = result; | 134 | req->result = result; |
135 | if (req->status != RQ_ABANDONED) | ||
136 | memcpy(req->reply_buf, server->rxbuf, req->datalen); | ||
107 | req->status = RQ_DONE; | 137 | req->status = RQ_DONE; |
108 | wake_up_all(&req->wq); | 138 | wake_up_all(&req->wq); |
139 | ncp_req_put(req); | ||
109 | } | 140 | } |
110 | 141 | ||
111 | static void __abort_ncp_connection(struct ncp_server *server, struct ncp_request_reply *aborted, int err) | 142 | static void __abort_ncp_connection(struct ncp_server *server) |
112 | { | 143 | { |
113 | struct ncp_request_reply *req; | 144 | struct ncp_request_reply *req; |
114 | 145 | ||
@@ -118,31 +149,19 @@ static void __abort_ncp_connection(struct ncp_server *server, struct ncp_request | |||
118 | req = list_entry(server->tx.requests.next, struct ncp_request_reply, req); | 149 | req = list_entry(server->tx.requests.next, struct ncp_request_reply, req); |
119 | 150 | ||
120 | list_del_init(&req->req); | 151 | list_del_init(&req->req); |
121 | if (req == aborted) { | 152 | ncp_finish_request(server, req, -EIO); |
122 | ncp_finish_request(req, err); | ||
123 | } else { | ||
124 | ncp_finish_request(req, -EIO); | ||
125 | } | ||
126 | } | 153 | } |
127 | req = server->rcv.creq; | 154 | req = server->rcv.creq; |
128 | if (req) { | 155 | if (req) { |
129 | server->rcv.creq = NULL; | 156 | server->rcv.creq = NULL; |
130 | if (req == aborted) { | 157 | ncp_finish_request(server, req, -EIO); |
131 | ncp_finish_request(req, err); | ||
132 | } else { | ||
133 | ncp_finish_request(req, -EIO); | ||
134 | } | ||
135 | server->rcv.ptr = NULL; | 158 | server->rcv.ptr = NULL; |
136 | server->rcv.state = 0; | 159 | server->rcv.state = 0; |
137 | } | 160 | } |
138 | req = server->tx.creq; | 161 | req = server->tx.creq; |
139 | if (req) { | 162 | if (req) { |
140 | server->tx.creq = NULL; | 163 | server->tx.creq = NULL; |
141 | if (req == aborted) { | 164 | ncp_finish_request(server, req, -EIO); |
142 | ncp_finish_request(req, err); | ||
143 | } else { | ||
144 | ncp_finish_request(req, -EIO); | ||
145 | } | ||
146 | } | 165 | } |
147 | } | 166 | } |
148 | 167 | ||
@@ -160,10 +179,12 @@ static inline void __ncp_abort_request(struct ncp_server *server, struct ncp_req | |||
160 | break; | 179 | break; |
161 | case RQ_QUEUED: | 180 | case RQ_QUEUED: |
162 | list_del_init(&req->req); | 181 | list_del_init(&req->req); |
163 | ncp_finish_request(req, err); | 182 | ncp_finish_request(server, req, err); |
164 | break; | 183 | break; |
165 | case RQ_INPROGRESS: | 184 | case RQ_INPROGRESS: |
166 | __abort_ncp_connection(server, req, err); | 185 | req->status = RQ_ABANDONED; |
186 | break; | ||
187 | case RQ_ABANDONED: | ||
167 | break; | 188 | break; |
168 | } | 189 | } |
169 | } | 190 | } |
@@ -177,7 +198,7 @@ static inline void ncp_abort_request(struct ncp_server *server, struct ncp_reque | |||
177 | 198 | ||
178 | static inline void __ncptcp_abort(struct ncp_server *server) | 199 | static inline void __ncptcp_abort(struct ncp_server *server) |
179 | { | 200 | { |
180 | __abort_ncp_connection(server, NULL, 0); | 201 | __abort_ncp_connection(server); |
181 | } | 202 | } |
182 | 203 | ||
183 | static int ncpdgram_send(struct socket *sock, struct ncp_request_reply *req) | 204 | static int ncpdgram_send(struct socket *sock, struct ncp_request_reply *req) |
@@ -294,6 +315,11 @@ static void ncptcp_start_request(struct ncp_server *server, struct ncp_request_r | |||
294 | 315 | ||
295 | static inline void __ncp_start_request(struct ncp_server *server, struct ncp_request_reply *req) | 316 | static inline void __ncp_start_request(struct ncp_server *server, struct ncp_request_reply *req) |
296 | { | 317 | { |
318 | /* we copy the data so that we do not depend on the caller | ||
319 | staying alive */ | ||
320 | memcpy(server->txbuf, req->tx_iov[1].iov_base, req->tx_iov[1].iov_len); | ||
321 | req->tx_iov[1].iov_base = server->txbuf; | ||
322 | |||
297 | if (server->ncp_sock->type == SOCK_STREAM) | 323 | if (server->ncp_sock->type == SOCK_STREAM) |
298 | ncptcp_start_request(server, req); | 324 | ncptcp_start_request(server, req); |
299 | else | 325 | else |
@@ -308,6 +334,7 @@ static int ncp_add_request(struct ncp_server *server, struct ncp_request_reply * | |||
308 | printk(KERN_ERR "ncpfs: tcp: Server died\n"); | 334 | printk(KERN_ERR "ncpfs: tcp: Server died\n"); |
309 | return -EIO; | 335 | return -EIO; |
310 | } | 336 | } |
337 | ncp_req_get(req); | ||
311 | if (server->tx.creq || server->rcv.creq) { | 338 | if (server->tx.creq || server->rcv.creq) { |
312 | req->status = RQ_QUEUED; | 339 | req->status = RQ_QUEUED; |
313 | list_add_tail(&req->req, &server->tx.requests); | 340 | list_add_tail(&req->req, &server->tx.requests); |
@@ -409,7 +436,7 @@ void ncpdgram_rcv_proc(struct work_struct *work) | |||
409 | server->timeout_last = NCP_MAX_RPC_TIMEOUT; | 436 | server->timeout_last = NCP_MAX_RPC_TIMEOUT; |
410 | mod_timer(&server->timeout_tm, jiffies + NCP_MAX_RPC_TIMEOUT); | 437 | mod_timer(&server->timeout_tm, jiffies + NCP_MAX_RPC_TIMEOUT); |
411 | } else if (reply.type == NCP_REPLY) { | 438 | } else if (reply.type == NCP_REPLY) { |
412 | result = _recv(sock, (void*)req->reply_buf, req->datalen, MSG_DONTWAIT); | 439 | result = _recv(sock, server->rxbuf, req->datalen, MSG_DONTWAIT); |
413 | #ifdef CONFIG_NCPFS_PACKET_SIGNING | 440 | #ifdef CONFIG_NCPFS_PACKET_SIGNING |
414 | if (result >= 0 && server->sign_active && req->tx_type != NCP_DEALLOC_SLOT_REQUEST) { | 441 | if (result >= 0 && server->sign_active && req->tx_type != NCP_DEALLOC_SLOT_REQUEST) { |
415 | if (result < 8 + 8) { | 442 | if (result < 8 + 8) { |
@@ -419,7 +446,7 @@ void ncpdgram_rcv_proc(struct work_struct *work) | |||
419 | 446 | ||
420 | result -= 8; | 447 | result -= 8; |
421 | hdrl = sock->sk->sk_family == AF_INET ? 8 : 6; | 448 | hdrl = sock->sk->sk_family == AF_INET ? 8 : 6; |
422 | if (sign_verify_reply(server, ((char*)req->reply_buf) + hdrl, result - hdrl, cpu_to_le32(result), ((char*)req->reply_buf) + result)) { | 449 | if (sign_verify_reply(server, server->rxbuf + hdrl, result - hdrl, cpu_to_le32(result), server->rxbuf + result)) { |
423 | printk(KERN_INFO "ncpfs: Signature violation\n"); | 450 | printk(KERN_INFO "ncpfs: Signature violation\n"); |
424 | result = -EIO; | 451 | result = -EIO; |
425 | } | 452 | } |
@@ -428,7 +455,7 @@ void ncpdgram_rcv_proc(struct work_struct *work) | |||
428 | #endif | 455 | #endif |
429 | del_timer(&server->timeout_tm); | 456 | del_timer(&server->timeout_tm); |
430 | server->rcv.creq = NULL; | 457 | server->rcv.creq = NULL; |
431 | ncp_finish_request(req, result); | 458 | ncp_finish_request(server, req, result); |
432 | __ncp_next_request(server); | 459 | __ncp_next_request(server); |
433 | mutex_unlock(&server->rcv.creq_mutex); | 460 | mutex_unlock(&server->rcv.creq_mutex); |
434 | continue; | 461 | continue; |
@@ -478,12 +505,6 @@ void ncpdgram_timeout_proc(struct work_struct *work) | |||
478 | mutex_unlock(&server->rcv.creq_mutex); | 505 | mutex_unlock(&server->rcv.creq_mutex); |
479 | } | 506 | } |
480 | 507 | ||
481 | static inline void ncp_init_req(struct ncp_request_reply* req) | ||
482 | { | ||
483 | init_waitqueue_head(&req->wq); | ||
484 | req->status = RQ_IDLE; | ||
485 | } | ||
486 | |||
487 | static int do_tcp_rcv(struct ncp_server *server, void *buffer, size_t len) | 508 | static int do_tcp_rcv(struct ncp_server *server, void *buffer, size_t len) |
488 | { | 509 | { |
489 | int result; | 510 | int result; |
@@ -601,8 +622,8 @@ skipdata:; | |||
601 | goto skipdata; | 622 | goto skipdata; |
602 | } | 623 | } |
603 | req->datalen = datalen - 8; | 624 | req->datalen = datalen - 8; |
604 | req->reply_buf->type = NCP_REPLY; | 625 | ((struct ncp_reply_header*)server->rxbuf)->type = NCP_REPLY; |
605 | server->rcv.ptr = (unsigned char*)(req->reply_buf) + 2; | 626 | server->rcv.ptr = server->rxbuf + 2; |
606 | server->rcv.len = datalen - 10; | 627 | server->rcv.len = datalen - 10; |
607 | server->rcv.state = 1; | 628 | server->rcv.state = 1; |
608 | break; | 629 | break; |
@@ -615,12 +636,12 @@ skipdata:; | |||
615 | case 1: | 636 | case 1: |
616 | req = server->rcv.creq; | 637 | req = server->rcv.creq; |
617 | if (req->tx_type != NCP_ALLOC_SLOT_REQUEST) { | 638 | if (req->tx_type != NCP_ALLOC_SLOT_REQUEST) { |
618 | if (req->reply_buf->sequence != server->sequence) { | 639 | if (((struct ncp_reply_header*)server->rxbuf)->sequence != server->sequence) { |
619 | printk(KERN_ERR "ncpfs: tcp: Bad sequence number\n"); | 640 | printk(KERN_ERR "ncpfs: tcp: Bad sequence number\n"); |
620 | __ncp_abort_request(server, req, -EIO); | 641 | __ncp_abort_request(server, req, -EIO); |
621 | return -EIO; | 642 | return -EIO; |
622 | } | 643 | } |
623 | if ((req->reply_buf->conn_low | (req->reply_buf->conn_high << 8)) != server->connection) { | 644 | if ((((struct ncp_reply_header*)server->rxbuf)->conn_low | (((struct ncp_reply_header*)server->rxbuf)->conn_high << 8)) != server->connection) { |
624 | printk(KERN_ERR "ncpfs: tcp: Connection number mismatch\n"); | 645 | printk(KERN_ERR "ncpfs: tcp: Connection number mismatch\n"); |
625 | __ncp_abort_request(server, req, -EIO); | 646 | __ncp_abort_request(server, req, -EIO); |
626 | return -EIO; | 647 | return -EIO; |
@@ -628,14 +649,14 @@ skipdata:; | |||
628 | } | 649 | } |
629 | #ifdef CONFIG_NCPFS_PACKET_SIGNING | 650 | #ifdef CONFIG_NCPFS_PACKET_SIGNING |
630 | if (server->sign_active && req->tx_type != NCP_DEALLOC_SLOT_REQUEST) { | 651 | if (server->sign_active && req->tx_type != NCP_DEALLOC_SLOT_REQUEST) { |
631 | if (sign_verify_reply(server, (unsigned char*)(req->reply_buf) + 6, req->datalen - 6, cpu_to_be32(req->datalen + 16), &server->rcv.buf.type)) { | 652 | if (sign_verify_reply(server, server->rxbuf + 6, req->datalen - 6, cpu_to_be32(req->datalen + 16), &server->rcv.buf.type)) { |
632 | printk(KERN_ERR "ncpfs: tcp: Signature violation\n"); | 653 | printk(KERN_ERR "ncpfs: tcp: Signature violation\n"); |
633 | __ncp_abort_request(server, req, -EIO); | 654 | __ncp_abort_request(server, req, -EIO); |
634 | return -EIO; | 655 | return -EIO; |
635 | } | 656 | } |
636 | } | 657 | } |
637 | #endif | 658 | #endif |
638 | ncp_finish_request(req, req->datalen); | 659 | ncp_finish_request(server, req, req->datalen); |
639 | nextreq:; | 660 | nextreq:; |
640 | __ncp_next_request(server); | 661 | __ncp_next_request(server); |
641 | case 2: | 662 | case 2: |
@@ -645,7 +666,7 @@ skipdata:; | |||
645 | server->rcv.state = 0; | 666 | server->rcv.state = 0; |
646 | break; | 667 | break; |
647 | case 3: | 668 | case 3: |
648 | ncp_finish_request(server->rcv.creq, -EIO); | 669 | ncp_finish_request(server, server->rcv.creq, -EIO); |
649 | goto nextreq; | 670 | goto nextreq; |
650 | case 5: | 671 | case 5: |
651 | info_server(server, 0, server->unexpected_packet.data, server->unexpected_packet.len); | 672 | info_server(server, 0, server->unexpected_packet.data, server->unexpected_packet.len); |
@@ -675,28 +696,39 @@ void ncp_tcp_tx_proc(struct work_struct *work) | |||
675 | } | 696 | } |
676 | 697 | ||
677 | static int do_ncp_rpc_call(struct ncp_server *server, int size, | 698 | static int do_ncp_rpc_call(struct ncp_server *server, int size, |
678 | struct ncp_reply_header* reply_buf, int max_reply_size) | 699 | unsigned char* reply_buf, int max_reply_size) |
679 | { | 700 | { |
680 | int result; | 701 | int result; |
681 | struct ncp_request_reply req; | 702 | struct ncp_request_reply *req; |
682 | 703 | ||
683 | ncp_init_req(&req); | 704 | req = ncp_alloc_req(); |
684 | req.reply_buf = reply_buf; | 705 | if (!req) |
685 | req.datalen = max_reply_size; | 706 | return -ENOMEM; |
686 | req.tx_iov[1].iov_base = server->packet; | 707 | |
687 | req.tx_iov[1].iov_len = size; | 708 | req->reply_buf = reply_buf; |
688 | req.tx_iovlen = 1; | 709 | req->datalen = max_reply_size; |
689 | req.tx_totallen = size; | 710 | req->tx_iov[1].iov_base = server->packet; |
690 | req.tx_type = *(u_int16_t*)server->packet; | 711 | req->tx_iov[1].iov_len = size; |
691 | 712 | req->tx_iovlen = 1; | |
692 | result = ncp_add_request(server, &req); | 713 | req->tx_totallen = size; |
693 | if (result < 0) { | 714 | req->tx_type = *(u_int16_t*)server->packet; |
694 | return result; | 715 | |
695 | } | 716 | result = ncp_add_request(server, req); |
696 | if (wait_event_interruptible(req.wq, req.status == RQ_DONE)) { | 717 | if (result < 0) |
697 | ncp_abort_request(server, &req, -EIO); | 718 | goto out; |
719 | |||
720 | if (wait_event_interruptible(req->wq, req->status == RQ_DONE)) { | ||
721 | ncp_abort_request(server, req, -EINTR); | ||
722 | result = -EINTR; | ||
723 | goto out; | ||
698 | } | 724 | } |
699 | return req.result; | 725 | |
726 | result = req->result; | ||
727 | |||
728 | out: | ||
729 | ncp_req_put(req); | ||
730 | |||
731 | return result; | ||
700 | } | 732 | } |
701 | 733 | ||
702 | /* | 734 | /* |
@@ -751,11 +783,6 @@ static int ncp_do_request(struct ncp_server *server, int size, | |||
751 | 783 | ||
752 | DDPRINTK("do_ncp_rpc_call returned %d\n", result); | 784 | DDPRINTK("do_ncp_rpc_call returned %d\n", result); |
753 | 785 | ||
754 | if (result < 0) { | ||
755 | /* There was a problem with I/O, so the connections is | ||
756 | * no longer usable. */ | ||
757 | ncp_invalidate_conn(server); | ||
758 | } | ||
759 | return result; | 786 | return result; |
760 | } | 787 | } |
761 | 788 | ||