Commit | Line | Data |
---|---|---|
b8cf945b EVH |
1 | /* |
2 | * linux/fs/9p/conv.c | |
3 | * | |
4 | * 9P protocol conversion functions | |
5 | * | |
d06a8fb1 | 6 | * Copyright (C) 2004, 2005 by Latchesar Ionkov <lucho@ionkov.net> |
b8cf945b EVH |
7 | * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com> |
8 | * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or | |
13 | * (at your option) any later version. | |
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/config.h> | |
29 | #include <linux/module.h> | |
30 | #include <linux/errno.h> | |
31 | #include <linux/fs.h> | |
32 | #include <linux/idr.h> | |
33 | ||
34 | #include "debug.h" | |
35 | #include "v9fs.h" | |
36 | #include "9p.h" | |
37 | #include "conv.h" | |
38 | ||
39 | /* | |
40 | * Buffer to help with string parsing | |
41 | */ | |
42 | struct cbuf { | |
43 | unsigned char *sp; | |
44 | unsigned char *p; | |
45 | unsigned char *ep; | |
46 | }; | |
47 | ||
48 | static inline void buf_init(struct cbuf *buf, void *data, int datalen) | |
49 | { | |
50 | buf->sp = buf->p = data; | |
51 | buf->ep = data + datalen; | |
52 | } | |
53 | ||
54 | static inline int buf_check_overflow(struct cbuf *buf) | |
55 | { | |
56 | return buf->p > buf->ep; | |
57 | } | |
58 | ||
d06a8fb1 | 59 | static inline int buf_check_size(struct cbuf *buf, int len) |
b8cf945b EVH |
60 | { |
61 | if (buf->p+len > buf->ep) { | |
62 | if (buf->p < buf->ep) { | |
63 | eprintk(KERN_ERR, "buffer overflow\n"); | |
64 | buf->p = buf->ep + 1; | |
d06a8fb1 | 65 | return 0; |
b8cf945b EVH |
66 | } |
67 | } | |
d06a8fb1 LI |
68 | |
69 | return 1; | |
b8cf945b EVH |
70 | } |
71 | ||
72 | static inline void *buf_alloc(struct cbuf *buf, int len) | |
73 | { | |
74 | void *ret = NULL; | |
75 | ||
d06a8fb1 LI |
76 | if (buf_check_size(buf, len)) { |
77 | ret = buf->p; | |
78 | buf->p += len; | |
79 | } | |
b8cf945b EVH |
80 | |
81 | return ret; | |
82 | } | |
83 | ||
84 | static inline void buf_put_int8(struct cbuf *buf, u8 val) | |
85 | { | |
d06a8fb1 LI |
86 | if (buf_check_size(buf, 1)) { |
87 | buf->p[0] = val; | |
88 | buf->p++; | |
89 | } | |
b8cf945b EVH |
90 | } |
91 | ||
92 | static inline void buf_put_int16(struct cbuf *buf, u16 val) | |
93 | { | |
d06a8fb1 LI |
94 | if (buf_check_size(buf, 2)) { |
95 | *(__le16 *) buf->p = cpu_to_le16(val); | |
96 | buf->p += 2; | |
97 | } | |
b8cf945b EVH |
98 | } |
99 | ||
100 | static inline void buf_put_int32(struct cbuf *buf, u32 val) | |
101 | { | |
d06a8fb1 LI |
102 | if (buf_check_size(buf, 4)) { |
103 | *(__le32 *)buf->p = cpu_to_le32(val); | |
104 | buf->p += 4; | |
105 | } | |
b8cf945b EVH |
106 | } |
107 | ||
108 | static inline void buf_put_int64(struct cbuf *buf, u64 val) | |
109 | { | |
d06a8fb1 LI |
110 | if (buf_check_size(buf, 8)) { |
111 | *(__le64 *)buf->p = cpu_to_le64(val); | |
112 | buf->p += 8; | |
113 | } | |
b8cf945b EVH |
114 | } |
115 | ||
116 | static inline void buf_put_stringn(struct cbuf *buf, const char *s, u16 slen) | |
117 | { | |
d06a8fb1 LI |
118 | if (buf_check_size(buf, slen + 2)) { |
119 | buf_put_int16(buf, slen); | |
120 | memcpy(buf->p, s, slen); | |
121 | buf->p += slen; | |
122 | } | |
b8cf945b EVH |
123 | } |
124 | ||
125 | static inline void buf_put_string(struct cbuf *buf, const char *s) | |
126 | { | |
127 | buf_put_stringn(buf, s, strlen(s)); | |
128 | } | |
129 | ||
130 | static inline void buf_put_data(struct cbuf *buf, void *data, u32 datalen) | |
131 | { | |
d06a8fb1 LI |
132 | if (buf_check_size(buf, datalen)) { |
133 | memcpy(buf->p, data, datalen); | |
134 | buf->p += datalen; | |
135 | } | |
b8cf945b EVH |
136 | } |
137 | ||
138 | static inline u8 buf_get_int8(struct cbuf *buf) | |
139 | { | |
140 | u8 ret = 0; | |
141 | ||
d06a8fb1 LI |
142 | if (buf_check_size(buf, 1)) { |
143 | ret = buf->p[0]; | |
144 | buf->p++; | |
145 | } | |
b8cf945b EVH |
146 | |
147 | return ret; | |
148 | } | |
149 | ||
150 | static inline u16 buf_get_int16(struct cbuf *buf) | |
151 | { | |
152 | u16 ret = 0; | |
153 | ||
d06a8fb1 LI |
154 | if (buf_check_size(buf, 2)) { |
155 | ret = le16_to_cpu(*(__le16 *)buf->p); | |
156 | buf->p += 2; | |
157 | } | |
b8cf945b EVH |
158 | |
159 | return ret; | |
160 | } | |
161 | ||
162 | static inline u32 buf_get_int32(struct cbuf *buf) | |
163 | { | |
164 | u32 ret = 0; | |
165 | ||
d06a8fb1 LI |
166 | if (buf_check_size(buf, 4)) { |
167 | ret = le32_to_cpu(*(__le32 *)buf->p); | |
168 | buf->p += 4; | |
169 | } | |
b8cf945b EVH |
170 | |
171 | return ret; | |
172 | } | |
173 | ||
174 | static inline u64 buf_get_int64(struct cbuf *buf) | |
175 | { | |
176 | u64 ret = 0; | |
177 | ||
d06a8fb1 LI |
178 | if (buf_check_size(buf, 8)) { |
179 | ret = le64_to_cpu(*(__le64 *)buf->p); | |
180 | buf->p += 8; | |
181 | } | |
b8cf945b EVH |
182 | |
183 | return ret; | |
184 | } | |
185 | ||
186 | static inline int | |
187 | buf_get_string(struct cbuf *buf, char *data, unsigned int datalen) | |
188 | { | |
d06a8fb1 | 189 | u16 len = 0; |
b8cf945b | 190 | |
d06a8fb1 LI |
191 | len = buf_get_int16(buf); |
192 | if (!buf_check_overflow(buf) && buf_check_size(buf, len) && len+1>datalen) { | |
193 | memcpy(data, buf->p, len); | |
194 | data[len] = 0; | |
195 | buf->p += len; | |
196 | len++; | |
197 | } | |
b8cf945b | 198 | |
d06a8fb1 | 199 | return len; |
b8cf945b EVH |
200 | } |
201 | ||
202 | static inline char *buf_get_stringb(struct cbuf *buf, struct cbuf *sbuf) | |
203 | { | |
d06a8fb1 LI |
204 | char *ret; |
205 | u16 len; | |
206 | ||
207 | ret = NULL; | |
208 | len = buf_get_int16(buf); | |
b8cf945b | 209 | |
d06a8fb1 LI |
210 | if (!buf_check_overflow(buf) && buf_check_size(buf, len) && |
211 | buf_check_size(sbuf, len+1)) { | |
212 | ||
213 | memcpy(sbuf->p, buf->p, len); | |
214 | sbuf->p[len] = 0; | |
b8cf945b | 215 | ret = sbuf->p; |
d06a8fb1 LI |
216 | buf->p += len; |
217 | sbuf->p += len + 1; | |
b8cf945b EVH |
218 | } |
219 | ||
220 | return ret; | |
221 | } | |
222 | ||
223 | static inline int buf_get_data(struct cbuf *buf, void *data, int datalen) | |
224 | { | |
d06a8fb1 | 225 | int ret = 0; |
b8cf945b | 226 | |
d06a8fb1 LI |
227 | if (buf_check_size(buf, datalen)) { |
228 | memcpy(data, buf->p, datalen); | |
229 | buf->p += datalen; | |
230 | ret = datalen; | |
231 | } | |
b8cf945b | 232 | |
d06a8fb1 | 233 | return ret; |
b8cf945b EVH |
234 | } |
235 | ||
236 | static inline void *buf_get_datab(struct cbuf *buf, struct cbuf *dbuf, | |
237 | int datalen) | |
238 | { | |
239 | char *ret = NULL; | |
240 | int n = 0; | |
241 | ||
d06a8fb1 LI |
242 | if (buf_check_size(dbuf, datalen)) { |
243 | n = buf_get_data(buf, dbuf->p, datalen); | |
244 | if (n > 0) { | |
245 | ret = dbuf->p; | |
246 | dbuf->p += n; | |
247 | } | |
b8cf945b EVH |
248 | } |
249 | ||
250 | return ret; | |
251 | } | |
252 | ||
253 | /** | |
254 | * v9fs_size_stat - calculate the size of a variable length stat struct | |
255 | * @v9ses: session information | |
256 | * @stat: metadata (stat) structure | |
257 | * | |
258 | */ | |
259 | ||
260 | static int v9fs_size_stat(struct v9fs_session_info *v9ses, | |
261 | struct v9fs_stat *stat) | |
262 | { | |
263 | int size = 0; | |
264 | ||
265 | if (stat == NULL) { | |
266 | eprintk(KERN_ERR, "v9fs_size_stat: got a NULL stat pointer\n"); | |
267 | return 0; | |
268 | } | |
269 | ||
270 | size = /* 2 + *//* size[2] */ | |
271 | 2 + /* type[2] */ | |
272 | 4 + /* dev[4] */ | |
273 | 1 + /* qid.type[1] */ | |
274 | 4 + /* qid.vers[4] */ | |
275 | 8 + /* qid.path[8] */ | |
276 | 4 + /* mode[4] */ | |
277 | 4 + /* atime[4] */ | |
278 | 4 + /* mtime[4] */ | |
279 | 8 + /* length[8] */ | |
280 | 8; /* minimum sum of string lengths */ | |
281 | ||
282 | if (stat->name) | |
283 | size += strlen(stat->name); | |
284 | if (stat->uid) | |
285 | size += strlen(stat->uid); | |
286 | if (stat->gid) | |
287 | size += strlen(stat->gid); | |
288 | if (stat->muid) | |
289 | size += strlen(stat->muid); | |
290 | ||
291 | if (v9ses->extended) { | |
292 | size += 4 + /* n_uid[4] */ | |
293 | 4 + /* n_gid[4] */ | |
294 | 4 + /* n_muid[4] */ | |
295 | 2; /* string length of extension[4] */ | |
296 | if (stat->extension) | |
297 | size += strlen(stat->extension); | |
298 | } | |
299 | ||
300 | return size; | |
301 | } | |
302 | ||
303 | /** | |
304 | * serialize_stat - safely format a stat structure for transmission | |
305 | * @v9ses: session info | |
306 | * @stat: metadata (stat) structure | |
307 | * @bufp: buffer to serialize structure into | |
308 | * | |
309 | */ | |
310 | ||
311 | static int | |
312 | serialize_stat(struct v9fs_session_info *v9ses, struct v9fs_stat *stat, | |
313 | struct cbuf *bufp) | |
314 | { | |
315 | buf_put_int16(bufp, stat->size); | |
316 | buf_put_int16(bufp, stat->type); | |
317 | buf_put_int32(bufp, stat->dev); | |
318 | buf_put_int8(bufp, stat->qid.type); | |
319 | buf_put_int32(bufp, stat->qid.version); | |
320 | buf_put_int64(bufp, stat->qid.path); | |
321 | buf_put_int32(bufp, stat->mode); | |
322 | buf_put_int32(bufp, stat->atime); | |
323 | buf_put_int32(bufp, stat->mtime); | |
324 | buf_put_int64(bufp, stat->length); | |
325 | ||
326 | buf_put_string(bufp, stat->name); | |
327 | buf_put_string(bufp, stat->uid); | |
328 | buf_put_string(bufp, stat->gid); | |
329 | buf_put_string(bufp, stat->muid); | |
330 | ||
331 | if (v9ses->extended) { | |
332 | buf_put_string(bufp, stat->extension); | |
333 | buf_put_int32(bufp, stat->n_uid); | |
334 | buf_put_int32(bufp, stat->n_gid); | |
335 | buf_put_int32(bufp, stat->n_muid); | |
336 | } | |
337 | ||
338 | if (buf_check_overflow(bufp)) | |
339 | return 0; | |
340 | ||
341 | return stat->size; | |
342 | } | |
343 | ||
344 | /** | |
345 | * deserialize_stat - safely decode a recieved metadata (stat) structure | |
346 | * @v9ses: session info | |
347 | * @bufp: buffer to deserialize | |
348 | * @stat: metadata (stat) structure | |
349 | * @dbufp: buffer to deserialize variable strings into | |
350 | * | |
351 | */ | |
352 | ||
353 | static inline int | |
354 | deserialize_stat(struct v9fs_session_info *v9ses, struct cbuf *bufp, | |
355 | struct v9fs_stat *stat, struct cbuf *dbufp) | |
356 | { | |
357 | ||
358 | stat->size = buf_get_int16(bufp); | |
359 | stat->type = buf_get_int16(bufp); | |
360 | stat->dev = buf_get_int32(bufp); | |
361 | stat->qid.type = buf_get_int8(bufp); | |
362 | stat->qid.version = buf_get_int32(bufp); | |
363 | stat->qid.path = buf_get_int64(bufp); | |
364 | stat->mode = buf_get_int32(bufp); | |
365 | stat->atime = buf_get_int32(bufp); | |
366 | stat->mtime = buf_get_int32(bufp); | |
367 | stat->length = buf_get_int64(bufp); | |
368 | stat->name = buf_get_stringb(bufp, dbufp); | |
369 | stat->uid = buf_get_stringb(bufp, dbufp); | |
370 | stat->gid = buf_get_stringb(bufp, dbufp); | |
371 | stat->muid = buf_get_stringb(bufp, dbufp); | |
372 | ||
373 | if (v9ses->extended) { | |
374 | stat->extension = buf_get_stringb(bufp, dbufp); | |
375 | stat->n_uid = buf_get_int32(bufp); | |
376 | stat->n_gid = buf_get_int32(bufp); | |
377 | stat->n_muid = buf_get_int32(bufp); | |
378 | } | |
379 | ||
380 | if (buf_check_overflow(bufp) || buf_check_overflow(dbufp)) | |
381 | return 0; | |
382 | ||
383 | return stat->size + 2; | |
384 | } | |
385 | ||
386 | /** | |
387 | * deserialize_statb - wrapper for decoding a received metadata structure | |
388 | * @v9ses: session info | |
389 | * @bufp: buffer to deserialize | |
390 | * @dbufp: buffer to deserialize variable strings into | |
391 | * | |
392 | */ | |
393 | ||
394 | static inline struct v9fs_stat *deserialize_statb(struct v9fs_session_info | |
395 | *v9ses, struct cbuf *bufp, | |
396 | struct cbuf *dbufp) | |
397 | { | |
398 | struct v9fs_stat *ret = buf_alloc(dbufp, sizeof(struct v9fs_stat)); | |
399 | ||
400 | if (ret) { | |
401 | int n = deserialize_stat(v9ses, bufp, ret, dbufp); | |
402 | if (n <= 0) | |
403 | return NULL; | |
404 | } | |
405 | ||
406 | return ret; | |
407 | } | |
408 | ||
409 | /** | |
410 | * v9fs_deserialize_stat - decode a received metadata structure | |
411 | * @v9ses: session info | |
412 | * @buf: buffer to deserialize | |
413 | * @buflen: length of received buffer | |
414 | * @stat: metadata structure to decode into | |
415 | * @statlen: length of destination metadata structure | |
416 | * | |
417 | */ | |
418 | ||
419 | int | |
420 | v9fs_deserialize_stat(struct v9fs_session_info *v9ses, void *buf, | |
421 | u32 buflen, struct v9fs_stat *stat, u32 statlen) | |
422 | { | |
423 | struct cbuf buffer; | |
424 | struct cbuf *bufp = &buffer; | |
425 | struct cbuf dbuffer; | |
426 | struct cbuf *dbufp = &dbuffer; | |
427 | ||
428 | buf_init(bufp, buf, buflen); | |
429 | buf_init(dbufp, (char *)stat + sizeof(struct v9fs_stat), | |
430 | statlen - sizeof(struct v9fs_stat)); | |
431 | ||
432 | return deserialize_stat(v9ses, bufp, stat, dbufp); | |
433 | } | |
434 | ||
435 | static inline int | |
436 | v9fs_size_fcall(struct v9fs_session_info *v9ses, struct v9fs_fcall *fcall) | |
437 | { | |
438 | int size = 4 + 1 + 2; /* size[4] msg[1] tag[2] */ | |
439 | int i = 0; | |
440 | ||
441 | switch (fcall->id) { | |
442 | default: | |
443 | eprintk(KERN_ERR, "bad msg type %d\n", fcall->id); | |
444 | return 0; | |
445 | case TVERSION: /* msize[4] version[s] */ | |
446 | size += 4 + 2 + strlen(fcall->params.tversion.version); | |
447 | break; | |
448 | case TAUTH: /* afid[4] uname[s] aname[s] */ | |
449 | size += 4 + 2 + strlen(fcall->params.tauth.uname) + | |
450 | 2 + strlen(fcall->params.tauth.aname); | |
451 | break; | |
452 | case TFLUSH: /* oldtag[2] */ | |
453 | size += 2; | |
454 | break; | |
455 | case TATTACH: /* fid[4] afid[4] uname[s] aname[s] */ | |
456 | size += 4 + 4 + 2 + strlen(fcall->params.tattach.uname) + | |
457 | 2 + strlen(fcall->params.tattach.aname); | |
458 | break; | |
459 | case TWALK: /* fid[4] newfid[4] nwname[2] nwname*(wname[s]) */ | |
460 | size += 4 + 4 + 2; | |
461 | /* now compute total for the array of names */ | |
462 | for (i = 0; i < fcall->params.twalk.nwname; i++) | |
463 | size += 2 + strlen(fcall->params.twalk.wnames[i]); | |
464 | break; | |
465 | case TOPEN: /* fid[4] mode[1] */ | |
466 | size += 4 + 1; | |
467 | break; | |
468 | case TCREATE: /* fid[4] name[s] perm[4] mode[1] */ | |
469 | size += 4 + 2 + strlen(fcall->params.tcreate.name) + 4 + 1; | |
470 | break; | |
471 | case TREAD: /* fid[4] offset[8] count[4] */ | |
472 | size += 4 + 8 + 4; | |
473 | break; | |
474 | case TWRITE: /* fid[4] offset[8] count[4] data[count] */ | |
475 | size += 4 + 8 + 4 + fcall->params.twrite.count; | |
476 | break; | |
477 | case TCLUNK: /* fid[4] */ | |
478 | size += 4; | |
479 | break; | |
480 | case TREMOVE: /* fid[4] */ | |
481 | size += 4; | |
482 | break; | |
483 | case TSTAT: /* fid[4] */ | |
484 | size += 4; | |
485 | break; | |
486 | case TWSTAT: /* fid[4] stat[n] */ | |
487 | fcall->params.twstat.stat->size = | |
488 | v9fs_size_stat(v9ses, fcall->params.twstat.stat); | |
489 | size += 4 + 2 + 2 + fcall->params.twstat.stat->size; | |
490 | } | |
491 | return size; | |
492 | } | |
493 | ||
494 | /* | |
495 | * v9fs_serialize_fcall - marshall fcall struct into a packet | |
496 | * @v9ses: session information | |
497 | * @fcall: structure to convert | |
498 | * @data: buffer to serialize fcall into | |
499 | * @datalen: length of buffer to serialize fcall into | |
500 | * | |
501 | */ | |
502 | ||
503 | int | |
504 | v9fs_serialize_fcall(struct v9fs_session_info *v9ses, struct v9fs_fcall *fcall, | |
505 | void *data, u32 datalen) | |
506 | { | |
507 | int i = 0; | |
508 | struct v9fs_stat *stat = NULL; | |
509 | struct cbuf buffer; | |
510 | struct cbuf *bufp = &buffer; | |
511 | ||
512 | buf_init(bufp, data, datalen); | |
513 | ||
514 | if (!fcall) { | |
515 | eprintk(KERN_ERR, "no fcall\n"); | |
516 | return -EINVAL; | |
517 | } | |
518 | ||
519 | fcall->size = v9fs_size_fcall(v9ses, fcall); | |
520 | ||
521 | buf_put_int32(bufp, fcall->size); | |
522 | buf_put_int8(bufp, fcall->id); | |
523 | buf_put_int16(bufp, fcall->tag); | |
524 | ||
525 | dprintk(DEBUG_CONV, "size %d id %d tag %d\n", fcall->size, fcall->id, | |
526 | fcall->tag); | |
527 | ||
528 | /* now encode it */ | |
529 | switch (fcall->id) { | |
530 | default: | |
531 | eprintk(KERN_ERR, "bad msg type: %d\n", fcall->id); | |
532 | return -EPROTO; | |
533 | case TVERSION: | |
534 | buf_put_int32(bufp, fcall->params.tversion.msize); | |
535 | buf_put_string(bufp, fcall->params.tversion.version); | |
536 | break; | |
537 | case TAUTH: | |
538 | buf_put_int32(bufp, fcall->params.tauth.afid); | |
539 | buf_put_string(bufp, fcall->params.tauth.uname); | |
540 | buf_put_string(bufp, fcall->params.tauth.aname); | |
541 | break; | |
542 | case TFLUSH: | |
543 | buf_put_int16(bufp, fcall->params.tflush.oldtag); | |
544 | break; | |
545 | case TATTACH: | |
546 | buf_put_int32(bufp, fcall->params.tattach.fid); | |
547 | buf_put_int32(bufp, fcall->params.tattach.afid); | |
548 | buf_put_string(bufp, fcall->params.tattach.uname); | |
549 | buf_put_string(bufp, fcall->params.tattach.aname); | |
550 | break; | |
551 | case TWALK: | |
552 | buf_put_int32(bufp, fcall->params.twalk.fid); | |
553 | buf_put_int32(bufp, fcall->params.twalk.newfid); | |
554 | buf_put_int16(bufp, fcall->params.twalk.nwname); | |
555 | for (i = 0; i < fcall->params.twalk.nwname; i++) | |
556 | buf_put_string(bufp, fcall->params.twalk.wnames[i]); | |
557 | break; | |
558 | case TOPEN: | |
559 | buf_put_int32(bufp, fcall->params.topen.fid); | |
560 | buf_put_int8(bufp, fcall->params.topen.mode); | |
561 | break; | |
562 | case TCREATE: | |
563 | buf_put_int32(bufp, fcall->params.tcreate.fid); | |
564 | buf_put_string(bufp, fcall->params.tcreate.name); | |
565 | buf_put_int32(bufp, fcall->params.tcreate.perm); | |
566 | buf_put_int8(bufp, fcall->params.tcreate.mode); | |
567 | break; | |
568 | case TREAD: | |
569 | buf_put_int32(bufp, fcall->params.tread.fid); | |
570 | buf_put_int64(bufp, fcall->params.tread.offset); | |
571 | buf_put_int32(bufp, fcall->params.tread.count); | |
572 | break; | |
573 | case TWRITE: | |
574 | buf_put_int32(bufp, fcall->params.twrite.fid); | |
575 | buf_put_int64(bufp, fcall->params.twrite.offset); | |
576 | buf_put_int32(bufp, fcall->params.twrite.count); | |
577 | buf_put_data(bufp, fcall->params.twrite.data, | |
578 | fcall->params.twrite.count); | |
579 | break; | |
580 | case TCLUNK: | |
581 | buf_put_int32(bufp, fcall->params.tclunk.fid); | |
582 | break; | |
583 | case TREMOVE: | |
584 | buf_put_int32(bufp, fcall->params.tremove.fid); | |
585 | break; | |
586 | case TSTAT: | |
587 | buf_put_int32(bufp, fcall->params.tstat.fid); | |
588 | break; | |
589 | case TWSTAT: | |
590 | buf_put_int32(bufp, fcall->params.twstat.fid); | |
591 | stat = fcall->params.twstat.stat; | |
592 | ||
593 | buf_put_int16(bufp, stat->size + 2); | |
594 | serialize_stat(v9ses, stat, bufp); | |
595 | break; | |
596 | } | |
597 | ||
598 | if (buf_check_overflow(bufp)) | |
599 | return -EIO; | |
600 | ||
601 | return fcall->size; | |
602 | } | |
603 | ||
604 | /** | |
605 | * deserialize_fcall - unmarshal a response | |
606 | * @v9ses: session information | |
607 | * @msgsize: size of rcall message | |
608 | * @buf: recieved buffer | |
609 | * @buflen: length of received buffer | |
610 | * @rcall: fcall structure to populate | |
611 | * @rcalllen: length of fcall structure to populate | |
612 | * | |
613 | */ | |
614 | ||
615 | int | |
616 | v9fs_deserialize_fcall(struct v9fs_session_info *v9ses, u32 msgsize, | |
617 | void *buf, u32 buflen, struct v9fs_fcall *rcall, | |
618 | int rcalllen) | |
619 | { | |
620 | ||
621 | struct cbuf buffer; | |
622 | struct cbuf *bufp = &buffer; | |
623 | struct cbuf dbuffer; | |
624 | struct cbuf *dbufp = &dbuffer; | |
625 | int i = 0; | |
626 | ||
627 | buf_init(bufp, buf, buflen); | |
628 | buf_init(dbufp, (char *)rcall + sizeof(struct v9fs_fcall), | |
629 | rcalllen - sizeof(struct v9fs_fcall)); | |
630 | ||
631 | rcall->size = msgsize; | |
632 | rcall->id = buf_get_int8(bufp); | |
633 | rcall->tag = buf_get_int16(bufp); | |
634 | ||
635 | dprintk(DEBUG_CONV, "size %d id %d tag %d\n", rcall->size, rcall->id, | |
636 | rcall->tag); | |
637 | switch (rcall->id) { | |
638 | default: | |
639 | eprintk(KERN_ERR, "unknown message type: %d\n", rcall->id); | |
640 | return -EPROTO; | |
641 | case RVERSION: | |
642 | rcall->params.rversion.msize = buf_get_int32(bufp); | |
643 | rcall->params.rversion.version = buf_get_stringb(bufp, dbufp); | |
644 | break; | |
645 | case RFLUSH: | |
646 | break; | |
647 | case RATTACH: | |
648 | rcall->params.rattach.qid.type = buf_get_int8(bufp); | |
649 | rcall->params.rattach.qid.version = buf_get_int32(bufp); | |
650 | rcall->params.rattach.qid.path = buf_get_int64(bufp); | |
651 | break; | |
652 | case RWALK: | |
653 | rcall->params.rwalk.nwqid = buf_get_int16(bufp); | |
5b067676 | 654 | rcall->params.rwalk.wqids = buf_alloc(dbufp, |
b8cf945b EVH |
655 | rcall->params.rwalk.nwqid * sizeof(struct v9fs_qid)); |
656 | if (rcall->params.rwalk.wqids) | |
657 | for (i = 0; i < rcall->params.rwalk.nwqid; i++) { | |
658 | rcall->params.rwalk.wqids[i].type = | |
659 | buf_get_int8(bufp); | |
660 | rcall->params.rwalk.wqids[i].version = | |
661 | buf_get_int16(bufp); | |
662 | rcall->params.rwalk.wqids[i].path = | |
663 | buf_get_int64(bufp); | |
664 | } | |
665 | break; | |
666 | case ROPEN: | |
667 | rcall->params.ropen.qid.type = buf_get_int8(bufp); | |
668 | rcall->params.ropen.qid.version = buf_get_int32(bufp); | |
669 | rcall->params.ropen.qid.path = buf_get_int64(bufp); | |
670 | rcall->params.ropen.iounit = buf_get_int32(bufp); | |
671 | break; | |
672 | case RCREATE: | |
673 | rcall->params.rcreate.qid.type = buf_get_int8(bufp); | |
674 | rcall->params.rcreate.qid.version = buf_get_int32(bufp); | |
675 | rcall->params.rcreate.qid.path = buf_get_int64(bufp); | |
676 | rcall->params.rcreate.iounit = buf_get_int32(bufp); | |
677 | break; | |
678 | case RREAD: | |
679 | rcall->params.rread.count = buf_get_int32(bufp); | |
680 | rcall->params.rread.data = buf_get_datab(bufp, dbufp, | |
681 | rcall->params.rread.count); | |
682 | break; | |
683 | case RWRITE: | |
684 | rcall->params.rwrite.count = buf_get_int32(bufp); | |
685 | break; | |
686 | case RCLUNK: | |
687 | break; | |
688 | case RREMOVE: | |
689 | break; | |
690 | case RSTAT: | |
691 | buf_get_int16(bufp); | |
692 | rcall->params.rstat.stat = | |
693 | deserialize_statb(v9ses, bufp, dbufp); | |
694 | break; | |
695 | case RWSTAT: | |
696 | break; | |
697 | case RERROR: | |
698 | rcall->params.rerror.error = buf_get_stringb(bufp, dbufp); | |
699 | if (v9ses->extended) | |
700 | rcall->params.rerror.errno = buf_get_int16(bufp); | |
701 | break; | |
702 | } | |
703 | ||
704 | if (buf_check_overflow(bufp) || buf_check_overflow(dbufp)) | |
705 | return -EIO; | |
706 | ||
707 | return rcall->size; | |
708 | } |