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