9p: rework client code to use new protocol support functions
[linux-2.6.git] / net / 9p / protocol.c
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>
30 #include <linux/uaccess.h>
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
52 static int
53 p9pdu_writef(struct p9_fcall *pdu, int optional, const char *fmt, ...);
54
55 #define PACKET_DEBUG 0
56
57 void
58 p9pdu_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 }
85 EXPORT_SYMBOL(p9pdu_dump);
86
87 void 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 }
95 EXPORT_SYMBOL(p9stat_free);
96
97 static 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
105 static 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
113 static size_t
114 pdu_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
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
139 static int
140 p9pdu_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;
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
344 int
345 p9pdu_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;
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,
402                                                  stbuf->dev, &stbuf->qid,
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;
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;
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
487 int 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
499 static int
500 p9pdu_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 }
511
512 int 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
517 int 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
532 void p9pdu_reset(struct p9_fcall *pdu)
533 {
534         pdu->offset = 0;
535         pdu->size = 0;
536 }