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