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