diff options
-rw-r--r-- | include/net/9p/9p.h | 8 | ||||
-rw-r--r-- | net/9p/Makefile | 1 | ||||
-rw-r--r-- | net/9p/protocol.c | 457 | ||||
-rw-r--r-- | net/9p/protocol.h | 31 |
4 files changed, 496 insertions, 1 deletions
diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index fb163e2e0de6..f9e25268b70f 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h | |||
@@ -509,6 +509,8 @@ struct p9_rwstat { | |||
509 | * @size: prefixed length of the structure | 509 | * @size: prefixed length of the structure |
510 | * @id: protocol operating identifier of type &p9_msg_t | 510 | * @id: protocol operating identifier of type &p9_msg_t |
511 | * @tag: transaction id of the request | 511 | * @tag: transaction id of the request |
512 | * @offset: used by marshalling routines to track currentposition in buffer | ||
513 | * @capacity: used by marshalling routines to track total capacity | ||
512 | * @sdata: payload | 514 | * @sdata: payload |
513 | * @params: per-operation parameters | 515 | * @params: per-operation parameters |
514 | * | 516 | * |
@@ -523,7 +525,11 @@ struct p9_fcall { | |||
523 | u32 size; | 525 | u32 size; |
524 | u8 id; | 526 | u8 id; |
525 | u16 tag; | 527 | u16 tag; |
526 | void *sdata; | 528 | |
529 | size_t offset; | ||
530 | size_t capacity; | ||
531 | |||
532 | uint8_t *sdata; | ||
527 | 533 | ||
528 | union { | 534 | union { |
529 | struct p9_tversion tversion; | 535 | struct p9_tversion tversion; |
diff --git a/net/9p/Makefile b/net/9p/Makefile index 519219480db1..84c23499a293 100644 --- a/net/9p/Makefile +++ b/net/9p/Makefile | |||
@@ -8,6 +8,7 @@ obj-$(CONFIG_NET_9P_VIRTIO) += 9pnet_virtio.o | |||
8 | error.o \ | 8 | error.o \ |
9 | fcprint.o \ | 9 | fcprint.o \ |
10 | util.o \ | 10 | util.o \ |
11 | protocol.o \ | ||
11 | trans_fd.o \ | 12 | trans_fd.o \ |
12 | 13 | ||
13 | 9pnet_virtio-objs := \ | 14 | 9pnet_virtio-objs := \ |
diff --git a/net/9p/protocol.c b/net/9p/protocol.c new file mode 100644 index 000000000000..43e98220e9a4 --- /dev/null +++ b/net/9p/protocol.c | |||
@@ -0,0 +1,457 @@ | |||
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 <net/9p/9p.h> | ||
31 | #include <net/9p/client.h> | ||
32 | #include "protocol.h" | ||
33 | |||
34 | #ifndef MIN | ||
35 | #define MIN(a, b) (((a) < (b)) ? (a) : (b)) | ||
36 | #endif | ||
37 | |||
38 | #ifndef MAX | ||
39 | #define MAX(a, b) (((a) > (b)) ? (a) : (b)) | ||
40 | #endif | ||
41 | |||
42 | #ifndef offset_of | ||
43 | #define offset_of(type, memb) \ | ||
44 | ((unsigned long)(&((type *)0)->memb)) | ||
45 | #endif | ||
46 | #ifndef container_of | ||
47 | #define container_of(obj, type, memb) \ | ||
48 | ((type *)(((char *)obj) - offset_of(type, memb))) | ||
49 | #endif | ||
50 | |||
51 | static int | ||
52 | p9pdu_writef(struct p9_fcall *pdu, int optional, const char *fmt, ...); | ||
53 | |||
54 | void p9stat_free(struct p9_wstat *stbuf) | ||
55 | { | ||
56 | kfree(stbuf->name); | ||
57 | kfree(stbuf->uid); | ||
58 | kfree(stbuf->gid); | ||
59 | kfree(stbuf->muid); | ||
60 | kfree(stbuf->extension); | ||
61 | } | ||
62 | EXPORT_SYMBOL(p9stat_free); | ||
63 | |||
64 | static size_t pdu_read(struct p9_fcall *pdu, void *data, size_t size) | ||
65 | { | ||
66 | size_t len = MIN(pdu->size - pdu->offset, size); | ||
67 | memcpy(data, &pdu->sdata[pdu->offset], len); | ||
68 | pdu->offset += len; | ||
69 | return size - len; | ||
70 | } | ||
71 | |||
72 | static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size) | ||
73 | { | ||
74 | size_t len = MIN(pdu->capacity - pdu->size, size); | ||
75 | memcpy(&pdu->sdata[pdu->size], data, len); | ||
76 | pdu->size += len; | ||
77 | return size - len; | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | b - int8_t | ||
82 | w - int16_t | ||
83 | d - int32_t | ||
84 | q - int64_t | ||
85 | s - string | ||
86 | S - stat | ||
87 | Q - qid | ||
88 | D - data blob (int32_t size followed by void *, results are not freed) | ||
89 | T - array of strings (int16_t count, followed by strings) | ||
90 | R - array of qids (int16_t count, followed by qids) | ||
91 | ? - if optional = 1, continue parsing | ||
92 | */ | ||
93 | |||
94 | static int | ||
95 | p9pdu_vreadf(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap) | ||
96 | { | ||
97 | const char *ptr; | ||
98 | int errcode = 0; | ||
99 | |||
100 | for (ptr = fmt; *ptr; ptr++) { | ||
101 | switch (*ptr) { | ||
102 | case 'b':{ | ||
103 | int8_t *val = va_arg(ap, int8_t *); | ||
104 | if (pdu_read(pdu, val, sizeof(*val))) { | ||
105 | errcode = -EFAULT; | ||
106 | break; | ||
107 | } | ||
108 | } | ||
109 | break; | ||
110 | case 'w':{ | ||
111 | int16_t *val = va_arg(ap, int16_t *); | ||
112 | if (pdu_read(pdu, val, sizeof(*val))) { | ||
113 | errcode = -EFAULT; | ||
114 | break; | ||
115 | } | ||
116 | *val = cpu_to_le16(*val); | ||
117 | } | ||
118 | break; | ||
119 | case 'd':{ | ||
120 | int32_t *val = va_arg(ap, int32_t *); | ||
121 | if (pdu_read(pdu, val, sizeof(*val))) { | ||
122 | errcode = -EFAULT; | ||
123 | break; | ||
124 | } | ||
125 | *val = cpu_to_le32(*val); | ||
126 | } | ||
127 | break; | ||
128 | case 'q':{ | ||
129 | int64_t *val = va_arg(ap, int64_t *); | ||
130 | if (pdu_read(pdu, val, sizeof(*val))) { | ||
131 | errcode = -EFAULT; | ||
132 | break; | ||
133 | } | ||
134 | *val = cpu_to_le64(*val); | ||
135 | } | ||
136 | break; | ||
137 | case 's':{ | ||
138 | char **ptr = va_arg(ap, char **); | ||
139 | int16_t len; | ||
140 | int size; | ||
141 | |||
142 | errcode = p9pdu_readf(pdu, optional, "w", &len); | ||
143 | if (errcode) | ||
144 | break; | ||
145 | |||
146 | size = MAX(len, 0); | ||
147 | |||
148 | *ptr = kmalloc(size + 1, GFP_KERNEL); | ||
149 | if (*ptr == NULL) { | ||
150 | errcode = -EFAULT; | ||
151 | break; | ||
152 | } | ||
153 | if (pdu_read(pdu, *ptr, size)) { | ||
154 | errcode = -EFAULT; | ||
155 | kfree(*ptr); | ||
156 | *ptr = NULL; | ||
157 | } else | ||
158 | (*ptr)[size] = 0; | ||
159 | } | ||
160 | break; | ||
161 | case 'Q':{ | ||
162 | struct p9_qid *qid = | ||
163 | va_arg(ap, struct p9_qid *); | ||
164 | |||
165 | errcode = p9pdu_readf(pdu, optional, "bdq", | ||
166 | &qid->type, &qid->version, | ||
167 | &qid->path); | ||
168 | } | ||
169 | break; | ||
170 | case 'S':{ | ||
171 | struct p9_wstat *stbuf = | ||
172 | va_arg(ap, struct p9_wstat *); | ||
173 | |||
174 | stbuf->extension = NULL; | ||
175 | stbuf->n_uid = stbuf->n_gid = stbuf->n_muid = | ||
176 | -1; | ||
177 | |||
178 | errcode = | ||
179 | p9pdu_readf(pdu, optional, | ||
180 | "wwdQdddqssss?sddd", | ||
181 | &stbuf->size, &stbuf->type, | ||
182 | &stbuf->dev, &stbuf->qid, | ||
183 | &stbuf->mode, &stbuf->atime, | ||
184 | &stbuf->mtime, &stbuf->length, | ||
185 | &stbuf->name, &stbuf->uid, | ||
186 | &stbuf->gid, &stbuf->muid, | ||
187 | &stbuf->extension, | ||
188 | &stbuf->n_uid, &stbuf->n_gid, | ||
189 | &stbuf->n_muid); | ||
190 | if (errcode) | ||
191 | p9stat_free(stbuf); | ||
192 | } | ||
193 | break; | ||
194 | case 'D':{ | ||
195 | int32_t *count = va_arg(ap, int32_t *); | ||
196 | void **data = va_arg(ap, void **); | ||
197 | |||
198 | errcode = | ||
199 | p9pdu_readf(pdu, optional, "d", count); | ||
200 | if (!errcode) { | ||
201 | *count = | ||
202 | MIN(*count, | ||
203 | pdu->size - pdu->offset); | ||
204 | *data = &pdu->sdata[pdu->offset]; | ||
205 | } | ||
206 | } | ||
207 | break; | ||
208 | case 'T':{ | ||
209 | int16_t *nwname = va_arg(ap, int16_t *); | ||
210 | char ***wnames = va_arg(ap, char ***); | ||
211 | |||
212 | errcode = | ||
213 | p9pdu_readf(pdu, optional, "w", nwname); | ||
214 | if (!errcode) { | ||
215 | *wnames = | ||
216 | kmalloc(sizeof(char *) * *nwname, | ||
217 | GFP_KERNEL); | ||
218 | if (!*wnames) | ||
219 | errcode = -ENOMEM; | ||
220 | } | ||
221 | |||
222 | if (!errcode) { | ||
223 | int i; | ||
224 | |||
225 | for (i = 0; i < *nwname; i++) { | ||
226 | errcode = | ||
227 | p9pdu_readf(pdu, optional, | ||
228 | "s", | ||
229 | &(*wnames)[i]); | ||
230 | if (errcode) | ||
231 | break; | ||
232 | } | ||
233 | } | ||
234 | |||
235 | if (errcode) { | ||
236 | if (*wnames) { | ||
237 | int i; | ||
238 | |||
239 | for (i = 0; i < *nwname; i++) | ||
240 | kfree((*wnames)[i]); | ||
241 | } | ||
242 | kfree(*wnames); | ||
243 | *wnames = NULL; | ||
244 | } | ||
245 | } | ||
246 | break; | ||
247 | case 'R':{ | ||
248 | int16_t *nwqid = va_arg(ap, int16_t *); | ||
249 | struct p9_qid **wqids = | ||
250 | va_arg(ap, struct p9_qid **); | ||
251 | |||
252 | *wqids = NULL; | ||
253 | |||
254 | errcode = | ||
255 | p9pdu_readf(pdu, optional, "w", nwqid); | ||
256 | if (!errcode) { | ||
257 | *wqids = | ||
258 | kmalloc(*nwqid * | ||
259 | sizeof(struct p9_qid), | ||
260 | GFP_KERNEL); | ||
261 | if (*wqids == NULL) | ||
262 | errcode = -ENOMEM; | ||
263 | } | ||
264 | |||
265 | if (!errcode) { | ||
266 | int i; | ||
267 | |||
268 | for (i = 0; i < *nwqid; i++) { | ||
269 | errcode = | ||
270 | p9pdu_readf(pdu, optional, | ||
271 | "Q", | ||
272 | &(*wqids)[i]); | ||
273 | if (errcode) | ||
274 | break; | ||
275 | } | ||
276 | } | ||
277 | |||
278 | if (errcode) { | ||
279 | kfree(*wqids); | ||
280 | *wqids = NULL; | ||
281 | } | ||
282 | } | ||
283 | break; | ||
284 | case '?': | ||
285 | if (!optional) | ||
286 | return 0; | ||
287 | break; | ||
288 | default: | ||
289 | BUG(); | ||
290 | break; | ||
291 | } | ||
292 | |||
293 | if (errcode) | ||
294 | break; | ||
295 | } | ||
296 | |||
297 | return errcode; | ||
298 | } | ||
299 | |||
300 | int | ||
301 | p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap) | ||
302 | { | ||
303 | const char *ptr; | ||
304 | int errcode = 0; | ||
305 | |||
306 | for (ptr = fmt; *ptr; ptr++) { | ||
307 | switch (*ptr) { | ||
308 | case 'b':{ | ||
309 | int8_t val = va_arg(ap, int); | ||
310 | if (pdu_write(pdu, &val, sizeof(val))) | ||
311 | errcode = -EFAULT; | ||
312 | } | ||
313 | break; | ||
314 | case 'w':{ | ||
315 | int16_t val = va_arg(ap, int); | ||
316 | if (pdu_write(pdu, &val, sizeof(val))) | ||
317 | errcode = -EFAULT; | ||
318 | } | ||
319 | break; | ||
320 | case 'd':{ | ||
321 | int32_t val = va_arg(ap, int32_t); | ||
322 | if (pdu_write(pdu, &val, sizeof(val))) | ||
323 | errcode = -EFAULT; | ||
324 | } | ||
325 | break; | ||
326 | case 'q':{ | ||
327 | int64_t val = va_arg(ap, int64_t); | ||
328 | if (pdu_write(pdu, &val, sizeof(val))) | ||
329 | errcode = -EFAULT; | ||
330 | } | ||
331 | break; | ||
332 | case 's':{ | ||
333 | const char *ptr = va_arg(ap, const char *); | ||
334 | int16_t len = 0; | ||
335 | |||
336 | if (ptr) | ||
337 | len = MIN(strlen(ptr), USHORT_MAX); | ||
338 | |||
339 | errcode = p9pdu_writef(pdu, optional, "w", len); | ||
340 | if (!errcode && pdu_write(pdu, ptr, len)) | ||
341 | errcode = -EFAULT; | ||
342 | } | ||
343 | break; | ||
344 | case 'Q':{ | ||
345 | const struct p9_qid *qid = | ||
346 | va_arg(ap, const struct p9_qid *); | ||
347 | errcode = | ||
348 | p9pdu_writef(pdu, optional, "bdq", | ||
349 | qid->type, qid->version, | ||
350 | qid->path); | ||
351 | } break; | ||
352 | case 'S':{ | ||
353 | const struct p9_wstat *stbuf = | ||
354 | va_arg(ap, const struct p9_wstat *); | ||
355 | errcode = | ||
356 | p9pdu_writef(pdu, optional, | ||
357 | "wwdQdddqssss?sddd", | ||
358 | stbuf->size, stbuf->type, | ||
359 | stbuf->dev, stbuf->qid, | ||
360 | stbuf->mode, stbuf->atime, | ||
361 | stbuf->mtime, stbuf->length, | ||
362 | stbuf->name, stbuf->uid, | ||
363 | stbuf->gid, stbuf->muid, | ||
364 | stbuf->extension, stbuf->n_uid, | ||
365 | stbuf->n_gid, stbuf->n_muid); | ||
366 | } break; | ||
367 | case 'D':{ | ||
368 | int32_t count = va_arg(ap, int32_t); | ||
369 | const void *data = va_arg(ap, const void *); | ||
370 | |||
371 | errcode = | ||
372 | p9pdu_writef(pdu, optional, "d", count); | ||
373 | if (!errcode && pdu_write(pdu, data, count)) | ||
374 | errcode = -EFAULT; | ||
375 | } | ||
376 | break; | ||
377 | case 'T':{ | ||
378 | int16_t nwname = va_arg(ap, int); | ||
379 | const char **wnames = va_arg(ap, const char **); | ||
380 | |||
381 | errcode = | ||
382 | p9pdu_writef(pdu, optional, "w", nwname); | ||
383 | if (!errcode) { | ||
384 | int i; | ||
385 | |||
386 | for (i = 0; i < nwname; i++) { | ||
387 | errcode = | ||
388 | p9pdu_writef(pdu, optional, | ||
389 | "s", | ||
390 | wnames[i]); | ||
391 | if (errcode) | ||
392 | break; | ||
393 | } | ||
394 | } | ||
395 | } | ||
396 | break; | ||
397 | case 'R':{ | ||
398 | int16_t nwqid = va_arg(ap, int); | ||
399 | struct p9_qid *wqids = | ||
400 | va_arg(ap, struct p9_qid *); | ||
401 | |||
402 | errcode = | ||
403 | p9pdu_writef(pdu, optional, "w", nwqid); | ||
404 | if (!errcode) { | ||
405 | int i; | ||
406 | |||
407 | for (i = 0; i < nwqid; i++) { | ||
408 | errcode = | ||
409 | p9pdu_writef(pdu, optional, | ||
410 | "Q", | ||
411 | &wqids[i]); | ||
412 | if (errcode) | ||
413 | break; | ||
414 | } | ||
415 | } | ||
416 | } | ||
417 | break; | ||
418 | case '?': | ||
419 | if (!optional) | ||
420 | return 0; | ||
421 | break; | ||
422 | default: | ||
423 | BUG(); | ||
424 | break; | ||
425 | } | ||
426 | |||
427 | if (errcode) | ||
428 | break; | ||
429 | } | ||
430 | |||
431 | return errcode; | ||
432 | } | ||
433 | |||
434 | int p9pdu_readf(struct p9_fcall *pdu, int optional, const char *fmt, ...) | ||
435 | { | ||
436 | va_list ap; | ||
437 | int ret; | ||
438 | |||
439 | va_start(ap, fmt); | ||
440 | ret = p9pdu_vreadf(pdu, optional, fmt, ap); | ||
441 | va_end(ap); | ||
442 | |||
443 | return ret; | ||
444 | } | ||
445 | |||
446 | static int | ||
447 | p9pdu_writef(struct p9_fcall *pdu, int optional, const char *fmt, ...) | ||
448 | { | ||
449 | va_list ap; | ||
450 | int ret; | ||
451 | |||
452 | va_start(ap, fmt); | ||
453 | ret = p9pdu_vwritef(pdu, optional, fmt, ap); | ||
454 | va_end(ap); | ||
455 | |||
456 | return ret; | ||
457 | } | ||
diff --git a/net/9p/protocol.h b/net/9p/protocol.h new file mode 100644 index 000000000000..596ee10d506f --- /dev/null +++ b/net/9p/protocol.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * net/9p/protocol.h | ||
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 | int | ||
29 | p9pdu_vwritef(struct p9_fcall *pdu, int optional, const char *fmt, va_list ap); | ||
30 | |||
31 | int p9pdu_readf(struct p9_fcall *pdu, int optional, const char *fmt, ...); | ||