8a4084fa8b5a907df17db9b023c215d1bb7da772
[pandora-kernel.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/kernel.h>
31 #include <linux/uaccess.h>
32 #include <linux/slab.h>
33 #include <linux/sched.h>
34 #include <linux/stddef.h>
35 #include <linux/types.h>
36 #include <net/9p/9p.h>
37 #include <net/9p/client.h>
38 #include "protocol.h"
39
40 static int
41 p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...);
42
43 #ifdef CONFIG_NET_9P_DEBUG
44 void
45 p9pdu_dump(int way, struct p9_fcall *pdu)
46 {
47         int i, n;
48         u8 *data = pdu->sdata;
49         int datalen = pdu->size;
50         char buf[255];
51         int buflen = 255;
52
53         i = n = 0;
54         if (datalen > (buflen-16))
55                 datalen = buflen-16;
56         while (i < datalen) {
57                 n += scnprintf(buf + n, buflen - n, "%02x ", data[i]);
58                 if (i%4 == 3)
59                         n += scnprintf(buf + n, buflen - n, " ");
60                 if (i%32 == 31)
61                         n += scnprintf(buf + n, buflen - n, "\n");
62
63                 i++;
64         }
65         n += scnprintf(buf + n, buflen - n, "\n");
66
67         if (way)
68                 P9_DPRINTK(P9_DEBUG_PKT, "[[[(%d) %s\n", datalen, buf);
69         else
70                 P9_DPRINTK(P9_DEBUG_PKT, "]]](%d) %s\n", datalen, buf);
71 }
72 #else
73 void
74 p9pdu_dump(int way, struct p9_fcall *pdu)
75 {
76 }
77 #endif
78 EXPORT_SYMBOL(p9pdu_dump);
79
80 void p9stat_free(struct p9_wstat *stbuf)
81 {
82         kfree(stbuf->name);
83         kfree(stbuf->uid);
84         kfree(stbuf->gid);
85         kfree(stbuf->muid);
86         kfree(stbuf->extension);
87 }
88 EXPORT_SYMBOL(p9stat_free);
89
90 static size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size)
91 {
92         size_t len = min(pdu->size - pdu->offset, size);
93         memcpy(data, &pdu->sdata[pdu->offset], len);
94         pdu->offset += len;
95         return size - len;
96 }
97
98 static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size)
99 {
100         size_t len = min(pdu->capacity - pdu->size, size);
101         memcpy(&pdu->sdata[pdu->size], data, len);
102         pdu->size += len;
103         return size - len;
104 }
105
106 static size_t
107 pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size)
108 {
109         size_t len = min(pdu->capacity - pdu->size, size);
110         if (copy_from_user(&pdu->sdata[pdu->size], udata, len))
111                 len = 0;
112
113         pdu->size += len;
114         return size - len;
115 }
116
117 static size_t
118 pdu_write_urw(struct p9_fcall *pdu, const char *kdata, const char __user *udata,
119                 size_t size)
120 {
121         BUG_ON(pdu->size > P9_IOHDRSZ);
122         pdu->pubuf = (char __user *)udata;
123         pdu->pkbuf = (char *)kdata;
124         pdu->pbuf_size = size;
125         return 0;
126 }
127
128 static size_t
129 pdu_write_readdir(struct p9_fcall *pdu, const char *kdata, size_t size)
130 {
131         BUG_ON(pdu->size > P9_READDIRHDRSZ);
132         pdu->pkbuf = (char *)kdata;
133         pdu->pbuf_size = size;
134         return 0;
135 }
136
137 /*
138         b - int8_t
139         w - int16_t
140         d - int32_t
141         q - int64_t
142         s - string
143         S - stat
144         Q - qid
145         D - data blob (int32_t size followed by void *, results are not freed)
146         T - array of strings (int16_t count, followed by strings)
147         R - array of qids (int16_t count, followed by qids)
148         A - stat for 9p2000.L (p9_stat_dotl)
149         ? - if optional = 1, continue parsing
150 */
151
152 static int
153 p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
154         va_list ap)
155 {
156         const char *ptr;
157         int errcode = 0;
158
159         for (ptr = fmt; *ptr; ptr++) {
160                 switch (*ptr) {
161                 case 'b':{
162                                 int8_t *val = va_arg(ap, int8_t *);
163                                 if (pdu_read(pdu, val, sizeof(*val))) {
164                                         errcode = -EFAULT;
165                                         break;
166                                 }
167                         }
168                         break;
169                 case 'w':{
170                                 int16_t *val = va_arg(ap, int16_t *);
171                                 __le16 le_val;
172                                 if (pdu_read(pdu, &le_val, sizeof(le_val))) {
173                                         errcode = -EFAULT;
174                                         break;
175                                 }
176                                 *val = le16_to_cpu(le_val);
177                         }
178                         break;
179                 case 'd':{
180                                 int32_t *val = va_arg(ap, int32_t *);
181                                 __le32 le_val;
182                                 if (pdu_read(pdu, &le_val, sizeof(le_val))) {
183                                         errcode = -EFAULT;
184                                         break;
185                                 }
186                                 *val = le32_to_cpu(le_val);
187                         }
188                         break;
189                 case 'q':{
190                                 int64_t *val = va_arg(ap, int64_t *);
191                                 __le64 le_val;
192                                 if (pdu_read(pdu, &le_val, sizeof(le_val))) {
193                                         errcode = -EFAULT;
194                                         break;
195                                 }
196                                 *val = le64_to_cpu(le_val);
197                         }
198                         break;
199                 case 's':{
200                                 char **sptr = va_arg(ap, char **);
201                                 uint16_t len;
202
203                                 errcode = p9pdu_readf(pdu, proto_version,
204                                                                 "w", &len);
205                                 if (errcode)
206                                         break;
207
208                                 *sptr = kmalloc(len + 1, GFP_NOFS);
209                                 if (*sptr == NULL) {
210                                         errcode = -EFAULT;
211                                         break;
212                                 }
213                                 if (pdu_read(pdu, *sptr, len)) {
214                                         errcode = -EFAULT;
215                                         kfree(*sptr);
216                                         *sptr = NULL;
217                                 } else
218                                         (*sptr)[len] = 0;
219                         }
220                         break;
221                 case 'Q':{
222                                 struct p9_qid *qid =
223                                     va_arg(ap, struct p9_qid *);
224
225                                 errcode = p9pdu_readf(pdu, proto_version, "bdq",
226                                                       &qid->type, &qid->version,
227                                                       &qid->path);
228                         }
229                         break;
230                 case 'S':{
231                                 struct p9_wstat *stbuf =
232                                     va_arg(ap, struct p9_wstat *);
233
234                                 memset(stbuf, 0, sizeof(struct p9_wstat));
235                                 stbuf->n_uid = stbuf->n_gid = stbuf->n_muid =
236                                                                         -1;
237                                 errcode =
238                                     p9pdu_readf(pdu, proto_version,
239                                                 "wwdQdddqssss?sddd",
240                                                 &stbuf->size, &stbuf->type,
241                                                 &stbuf->dev, &stbuf->qid,
242                                                 &stbuf->mode, &stbuf->atime,
243                                                 &stbuf->mtime, &stbuf->length,
244                                                 &stbuf->name, &stbuf->uid,
245                                                 &stbuf->gid, &stbuf->muid,
246                                                 &stbuf->extension,
247                                                 &stbuf->n_uid, &stbuf->n_gid,
248                                                 &stbuf->n_muid);
249                                 if (errcode)
250                                         p9stat_free(stbuf);
251                         }
252                         break;
253                 case 'D':{
254                                 uint32_t *count = va_arg(ap, uint32_t *);
255                                 void **data = va_arg(ap, void **);
256
257                                 errcode =
258                                     p9pdu_readf(pdu, proto_version, "d", count);
259                                 if (!errcode) {
260                                         *count =
261                                             min_t(uint32_t, *count,
262                                                   pdu->size - pdu->offset);
263                                         *data = &pdu->sdata[pdu->offset];
264                                 }
265                         }
266                         break;
267                 case 'T':{
268                                 int16_t *nwname = va_arg(ap, int16_t *);
269                                 char ***wnames = va_arg(ap, char ***);
270
271                                 errcode = p9pdu_readf(pdu, proto_version,
272                                                                 "w", nwname);
273                                 if (!errcode) {
274                                         *wnames =
275                                             kmalloc(sizeof(char *) * *nwname,
276                                                     GFP_NOFS);
277                                         if (!*wnames)
278                                                 errcode = -ENOMEM;
279                                 }
280
281                                 if (!errcode) {
282                                         int i;
283
284                                         for (i = 0; i < *nwname; i++) {
285                                                 errcode =
286                                                     p9pdu_readf(pdu,
287                                                                 proto_version,
288                                                                 "s",
289                                                                 &(*wnames)[i]);
290                                                 if (errcode)
291                                                         break;
292                                         }
293                                 }
294
295                                 if (errcode) {
296                                         if (*wnames) {
297                                                 int i;
298
299                                                 for (i = 0; i < *nwname; i++)
300                                                         kfree((*wnames)[i]);
301                                         }
302                                         kfree(*wnames);
303                                         *wnames = NULL;
304                                 }
305                         }
306                         break;
307                 case 'R':{
308                                 int16_t *nwqid = va_arg(ap, int16_t *);
309                                 struct p9_qid **wqids =
310                                     va_arg(ap, struct p9_qid **);
311
312                                 *wqids = NULL;
313
314                                 errcode =
315                                     p9pdu_readf(pdu, proto_version, "w", nwqid);
316                                 if (!errcode) {
317                                         *wqids =
318                                             kmalloc(*nwqid *
319                                                     sizeof(struct p9_qid),
320                                                     GFP_NOFS);
321                                         if (*wqids == NULL)
322                                                 errcode = -ENOMEM;
323                                 }
324
325                                 if (!errcode) {
326                                         int i;
327
328                                         for (i = 0; i < *nwqid; i++) {
329                                                 errcode =
330                                                     p9pdu_readf(pdu,
331                                                                 proto_version,
332                                                                 "Q",
333                                                                 &(*wqids)[i]);
334                                                 if (errcode)
335                                                         break;
336                                         }
337                                 }
338
339                                 if (errcode) {
340                                         kfree(*wqids);
341                                         *wqids = NULL;
342                                 }
343                         }
344                         break;
345                 case 'A': {
346                                 struct p9_stat_dotl *stbuf =
347                                     va_arg(ap, struct p9_stat_dotl *);
348
349                                 memset(stbuf, 0, sizeof(struct p9_stat_dotl));
350                                 errcode =
351                                     p9pdu_readf(pdu, proto_version,
352                                         "qQdddqqqqqqqqqqqqqqq",
353                                         &stbuf->st_result_mask,
354                                         &stbuf->qid,
355                                         &stbuf->st_mode,
356                                         &stbuf->st_uid, &stbuf->st_gid,
357                                         &stbuf->st_nlink,
358                                         &stbuf->st_rdev, &stbuf->st_size,
359                                         &stbuf->st_blksize, &stbuf->st_blocks,
360                                         &stbuf->st_atime_sec,
361                                         &stbuf->st_atime_nsec,
362                                         &stbuf->st_mtime_sec,
363                                         &stbuf->st_mtime_nsec,
364                                         &stbuf->st_ctime_sec,
365                                         &stbuf->st_ctime_nsec,
366                                         &stbuf->st_btime_sec,
367                                         &stbuf->st_btime_nsec,
368                                         &stbuf->st_gen,
369                                         &stbuf->st_data_version);
370                         }
371                         break;
372                 case '?':
373                         if ((proto_version != p9_proto_2000u) &&
374                                 (proto_version != p9_proto_2000L))
375                                 return 0;
376                         break;
377                 default:
378                         BUG();
379                         break;
380                 }
381
382                 if (errcode)
383                         break;
384         }
385
386         return errcode;
387 }
388
389 int
390 p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt,
391         va_list ap)
392 {
393         const char *ptr;
394         int errcode = 0;
395
396         for (ptr = fmt; *ptr; ptr++) {
397                 switch (*ptr) {
398                 case 'b':{
399                                 int8_t val = va_arg(ap, int);
400                                 if (pdu_write(pdu, &val, sizeof(val)))
401                                         errcode = -EFAULT;
402                         }
403                         break;
404                 case 'w':{
405                                 __le16 val = cpu_to_le16(va_arg(ap, int));
406                                 if (pdu_write(pdu, &val, sizeof(val)))
407                                         errcode = -EFAULT;
408                         }
409                         break;
410                 case 'd':{
411                                 __le32 val = cpu_to_le32(va_arg(ap, int32_t));
412                                 if (pdu_write(pdu, &val, sizeof(val)))
413                                         errcode = -EFAULT;
414                         }
415                         break;
416                 case 'q':{
417                                 __le64 val = cpu_to_le64(va_arg(ap, int64_t));
418                                 if (pdu_write(pdu, &val, sizeof(val)))
419                                         errcode = -EFAULT;
420                         }
421                         break;
422                 case 's':{
423                                 const char *sptr = va_arg(ap, const char *);
424                                 uint16_t len = 0;
425                                 if (sptr)
426                                         len = min_t(uint16_t, strlen(sptr),
427                                                                 USHRT_MAX);
428
429                                 errcode = p9pdu_writef(pdu, proto_version,
430                                                                 "w", len);
431                                 if (!errcode && pdu_write(pdu, sptr, len))
432                                         errcode = -EFAULT;
433                         }
434                         break;
435                 case 'Q':{
436                                 const struct p9_qid *qid =
437                                     va_arg(ap, const struct p9_qid *);
438                                 errcode =
439                                     p9pdu_writef(pdu, proto_version, "bdq",
440                                                  qid->type, qid->version,
441                                                  qid->path);
442                         } break;
443                 case 'S':{
444                                 const struct p9_wstat *stbuf =
445                                     va_arg(ap, const struct p9_wstat *);
446                                 errcode =
447                                     p9pdu_writef(pdu, proto_version,
448                                                  "wwdQdddqssss?sddd",
449                                                  stbuf->size, stbuf->type,
450                                                  stbuf->dev, &stbuf->qid,
451                                                  stbuf->mode, stbuf->atime,
452                                                  stbuf->mtime, stbuf->length,
453                                                  stbuf->name, stbuf->uid,
454                                                  stbuf->gid, stbuf->muid,
455                                                  stbuf->extension, stbuf->n_uid,
456                                                  stbuf->n_gid, stbuf->n_muid);
457                         } break;
458                 case 'D':{
459                                 uint32_t count = va_arg(ap, uint32_t);
460                                 const void *data = va_arg(ap, const void *);
461
462                                 errcode = p9pdu_writef(pdu, proto_version, "d",
463                                                                         count);
464                                 if (!errcode && pdu_write(pdu, data, count))
465                                         errcode = -EFAULT;
466                         }
467                         break;
468                 case 'E':{
469                                  int32_t cnt = va_arg(ap, int32_t);
470                                  const char *k = va_arg(ap, const void *);
471                                  const char *u = va_arg(ap, const void *);
472                                  errcode = p9pdu_writef(pdu, proto_version, "d",
473                                                  cnt);
474                                  if (!errcode && pdu_write_urw(pdu, k, u, cnt))
475                                         errcode = -EFAULT;
476                          }
477                          break;
478                 case 'F':{
479                                  int32_t cnt = va_arg(ap, int32_t);
480                                  const char *k = va_arg(ap, const void *);
481                                  errcode = p9pdu_writef(pdu, proto_version, "d",
482                                                  cnt);
483                                  if (!errcode && pdu_write_readdir(pdu, k, cnt))
484                                         errcode = -EFAULT;
485                          }
486                          break;
487                 case 'U':{
488                                 int32_t count = va_arg(ap, int32_t);
489                                 const char __user *udata =
490                                                 va_arg(ap, const void __user *);
491                                 errcode = p9pdu_writef(pdu, proto_version, "d",
492                                                                         count);
493                                 if (!errcode && pdu_write_u(pdu, udata, count))
494                                         errcode = -EFAULT;
495                         }
496                         break;
497                 case 'T':{
498                                 int16_t nwname = va_arg(ap, int);
499                                 const char **wnames = va_arg(ap, const char **);
500
501                                 errcode = p9pdu_writef(pdu, proto_version, "w",
502                                                                         nwname);
503                                 if (!errcode) {
504                                         int i;
505
506                                         for (i = 0; i < nwname; i++) {
507                                                 errcode =
508                                                     p9pdu_writef(pdu,
509                                                                 proto_version,
510                                                                  "s",
511                                                                  wnames[i]);
512                                                 if (errcode)
513                                                         break;
514                                         }
515                                 }
516                         }
517                         break;
518                 case 'R':{
519                                 int16_t nwqid = va_arg(ap, int);
520                                 struct p9_qid *wqids =
521                                     va_arg(ap, struct p9_qid *);
522
523                                 errcode = p9pdu_writef(pdu, proto_version, "w",
524                                                                         nwqid);
525                                 if (!errcode) {
526                                         int i;
527
528                                         for (i = 0; i < nwqid; i++) {
529                                                 errcode =
530                                                     p9pdu_writef(pdu,
531                                                                 proto_version,
532                                                                  "Q",
533                                                                  &wqids[i]);
534                                                 if (errcode)
535                                                         break;
536                                         }
537                                 }
538                         }
539                         break;
540                 case 'I':{
541                                 struct p9_iattr_dotl *p9attr = va_arg(ap,
542                                                         struct p9_iattr_dotl *);
543
544                                 errcode = p9pdu_writef(pdu, proto_version,
545                                                         "ddddqqqqq",
546                                                         p9attr->valid,
547                                                         p9attr->mode,
548                                                         p9attr->uid,
549                                                         p9attr->gid,
550                                                         p9attr->size,
551                                                         p9attr->atime_sec,
552                                                         p9attr->atime_nsec,
553                                                         p9attr->mtime_sec,
554                                                         p9attr->mtime_nsec);
555                         }
556                         break;
557                 case '?':
558                         if ((proto_version != p9_proto_2000u) &&
559                                 (proto_version != p9_proto_2000L))
560                                 return 0;
561                         break;
562                 default:
563                         BUG();
564                         break;
565                 }
566
567                 if (errcode)
568                         break;
569         }
570
571         return errcode;
572 }
573
574 int p9pdu_readf(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
575 {
576         va_list ap;
577         int ret;
578
579         va_start(ap, fmt);
580         ret = p9pdu_vreadf(pdu, proto_version, fmt, ap);
581         va_end(ap);
582
583         return ret;
584 }
585
586 static int
587 p9pdu_writef(struct p9_fcall *pdu, int proto_version, const char *fmt, ...)
588 {
589         va_list ap;
590         int ret;
591
592         va_start(ap, fmt);
593         ret = p9pdu_vwritef(pdu, proto_version, fmt, ap);
594         va_end(ap);
595
596         return ret;
597 }
598
599 int p9stat_read(char *buf, int len, struct p9_wstat *st, int proto_version)
600 {
601         struct p9_fcall fake_pdu;
602         int ret;
603
604         fake_pdu.size = len;
605         fake_pdu.capacity = len;
606         fake_pdu.sdata = buf;
607         fake_pdu.offset = 0;
608
609         ret = p9pdu_readf(&fake_pdu, proto_version, "S", st);
610         if (ret) {
611                 P9_DPRINTK(P9_DEBUG_9P, "<<< p9stat_read failed: %d\n", ret);
612                 p9pdu_dump(1, &fake_pdu);
613         }
614
615         return ret;
616 }
617 EXPORT_SYMBOL(p9stat_read);
618
619 int p9pdu_prepare(struct p9_fcall *pdu, int16_t tag, int8_t type)
620 {
621         pdu->id = type;
622         return p9pdu_writef(pdu, 0, "dbw", 0, type, tag);
623 }
624
625 int p9pdu_finalize(struct p9_fcall *pdu)
626 {
627         int size = pdu->size;
628         int err;
629
630         pdu->size = 0;
631         err = p9pdu_writef(pdu, 0, "d", size);
632         pdu->size = size;
633
634 #ifdef CONFIG_NET_9P_DEBUG
635         if ((p9_debug_level & P9_DEBUG_PKT) == P9_DEBUG_PKT)
636                 p9pdu_dump(0, pdu);
637 #endif
638
639         P9_DPRINTK(P9_DEBUG_9P, ">>> size=%d type: %d tag: %d\n", pdu->size,
640                                                         pdu->id, pdu->tag);
641
642         return err;
643 }
644
645 void p9pdu_reset(struct p9_fcall *pdu)
646 {
647         pdu->offset = 0;
648         pdu->size = 0;
649         pdu->private = NULL;
650         pdu->pubuf = NULL;
651         pdu->pkbuf = NULL;
652         pdu->pbuf_size = 0;
653 }
654
655 int p9dirent_read(char *buf, int len, struct p9_dirent *dirent,
656                                                 int proto_version)
657 {
658         struct p9_fcall fake_pdu;
659         int ret;
660         char *nameptr;
661
662         fake_pdu.size = len;
663         fake_pdu.capacity = len;
664         fake_pdu.sdata = buf;
665         fake_pdu.offset = 0;
666
667         ret = p9pdu_readf(&fake_pdu, proto_version, "Qqbs", &dirent->qid,
668                         &dirent->d_off, &dirent->d_type, &nameptr);
669         if (ret) {
670                 P9_DPRINTK(P9_DEBUG_9P, "<<< p9dirent_read failed: %d\n", ret);
671                 p9pdu_dump(1, &fake_pdu);
672                 goto out;
673         }
674
675         strcpy(dirent->d_name, nameptr);
676
677 out:
678         return fake_pdu.offset;
679 }
680 EXPORT_SYMBOL(p9dirent_read);