[PATCH] v9fs: fix handling of malformed 9P messages
[linux-2.6.git] / fs / 9p / mux.c
1 /*
2  * linux/fs/9p/mux.c
3  *
4  * Protocol Multiplexer
5  *
6  *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
7  *  Copyright (C) 2004 by Latchesar Ionkov <lucho@ionkov.net>
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to:
21  *  Free Software Foundation
22  *  51 Franklin Street, Fifth Floor
23  *  Boston, MA  02111-1301  USA
24  *
25  */
26
27 #include <linux/config.h>
28 #include <linux/module.h>
29 #include <linux/errno.h>
30 #include <linux/fs.h>
31 #include <linux/kthread.h>
32 #include <linux/idr.h>
33
34 #include "debug.h"
35 #include "v9fs.h"
36 #include "9p.h"
37 #include "transport.h"
38 #include "conv.h"
39 #include "mux.h"
40
41 /**
42  * dprintcond - print condition of session info
43  * @v9ses: session info structure
44  * @req: RPC request structure
45  *
46  */
47
48 static inline int
49 dprintcond(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req)
50 {
51         dprintk(DEBUG_MUX, "condition: %d, %p\n", v9ses->transport->status,
52                 req->rcall);
53         return 0;
54 }
55
56 /**
57  * xread - force read of a certain number of bytes
58  * @v9ses: session info structure
59  * @ptr: pointer to buffer
60  * @sz: number of bytes to read
61  *
62  * Chuck Cranor CS-533 project1
63  */
64
65 static int xread(struct v9fs_session_info *v9ses, void *ptr, unsigned long sz)
66 {
67         int rd = 0;
68         int ret = 0;
69         while (rd < sz) {
70                 ret = v9ses->transport->read(v9ses->transport, ptr, sz - rd);
71                 if (ret <= 0) {
72                         dprintk(DEBUG_ERROR, "xread errno %d\n", ret);
73                         return ret;
74                 }
75                 rd += ret;
76                 ptr += ret;
77         }
78         return (rd);
79 }
80
81 /**
82  * read_message - read a full 9P2000 fcall packet
83  * @v9ses: session info structure
84  * @rcall: fcall structure to read into
85  * @rcalllen: size of fcall buffer
86  *
87  */
88
89 static int
90 read_message(struct v9fs_session_info *v9ses,
91              struct v9fs_fcall *rcall, int rcalllen)
92 {
93         unsigned char buf[4];
94         void *data;
95         int size = 0;
96         int res = 0;
97
98         res = xread(v9ses, buf, sizeof(buf));
99         if (res < 0) {
100                 dprintk(DEBUG_ERROR,
101                         "Reading of count field failed returned: %d\n", res);
102                 return res;
103         }
104
105         if (res < 4) {
106                 dprintk(DEBUG_ERROR,
107                         "Reading of count field failed returned: %d\n", res);
108                 return -EIO;
109         }
110
111         size = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
112         dprintk(DEBUG_MUX, "got a packet count: %d\n", size);
113
114         /* adjust for the four bytes of size */
115         size -= 4;
116
117         if (size > v9ses->maxdata) {
118                 dprintk(DEBUG_ERROR, "packet too big: %d\n", size);
119                 return -E2BIG;
120         }
121
122         data = kmalloc(size, GFP_KERNEL);
123         if (!data) {
124                 eprintk(KERN_WARNING, "out of memory\n");
125                 return -ENOMEM;
126         }
127
128         res = xread(v9ses, data, size);
129         if (res < size) {
130                 dprintk(DEBUG_ERROR, "Reading of fcall failed returned: %d\n",
131                         res);
132                 kfree(data);
133                 return res;
134         }
135
136         /* we now have an in-memory string that is the reply.
137          * deserialize it. There is very little to go wrong at this point
138          * save for v9fs_alloc errors.
139          */
140         res = v9fs_deserialize_fcall(v9ses, size, data, v9ses->maxdata,
141                                      rcall, rcalllen);
142
143         kfree(data);
144
145         if (res < 0)
146                 return res;
147
148         return 0;
149 }
150
151 /**
152  * v9fs_recv - receive an RPC response for a particular tag
153  * @v9ses: session info structure
154  * @req: RPC request structure
155  *
156  */
157
158 static int v9fs_recv(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req)
159 {
160         int ret = 0;
161
162         dprintk(DEBUG_MUX, "waiting for response: %d\n", req->tcall->tag);
163         ret = wait_event_interruptible(v9ses->read_wait,
164                        ((v9ses->transport->status != Connected) ||
165                         (req->rcall != 0) || (req->err < 0) ||
166                         dprintcond(v9ses, req)));
167
168         dprintk(DEBUG_MUX, "got it: rcall %p\n", req->rcall);
169
170         spin_lock(&v9ses->muxlock);
171         list_del(&req->next);
172         spin_unlock(&v9ses->muxlock);
173
174         if (req->err < 0)
175                 return req->err;
176
177         if (v9ses->transport->status == Disconnected)
178                 return -ECONNRESET;
179
180         return ret;
181 }
182
183 /**
184  * v9fs_send - send a 9P request
185  * @v9ses: session info structure
186  * @req: RPC request to send
187  *
188  */
189
190 static int v9fs_send(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req)
191 {
192         int ret = -1;
193         void *data = NULL;
194         struct v9fs_fcall *tcall = req->tcall;
195
196         data = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL);
197         if (!data)
198                 return -ENOMEM;
199
200         tcall->size = 0;        /* enforce size recalculation */
201         ret =
202             v9fs_serialize_fcall(v9ses, tcall, data,
203                                  v9ses->maxdata + V9FS_IOHDRSZ);
204         if (ret < 0)
205                 goto free_data;
206
207         spin_lock(&v9ses->muxlock);
208         list_add(&req->next, &v9ses->mux_fcalls);
209         spin_unlock(&v9ses->muxlock);
210
211         dprintk(DEBUG_MUX, "sending message: tag %d size %d\n", tcall->tag,
212                 tcall->size);
213         ret = v9ses->transport->write(v9ses->transport, data, tcall->size);
214
215         if (ret != tcall->size) {
216                 spin_lock(&v9ses->muxlock);
217                 list_del(&req->next);
218                 kfree(req->rcall);
219
220                 spin_unlock(&v9ses->muxlock);
221                 if (ret >= 0)
222                         ret = -EREMOTEIO;
223         } else
224                 ret = 0;
225
226       free_data:
227         kfree(data);
228         return ret;
229 }
230
231 /**
232  * v9fs_mux_rpc - send a request, receive a response
233  * @v9ses: session info structure
234  * @tcall: fcall to send
235  * @rcall: buffer to place response into
236  *
237  */
238
239 long
240 v9fs_mux_rpc(struct v9fs_session_info *v9ses, struct v9fs_fcall *tcall,
241              struct v9fs_fcall **rcall)
242 {
243         int tid = -1;
244         struct v9fs_fcall *fcall = NULL;
245         struct v9fs_rpcreq req;
246         int ret = -1;
247
248         if (!v9ses)
249                 return -EINVAL;
250
251         if (!v9ses->transport || v9ses->transport->status != Connected)
252                 return -EIO;
253
254         if (rcall)
255                 *rcall = NULL;
256
257         if (tcall->id != TVERSION) {
258                 tid = v9fs_get_idpool(&v9ses->tidpool);
259                 if (tid < 0)
260                         return -ENOMEM;
261         }
262
263         tcall->tag = tid;
264
265         req.tcall = tcall;
266         req.err = 0;
267         req.rcall = NULL;
268
269         ret = v9fs_send(v9ses, &req);
270
271         if (ret < 0) {
272                 if (tcall->id != TVERSION)
273                         v9fs_put_idpool(tid, &v9ses->tidpool);
274                 dprintk(DEBUG_MUX, "error %d\n", ret);
275                 return ret;
276         }
277
278         ret = v9fs_recv(v9ses, &req);
279
280         fcall = req.rcall;
281
282         dprintk(DEBUG_MUX, "received: tag=%x, ret=%d\n", tcall->tag, ret);
283         if (ret == -ERESTARTSYS) {
284                 if (v9ses->transport->status != Disconnected
285                     && tcall->id != TFLUSH) {
286                         unsigned long flags;
287
288                         dprintk(DEBUG_MUX, "flushing the tag: %d\n",
289                                 tcall->tag);
290                         clear_thread_flag(TIF_SIGPENDING);
291                         v9fs_t_flush(v9ses, tcall->tag);
292                         spin_lock_irqsave(&current->sighand->siglock, flags);
293                         recalc_sigpending();
294                         spin_unlock_irqrestore(&current->sighand->siglock,
295                                                flags);
296                         dprintk(DEBUG_MUX, "flushing done\n");
297                 }
298
299                 goto release_req;
300         } else if (ret < 0)
301                 goto release_req;
302
303         if (!fcall)
304                 ret = -EIO;
305         else {
306                 if (fcall->id == RERROR) {
307                         ret = v9fs_errstr2errno(fcall->params.rerror.error);
308                         if (ret == 0) { /* string match failed */
309                                 if (fcall->params.rerror.errno)
310                                         ret = -(fcall->params.rerror.errno);
311                                 else
312                                         ret = -ESERVERFAULT;
313                         }
314                 } else if (fcall->id != tcall->id + 1) {
315                         dprintk(DEBUG_ERROR,
316                                 "fcall mismatch: expected %d, got %d\n",
317                                 tcall->id + 1, fcall->id);
318                         ret = -EIO;
319                 }
320         }
321
322       release_req:
323         if (tcall->id != TVERSION)
324                 v9fs_put_idpool(tid, &v9ses->tidpool);
325         if (rcall)
326                 *rcall = fcall;
327         else
328                 kfree(fcall);
329
330         return ret;
331 }
332
333 /**
334  * v9fs_mux_cancel_requests - cancels all pending requests
335  *
336  * @v9ses: session info structure
337  * @err: error code to return to the requests
338  */
339 void v9fs_mux_cancel_requests(struct v9fs_session_info *v9ses, int err)
340 {
341         struct v9fs_rpcreq *rptr;
342         struct v9fs_rpcreq *rreq;
343
344         dprintk(DEBUG_MUX, " %d\n", err);
345         spin_lock(&v9ses->muxlock);
346         list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) {
347                 rreq->err = err;
348         }
349         spin_unlock(&v9ses->muxlock);
350         wake_up_all(&v9ses->read_wait);
351 }
352
353 /**
354  * v9fs_recvproc - kproc to handle demultiplexing responses
355  * @data: session info structure
356  *
357  */
358
359 static int v9fs_recvproc(void *data)
360 {
361         struct v9fs_session_info *v9ses = (struct v9fs_session_info *)data;
362         struct v9fs_fcall *rcall = NULL;
363         struct v9fs_rpcreq *rptr;
364         struct v9fs_rpcreq *req;
365         struct v9fs_rpcreq *rreq;
366         int err = 0;
367
368         allow_signal(SIGKILL);
369         set_current_state(TASK_INTERRUPTIBLE);
370         complete(&v9ses->proccmpl);
371         while (!kthread_should_stop() && err >= 0) {
372                 req = rptr = rreq = NULL;
373
374                 rcall = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL);
375                 if (!rcall) {
376                         eprintk(KERN_ERR, "no memory for buffers\n");
377                         break;
378                 }
379
380                 err = read_message(v9ses, rcall, v9ses->maxdata + V9FS_IOHDRSZ);
381                 spin_lock(&v9ses->muxlock);
382                 if (err < 0) {
383                         list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) {
384                                 rreq->err = err;
385                         }
386                         if(err != -ERESTARTSYS)
387                                 eprintk(KERN_ERR,
388                                         "Transport error while reading message %d\n", err);
389                 } else {
390                         list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) {
391                                 if (rreq->tcall->tag == rcall->tag) {
392                                         req = rreq;
393                                         req->rcall = rcall;
394                                         break;
395                                 }
396                         }
397                 }
398
399                 if (req && (req->tcall->id == TFLUSH)) {
400                         struct v9fs_rpcreq *treq = NULL;
401                         list_for_each_entry_safe(treq, rptr, &v9ses->mux_fcalls, next) {
402                                 if (treq->tcall->tag ==
403                                     req->tcall->params.tflush.oldtag) {
404                                         list_del(&rptr->next);
405                                         kfree(treq->rcall);
406                                         break;
407                                 }
408                         }
409                 }
410
411                 spin_unlock(&v9ses->muxlock);
412
413                 if (!req) {
414                         if (err >= 0)
415                                 dprintk(DEBUG_ERROR,
416                                         "unexpected response: id %d tag %d\n",
417                                         rcall->id, rcall->tag);
418
419                         kfree(rcall);
420                 }
421
422                 wake_up_all(&v9ses->read_wait);
423                 set_current_state(TASK_INTERRUPTIBLE);
424         }
425
426         v9ses->transport->close(v9ses->transport);
427
428         /* Inform all pending processes about the failure */
429         wake_up_all(&v9ses->read_wait);
430
431         if (signal_pending(current))
432                 complete(&v9ses->proccmpl);
433
434         dprintk(DEBUG_MUX, "recvproc: end\n");
435         v9ses->recvproc = NULL;
436
437         return err >= 0;
438 }
439
440 /**
441  * v9fs_mux_init - initialize multiplexer (spawn kproc)
442  * @v9ses: session info structure
443  * @dev_name: mount device information (to create unique kproc)
444  *
445  */
446
447 int v9fs_mux_init(struct v9fs_session_info *v9ses, const char *dev_name)
448 {
449         char procname[60];
450
451         strncpy(procname, dev_name, sizeof(procname));
452         procname[sizeof(procname) - 1] = 0;
453
454         init_waitqueue_head(&v9ses->read_wait);
455         init_completion(&v9ses->fcread);
456         init_completion(&v9ses->proccmpl);
457         spin_lock_init(&v9ses->muxlock);
458         INIT_LIST_HEAD(&v9ses->mux_fcalls);
459         v9ses->recvproc = NULL;
460         v9ses->curfcall = NULL;
461
462         v9ses->recvproc = kthread_create(v9fs_recvproc, v9ses,
463                                          "v9fs_recvproc %s", procname);
464
465         if (IS_ERR(v9ses->recvproc)) {
466                 eprintk(KERN_ERR, "cannot create receiving thread\n");
467                 v9fs_session_close(v9ses);
468                 return -ECONNABORTED;
469         }
470
471         wake_up_process(v9ses->recvproc);
472         wait_for_completion(&v9ses->proccmpl);
473
474         return 0;
475 }