9p: rework client code to use new protocol support functions
[linux-2.6-block.git] / net / 9p / protocol.c
CommitLineData
ace51c4d
EVH
1/*
2 * net/9p/protocol.c
3 *
4 * 9P Protocol Support Code
5 *
6 * Copyright (C) 2008 by Eric Van Hensbergen <ericvh@gmail.com>
7 *
8 * Base on code from Anthony Liguori <aliguori@us.ibm.com>
9 * Copyright (C) 2008 by IBM, Corp.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2
13 * as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to:
22 * Free Software Foundation
23 * 51 Franklin Street, Fifth Floor
24 * Boston, MA 02111-1301 USA
25 *
26 */
27
28#include <linux/module.h>
29#include <linux/errno.h>
51a87c55 30#include <linux/uaccess.h>
ace51c4d
EVH
31#include <net/9p/9p.h>
32#include <net/9p/client.h>
33#include "protocol.h"
34
35#ifndef MIN
36#define MIN(a, b) (((a) < (b)) ? (a) : (b))
37#endif
38
39#ifndef MAX
40#define MAX(a, b) (((a) > (b)) ? (a) : (b))
41#endif
42
43#ifndef offset_of
44#define offset_of(type, memb) \
45 ((unsigned long)(&((type *)0)->memb))
46#endif
47#ifndef container_of
48#define container_of(obj, type, memb) \
49 ((type *)(((char *)obj) - offset_of(type, memb)))
50#endif
51
52static int
53p9pdu_writef(struct p9_fcall *pdu, int optional, const char *fmt, ...);
54
51a87c55
EVH
55#define PACKET_DEBUG 0
56
57void
58p9pdu_dump(int way, struct p9_fcall *pdu)
59{
60 int i, n;
61 u8 *data = pdu->sdata;
62 int datalen = pdu->size;
63 char buf[255];
64 int buflen = 255;
65
66 i = n = 0;
67 if (datalen > (buflen-16))
68 datalen = buflen-16;
69 while (i < datalen) {
70 n += scnprintf(buf + n, buflen - n, "%02x ", data[i]);
71 if (i%4 == 3)
72 n += scnprintf(buf + n, buflen - n, " ");
73 if (i%32 == 31)
74 n += scnprintf(buf + n, buflen - n, "\n");
75
76 i++;
77 }
78 n += scnprintf(buf + n, buflen - n, "\n");
79
80 if (way)
81 printk(KERN_NOTICE "[[(%d)[ %s\n", datalen, buf);
82 else
83 printk(KERN_NOTICE "]](%d)] %s\n", datalen, buf);
84}
85EXPORT_SYMBOL(p9pdu_dump);
86
ace51c4d
EVH
87void p9stat_free(struct p9_wstat *stbuf)
88{
89 kfree(stbuf->name);
90 kfree(stbuf->uid);
91 kfree(stbuf->gid);
92 kfree(stbuf->muid);
93 kfree(stbuf->extension);
94}
95EXPORT_SYMBOL(p9stat_free);
96
97static size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size)
98{
99 size_t len = MIN(pdu->size - pdu->offset, size);
100 memcpy(data, &pdu->sdata[pdu->offset], len);
101 pdu->offset += len;
102 return size - len;
103}
104
105static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size)
106{
107 size_t len = MIN(pdu->capacity - pdu->size, size);
108 memcpy(&pdu->sdata[pdu->size], data, len);
109 pdu->size += len;
110 return size - len;
111}
112
51a87c55
EVH
113static size_t
114pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size)
115{
116 size_t len = MIN(pdu->capacity - pdu->size, size);
117 int err = copy_from_user(&pdu->sdata[pdu->size], udata, len);
118 if (err)
119 printk(KERN_WARNING "pdu_write_u returning: %d\n", err);
120
121 pdu->size += len;
122 return size - len;
123}
124
ace51c4d
EVH
125/*
126 b - int8_t
127 w - int16_t
128 d - int32_t
129 q - int64_t
130 s - string
131 S - stat
132 Q - qid
133 D - data blob (int32_t size followed by void *, results are not freed)
134 T - array of strings (int16_t count, followed by strings)
135 R - array of qids (int16_t count, followed by qids)
136 ? - if optional = 1, continue parsing
137*/
138
139static int
140p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
141{
142 const char *ptr;
143 int errcode = 0;
144
145 for (ptr = fmt; *ptr; ptr++) {
146 switch (*ptr) {
147 case 'b':{
148 int8_t *val = va_arg(ap, int8_t *);
149 if (pdu_read(pdu, val, sizeof(*val))) {
150 errcode = -EFAULT;
151 break;
152 }
153 }
154 break;
155 case 'w':{
156 int16_t *val = va_arg(ap, int16_t *);
157 if (pdu_read(pdu, val, sizeof(*val))) {
158 errcode = -EFAULT;
159 break;
160 }
161 *val = cpu_to_le16(*val);
162 }
163 break;
164 case 'd':{
165 int32_t *val = va_arg(ap, int32_t *);
166 if (pdu_read(pdu, val, sizeof(*val))) {
167 errcode = -EFAULT;
168 break;
169 }
170 *val = cpu_to_le32(*val);
171 }
172 break;
173 case 'q':{
174 int64_t *val = va_arg(ap, int64_t *);
175 if (pdu_read(pdu, val, sizeof(*val))) {
176 errcode = -EFAULT;
177 break;
178 }
179 *val = cpu_to_le64(*val);
180 }
181 break;
182 case 's':{
183 char **ptr = va_arg(ap, char **);
184 int16_t len;
185 int size;
186
187 errcode = p9pdu_readf(pdu, optional, "w", &len);
188 if (errcode)
189 break;
190
191 size = MAX(len, 0);
192
193 *ptr = kmalloc(size + 1, GFP_KERNEL);
194 if (*ptr == NULL) {
195 errcode = -EFAULT;
196 break;
197 }
198 if (pdu_read(pdu, *ptr, size)) {
199 errcode = -EFAULT;
200 kfree(*ptr);
201 *ptr = NULL;
202 } else
203 (*ptr)[size] = 0;
204 }
205 break;
206 case 'Q':{
207 struct p9_qid *qid =
208 va_arg(ap, struct p9_qid *);
209
210 errcode = p9pdu_readf(pdu, optional, "bdq",
211 &qid->type, &qid->version,
212 &qid->path);
213 }
214 break;
215 case 'S':{
216 struct p9_wstat *stbuf =
217 va_arg(ap, struct p9_wstat *);
218
219 stbuf->extension = NULL;
220 stbuf->n_uid = stbuf->n_gid = stbuf->n_muid =
221 -1;
ace51c4d
EVH
222 errcode =
223 p9pdu_readf(pdu, optional,
224 "wwdQdddqssss?sddd",
225 &stbuf->size, &stbuf->type,
226 &stbuf->dev, &stbuf->qid,
227 &stbuf->mode, &stbuf->atime,
228 &stbuf->mtime, &stbuf->length,
229 &stbuf->name, &stbuf->uid,
230 &stbuf->gid, &stbuf->muid,
231 &stbuf->extension,
232 &stbuf->n_uid, &stbuf->n_gid,
233 &stbuf->n_muid);
234 if (errcode)
235 p9stat_free(stbuf);
236 }
237 break;
238 case 'D':{
239 int32_t *count = va_arg(ap, int32_t *);
240 void **data = va_arg(ap, void **);
241
242 errcode =
243 p9pdu_readf(pdu, optional, "d", count);
244 if (!errcode) {
245 *count =
246 MIN(*count,
247 pdu->size - pdu->offset);
248 *data = &pdu->sdata[pdu->offset];
249 }
250 }
251 break;
252 case 'T':{
253 int16_t *nwname = va_arg(ap, int16_t *);
254 char ***wnames = va_arg(ap, char ***);
255
256 errcode =
257 p9pdu_readf(pdu, optional, "w", nwname);
258 if (!errcode) {
259 *wnames =
260 kmalloc(sizeof(char *) * *nwname,
261 GFP_KERNEL);
262 if (!*wnames)
263 errcode = -ENOMEM;
264 }
265
266 if (!errcode) {
267 int i;
268
269 for (i = 0; i < *nwname; i++) {
270 errcode =
271 p9pdu_readf(pdu, optional,
272 "s",
273 &(*wnames)[i]);
274 if (errcode)
275 break;
276 }
277 }
278
279 if (errcode) {
280 if (*wnames) {
281 int i;
282
283 for (i = 0; i < *nwname; i++)
284 kfree((*wnames)[i]);
285 }
286 kfree(*wnames);
287 *wnames = NULL;
288 }
289 }
290 break;
291 case 'R':{
292 int16_t *nwqid = va_arg(ap, int16_t *);
293 struct p9_qid **wqids =
294 va_arg(ap, struct p9_qid **);
295
296 *wqids = NULL;
297
298 errcode =
299 p9pdu_readf(pdu, optional, "w", nwqid);
300 if (!errcode) {
301 *wqids =
302 kmalloc(*nwqid *
303 sizeof(struct p9_qid),
304 GFP_KERNEL);
305 if (*wqids == NULL)
306 errcode = -ENOMEM;
307 }
308
309 if (!errcode) {
310 int i;
311
312 for (i = 0; i < *nwqid; i++) {
313 errcode =
314 p9pdu_readf(pdu, optional,
315 "Q",
316 &(*wqids)[i]);
317 if (errcode)
318 break;
319 }
320 }
321
322 if (errcode) {
323 kfree(*wqids);
324 *wqids = NULL;
325 }
326 }
327 break;
328 case '?':
329 if (!optional)
330 return 0;
331 break;
332 default:
333 BUG();
334 break;
335 }
336
337 if (errcode)
338 break;
339 }
340
341 return errcode;
342}
343
344int
345p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap)
346{
347 const char *ptr;
348 int errcode = 0;
349
350 for (ptr = fmt; *ptr; ptr++) {
351 switch (*ptr) {
352 case 'b':{
353 int8_t val = va_arg(ap, int);
354 if (pdu_write(pdu, &val, sizeof(val)))
355 errcode = -EFAULT;
356 }
357 break;
358 case 'w':{
359 int16_t val = va_arg(ap, int);
360 if (pdu_write(pdu, &val, sizeof(val)))
361 errcode = -EFAULT;
362 }
363 break;
364 case 'd':{
365 int32_t val = va_arg(ap, int32_t);
366 if (pdu_write(pdu, &val, sizeof(val)))
367 errcode = -EFAULT;
368 }
369 break;
370 case 'q':{
371 int64_t val = va_arg(ap, int64_t);
372 if (pdu_write(pdu, &val, sizeof(val)))
373 errcode = -EFAULT;
374 }
375 break;
376 case 's':{
377 const char *ptr = va_arg(ap, const char *);
378 int16_t len = 0;
ace51c4d
EVH
379 if (ptr)
380 len = MIN(strlen(ptr), USHORT_MAX);
381
382 errcode = p9pdu_writef(pdu, optional, "w", len);
383 if (!errcode && pdu_write(pdu, ptr, len))
384 errcode = -EFAULT;
385 }
386 break;
387 case 'Q':{
388 const struct p9_qid *qid =
389 va_arg(ap, const struct p9_qid *);
390 errcode =
391 p9pdu_writef(pdu, optional, "bdq",
392 qid->type, qid->version,
393 qid->path);
394 } break;
395 case 'S':{
396 const struct p9_wstat *stbuf =
397 va_arg(ap, const struct p9_wstat *);
398 errcode =
399 p9pdu_writef(pdu, optional,
400 "wwdQdddqssss?sddd",
401 stbuf->size, stbuf->type,
51a87c55 402 stbuf->dev, &stbuf->qid,
ace51c4d
EVH
403 stbuf->mode, stbuf->atime,
404 stbuf->mtime, stbuf->length,
405 stbuf->name, stbuf->uid,
406 stbuf->gid, stbuf->muid,
407 stbuf->extension, stbuf->n_uid,
408 stbuf->n_gid, stbuf->n_muid);
409 } break;
410 case 'D':{
411 int32_t count = va_arg(ap, int32_t);
412 const void *data = va_arg(ap, const void *);
413
414 errcode =
415 p9pdu_writef(pdu, optional, "d", count);
416 if (!errcode && pdu_write(pdu, data, count))
417 errcode = -EFAULT;
418 }
419 break;
51a87c55
EVH
420 case 'U':{
421 int32_t count = va_arg(ap, int32_t);
422 const char __user *udata =
423 va_arg(ap, const void *);
424 errcode =
425 p9pdu_writef(pdu, optional, "d", count);
426 if (!errcode && pdu_write_u(pdu, udata, count))
427 errcode = -EFAULT;
428 }
429 break;
ace51c4d
EVH
430 case 'T':{
431 int16_t nwname = va_arg(ap, int);
432 const char **wnames = va_arg(ap, const char **);
433
434 errcode =
435 p9pdu_writef(pdu, optional, "w", nwname);
436 if (!errcode) {
437 int i;
438
439 for (i = 0; i < nwname; i++) {
440 errcode =
441 p9pdu_writef(pdu, optional,
442 "s",
443 wnames[i]);
444 if (errcode)
445 break;
446 }
447 }
448 }
449 break;
450 case 'R':{
451 int16_t nwqid = va_arg(ap, int);
452 struct p9_qid *wqids =
453 va_arg(ap, struct p9_qid *);
454
455 errcode =
456 p9pdu_writef(pdu, optional, "w", nwqid);
457 if (!errcode) {
458 int i;
459
460 for (i = 0; i < nwqid; i++) {
461 errcode =
462 p9pdu_writef(pdu, optional,
463 "Q",
464 &wqids[i]);
465 if (errcode)
466 break;
467 }
468 }
469 }
470 break;
471 case '?':
472 if (!optional)
473 return 0;
474 break;
475 default:
476 BUG();
477 break;
478 }
479
480 if (errcode)
481 break;
482 }
483
484 return errcode;
485}
486
487int p9pdu_readf(struct p9_fcall *pdu, int optional, const char *fmt, ...)
488{
489 va_list ap;
490 int ret;
491
492 va_start(ap, fmt);
493 ret = p9pdu_vreadf(pdu, optional, fmt, ap);
494 va_end(ap);
495
496 return ret;
497}
498
499static int
500p9pdu_writef(struct p9_fcall *pdu, int optional, const char *fmt, ...)
501{
502 va_list ap;
503 int ret;
504
505 va_start(ap, fmt);
506 ret = p9pdu_vwritef(pdu, optional, fmt, ap);
507 va_end(ap);
508
509 return ret;
510}
51a87c55
EVH
511
512int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type)
513{
514 return p9pdu_writef(pdu, 0, "dbw", 0, type, tag);
515}
516
517int p9pdu_finalize(struct p9_fcall *pdu)
518{
519 int size = pdu->size;
520 int err;
521
522 pdu->size = 0;
523 err = p9pdu_writef(pdu, 0, "d", size);
524 pdu->size = size;
525
526 if (PACKET_DEBUG)
527 p9pdu_dump(0, pdu);
528
529 return err;
530}
531
532void p9pdu_reset(struct p9_fcall *pdu)
533{
534 pdu->offset = 0;
535 pdu->size = 0;
536}