]>
Commit | Line | Data |
---|---|---|
b8cf945b | 1 | /* |
bd238fb4 | 2 | * net/9p/conv.c |
b8cf945b EVH |
3 | * |
4 | * 9P protocol conversion functions | |
5 | * | |
d06a8fb1 | 6 | * Copyright (C) 2004, 2005 by Latchesar Ionkov <[email protected]> |
b8cf945b EVH |
7 | * Copyright (C) 2004 by Eric Van Hensbergen <[email protected]> |
8 | * Copyright (C) 2002 by Ron Minnich <[email protected]> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
42e8c509 EVH |
11 | * it under the terms of the GNU General Public License version 2 |
12 | * as published by the Free Software Foundation. | |
b8cf945b EVH |
13 | * |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program; if not, write to: | |
21 | * Free Software Foundation | |
22 | * 51 Franklin Street, Fifth Floor | |
23 | * Boston, MA 02111-1301 USA | |
24 | * | |
25 | */ | |
26 | ||
b8cf945b EVH |
27 | #include <linux/module.h> |
28 | #include <linux/errno.h> | |
29 | #include <linux/fs.h> | |
914e2637 | 30 | #include <linux/sched.h> |
b8cf945b | 31 | #include <linux/idr.h> |
bd238fb4 LI |
32 | #include <linux/uaccess.h> |
33 | #include <net/9p/9p.h> | |
b8cf945b EVH |
34 | |
35 | /* | |
36 | * Buffer to help with string parsing | |
37 | */ | |
38 | struct cbuf { | |
39 | unsigned char *sp; | |
40 | unsigned char *p; | |
41 | unsigned char *ep; | |
42 | }; | |
43 | ||
44 | static inline void buf_init(struct cbuf *buf, void *data, int datalen) | |
45 | { | |
46 | buf->sp = buf->p = data; | |
47 | buf->ep = data + datalen; | |
48 | } | |
49 | ||
50 | static inline int buf_check_overflow(struct cbuf *buf) | |
51 | { | |
52 | return buf->p > buf->ep; | |
53 | } | |
54 | ||
858119e1 | 55 | static int buf_check_size(struct cbuf *buf, int len) |
b8cf945b | 56 | { |
1dac06b2 LI |
57 | if (buf->p + len > buf->ep) { |
58 | if (buf->p < buf->ep) { | |
bd238fb4 LI |
59 | P9_EPRINTK(KERN_ERR, |
60 | "buffer overflow: want %d has %d\n", len, | |
61 | (int)(buf->ep - buf->p)); | |
1dac06b2 LI |
62 | dump_stack(); |
63 | buf->p = buf->ep + 1; | |
64 | } | |
65 | ||
531b1094 | 66 | return 0; |
b8cf945b | 67 | } |
d06a8fb1 LI |
68 | |
69 | return 1; | |
b8cf945b EVH |
70 | } |
71 | ||
858119e1 | 72 | static void *buf_alloc(struct cbuf *buf, int len) |
b8cf945b EVH |
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 | ||
858119e1 | 84 | static void buf_put_int8(struct cbuf *buf, u8 val) |
b8cf945b | 85 | { |
d06a8fb1 LI |
86 | if (buf_check_size(buf, 1)) { |
87 | buf->p[0] = val; | |
88 | buf->p++; | |
89 | } | |
b8cf945b EVH |
90 | } |
91 | ||
858119e1 | 92 | static void buf_put_int16(struct cbuf *buf, u16 val) |
b8cf945b | 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 | ||
858119e1 | 100 | static void buf_put_int32(struct cbuf *buf, u32 val) |
b8cf945b | 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 | ||
858119e1 | 108 | static void buf_put_int64(struct cbuf *buf, u64 val) |
b8cf945b | 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 | ||
05818a00 | 116 | static char *buf_put_stringn(struct cbuf *buf, const char *s, u16 slen) |
b8cf945b | 117 | { |
05818a00 LI |
118 | char *ret; |
119 | ||
120 | ret = NULL; | |
d06a8fb1 LI |
121 | if (buf_check_size(buf, slen + 2)) { |
122 | buf_put_int16(buf, slen); | |
05818a00 | 123 | ret = buf->p; |
d06a8fb1 LI |
124 | memcpy(buf->p, s, slen); |
125 | buf->p += slen; | |
126 | } | |
05818a00 LI |
127 | |
128 | return ret; | |
b8cf945b EVH |
129 | } |
130 | ||
858119e1 | 131 | static u8 buf_get_int8(struct cbuf *buf) |
b8cf945b EVH |
132 | { |
133 | u8 ret = 0; | |
134 | ||
d06a8fb1 LI |
135 | if (buf_check_size(buf, 1)) { |
136 | ret = buf->p[0]; | |
137 | buf->p++; | |
138 | } | |
b8cf945b EVH |
139 | |
140 | return ret; | |
141 | } | |
142 | ||
858119e1 | 143 | static u16 buf_get_int16(struct cbuf *buf) |
b8cf945b EVH |
144 | { |
145 | u16 ret = 0; | |
146 | ||
d06a8fb1 LI |
147 | if (buf_check_size(buf, 2)) { |
148 | ret = le16_to_cpu(*(__le16 *)buf->p); | |
149 | buf->p += 2; | |
150 | } | |
b8cf945b EVH |
151 | |
152 | return ret; | |
153 | } | |
154 | ||
858119e1 | 155 | static u32 buf_get_int32(struct cbuf *buf) |
b8cf945b EVH |
156 | { |
157 | u32 ret = 0; | |
158 | ||
d06a8fb1 LI |
159 | if (buf_check_size(buf, 4)) { |
160 | ret = le32_to_cpu(*(__le32 *)buf->p); | |
161 | buf->p += 4; | |
162 | } | |
b8cf945b EVH |
163 | |
164 | return ret; | |
165 | } | |
166 | ||
858119e1 | 167 | static u64 buf_get_int64(struct cbuf *buf) |
b8cf945b EVH |
168 | { |
169 | u64 ret = 0; | |
170 | ||
d06a8fb1 LI |
171 | if (buf_check_size(buf, 8)) { |
172 | ret = le64_to_cpu(*(__le64 *)buf->p); | |
173 | buf->p += 8; | |
174 | } | |
b8cf945b EVH |
175 | |
176 | return ret; | |
177 | } | |
178 | ||
bd238fb4 | 179 | static void buf_get_str(struct cbuf *buf, struct p9_str *vstr) |
b8cf945b | 180 | { |
531b1094 LI |
181 | vstr->len = buf_get_int16(buf); |
182 | if (!buf_check_overflow(buf) && buf_check_size(buf, vstr->len)) { | |
183 | vstr->str = buf->p; | |
184 | buf->p += vstr->len; | |
185 | } else { | |
186 | vstr->len = 0; | |
187 | vstr->str = NULL; | |
d06a8fb1 | 188 | } |
b8cf945b EVH |
189 | } |
190 | ||
bd238fb4 | 191 | static void buf_get_qid(struct cbuf *bufp, struct p9_qid *qid) |
b8cf945b | 192 | { |
531b1094 LI |
193 | qid->type = buf_get_int8(bufp); |
194 | qid->version = buf_get_int32(bufp); | |
195 | qid->path = buf_get_int64(bufp); | |
b8cf945b EVH |
196 | } |
197 | ||
198 | /** | |
bd238fb4 | 199 | * p9_size_wstat - calculate the size of a variable length stat struct |
ee443996 | 200 | * @wstat: metadata (stat) structure |
bd238fb4 | 201 | * @dotu: non-zero if 9P2000.u |
b8cf945b EVH |
202 | * |
203 | */ | |
204 | ||
bd238fb4 | 205 | static int p9_size_wstat(struct p9_wstat *wstat, int dotu) |
b8cf945b EVH |
206 | { |
207 | int size = 0; | |
208 | ||
531b1094 | 209 | if (wstat == NULL) { |
bd238fb4 | 210 | P9_EPRINTK(KERN_ERR, "p9_size_stat: got a NULL stat pointer\n"); |
b8cf945b EVH |
211 | return 0; |
212 | } | |
213 | ||
214 | size = /* 2 + *//* size[2] */ | |
215 | 2 + /* type[2] */ | |
216 | 4 + /* dev[4] */ | |
217 | 1 + /* qid.type[1] */ | |
218 | 4 + /* qid.vers[4] */ | |
219 | 8 + /* qid.path[8] */ | |
220 | 4 + /* mode[4] */ | |
221 | 4 + /* atime[4] */ | |
222 | 4 + /* mtime[4] */ | |
223 | 8 + /* length[8] */ | |
224 | 8; /* minimum sum of string lengths */ | |
225 | ||
531b1094 LI |
226 | if (wstat->name) |
227 | size += strlen(wstat->name); | |
228 | if (wstat->uid) | |
229 | size += strlen(wstat->uid); | |
230 | if (wstat->gid) | |
231 | size += strlen(wstat->gid); | |
232 | if (wstat->muid) | |
233 | size += strlen(wstat->muid); | |
b8cf945b | 234 | |
bd238fb4 | 235 | if (dotu) { |
b8cf945b EVH |
236 | size += 4 + /* n_uid[4] */ |
237 | 4 + /* n_gid[4] */ | |
238 | 4 + /* n_muid[4] */ | |
239 | 2; /* string length of extension[4] */ | |
531b1094 LI |
240 | if (wstat->extension) |
241 | size += strlen(wstat->extension); | |
b8cf945b EVH |
242 | } |
243 | ||
244 | return size; | |
245 | } | |
246 | ||
247 | /** | |
531b1094 | 248 | * buf_get_stat - safely decode a recieved metadata (stat) structure |
b8cf945b EVH |
249 | * @bufp: buffer to deserialize |
250 | * @stat: metadata (stat) structure | |
bd238fb4 | 251 | * @dotu: non-zero if 9P2000.u |
b8cf945b EVH |
252 | * |
253 | */ | |
254 | ||
858119e1 | 255 | static void |
bd238fb4 | 256 | buf_get_stat(struct cbuf *bufp, struct p9_stat *stat, int dotu) |
b8cf945b | 257 | { |
b8cf945b EVH |
258 | stat->size = buf_get_int16(bufp); |
259 | stat->type = buf_get_int16(bufp); | |
260 | stat->dev = buf_get_int32(bufp); | |
261 | stat->qid.type = buf_get_int8(bufp); | |
262 | stat->qid.version = buf_get_int32(bufp); | |
263 | stat->qid.path = buf_get_int64(bufp); | |
264 | stat->mode = buf_get_int32(bufp); | |
265 | stat->atime = buf_get_int32(bufp); | |
266 | stat->mtime = buf_get_int32(bufp); | |
267 | stat->length = buf_get_int64(bufp); | |
531b1094 LI |
268 | buf_get_str(bufp, &stat->name); |
269 | buf_get_str(bufp, &stat->uid); | |
270 | buf_get_str(bufp, &stat->gid); | |
271 | buf_get_str(bufp, &stat->muid); | |
b8cf945b | 272 | |
bd238fb4 | 273 | if (dotu) { |
531b1094 | 274 | buf_get_str(bufp, &stat->extension); |
b8cf945b EVH |
275 | stat->n_uid = buf_get_int32(bufp); |
276 | stat->n_gid = buf_get_int32(bufp); | |
277 | stat->n_muid = buf_get_int32(bufp); | |
278 | } | |
b8cf945b EVH |
279 | } |
280 | ||
281 | /** | |
bd238fb4 | 282 | * p9_deserialize_stat - decode a received metadata structure |
b8cf945b EVH |
283 | * @buf: buffer to deserialize |
284 | * @buflen: length of received buffer | |
285 | * @stat: metadata structure to decode into | |
bd238fb4 | 286 | * @dotu: non-zero if 9P2000.u |
b8cf945b | 287 | * |
531b1094 | 288 | * Note: stat will point to the buf region. |
b8cf945b EVH |
289 | */ |
290 | ||
531b1094 | 291 | int |
bd238fb4 LI |
292 | p9_deserialize_stat(void *buf, u32 buflen, struct p9_stat *stat, |
293 | int dotu) | |
b8cf945b EVH |
294 | { |
295 | struct cbuf buffer; | |
296 | struct cbuf *bufp = &buffer; | |
531b1094 | 297 | unsigned char *p; |
b8cf945b EVH |
298 | |
299 | buf_init(bufp, buf, buflen); | |
531b1094 | 300 | p = bufp->p; |
bd238fb4 | 301 | buf_get_stat(bufp, stat, dotu); |
b8cf945b | 302 | |
531b1094 | 303 | if (buf_check_overflow(bufp)) |
b8cf945b | 304 | return 0; |
531b1094 LI |
305 | else |
306 | return bufp->p - p; | |
b8cf945b | 307 | } |
bd238fb4 | 308 | EXPORT_SYMBOL(p9_deserialize_stat); |
b8cf945b EVH |
309 | |
310 | /** | |
311 | * deserialize_fcall - unmarshal a response | |
b8cf945b EVH |
312 | * @buf: recieved buffer |
313 | * @buflen: length of received buffer | |
314 | * @rcall: fcall structure to populate | |
315 | * @rcalllen: length of fcall structure to populate | |
bd238fb4 | 316 | * @dotu: non-zero if 9P2000.u |
b8cf945b EVH |
317 | * |
318 | */ | |
319 | ||
320 | int | |
bd238fb4 LI |
321 | p9_deserialize_fcall(void *buf, u32 buflen, struct p9_fcall *rcall, |
322 | int dotu) | |
b8cf945b EVH |
323 | { |
324 | ||
325 | struct cbuf buffer; | |
326 | struct cbuf *bufp = &buffer; | |
b8cf945b EVH |
327 | int i = 0; |
328 | ||
329 | buf_init(bufp, buf, buflen); | |
b8cf945b | 330 | |
3cf6429a | 331 | rcall->size = buf_get_int32(bufp); |
b8cf945b EVH |
332 | rcall->id = buf_get_int8(bufp); |
333 | rcall->tag = buf_get_int16(bufp); | |
334 | ||
bd238fb4 LI |
335 | P9_DPRINTK(P9_DEBUG_CONV, "size %d id %d tag %d\n", rcall->size, |
336 | rcall->id, rcall->tag); | |
531b1094 | 337 | |
b8cf945b EVH |
338 | switch (rcall->id) { |
339 | default: | |
bd238fb4 | 340 | P9_EPRINTK(KERN_ERR, "unknown message type: %d\n", rcall->id); |
b8cf945b | 341 | return -EPROTO; |
bd238fb4 | 342 | case P9_RVERSION: |
b8cf945b | 343 | rcall->params.rversion.msize = buf_get_int32(bufp); |
531b1094 | 344 | buf_get_str(bufp, &rcall->params.rversion.version); |
b8cf945b | 345 | break; |
bd238fb4 | 346 | case P9_RFLUSH: |
b8cf945b | 347 | break; |
bd238fb4 | 348 | case P9_RATTACH: |
b8cf945b EVH |
349 | rcall->params.rattach.qid.type = buf_get_int8(bufp); |
350 | rcall->params.rattach.qid.version = buf_get_int32(bufp); | |
351 | rcall->params.rattach.qid.path = buf_get_int64(bufp); | |
352 | break; | |
bd238fb4 | 353 | case P9_RWALK: |
b8cf945b | 354 | rcall->params.rwalk.nwqid = buf_get_int16(bufp); |
bd238fb4 LI |
355 | if (rcall->params.rwalk.nwqid > P9_MAXWELEM) { |
356 | P9_EPRINTK(KERN_ERR, | |
357 | "Rwalk with more than %d qids: %d\n", | |
358 | P9_MAXWELEM, rcall->params.rwalk.nwqid); | |
3cf6429a LI |
359 | return -EPROTO; |
360 | } | |
361 | ||
531b1094 LI |
362 | for (i = 0; i < rcall->params.rwalk.nwqid; i++) |
363 | buf_get_qid(bufp, &rcall->params.rwalk.wqids[i]); | |
b8cf945b | 364 | break; |
bd238fb4 | 365 | case P9_ROPEN: |
531b1094 | 366 | buf_get_qid(bufp, &rcall->params.ropen.qid); |
b8cf945b EVH |
367 | rcall->params.ropen.iounit = buf_get_int32(bufp); |
368 | break; | |
bd238fb4 | 369 | case P9_RCREATE: |
531b1094 | 370 | buf_get_qid(bufp, &rcall->params.rcreate.qid); |
b8cf945b EVH |
371 | rcall->params.rcreate.iounit = buf_get_int32(bufp); |
372 | break; | |
bd238fb4 | 373 | case P9_RREAD: |
b8cf945b | 374 | rcall->params.rread.count = buf_get_int32(bufp); |
531b1094 LI |
375 | rcall->params.rread.data = bufp->p; |
376 | buf_check_size(bufp, rcall->params.rread.count); | |
b8cf945b | 377 | break; |
bd238fb4 | 378 | case P9_RWRITE: |
b8cf945b EVH |
379 | rcall->params.rwrite.count = buf_get_int32(bufp); |
380 | break; | |
bd238fb4 | 381 | case P9_RCLUNK: |
b8cf945b | 382 | break; |
bd238fb4 | 383 | case P9_RREMOVE: |
b8cf945b | 384 | break; |
bd238fb4 | 385 | case P9_RSTAT: |
b8cf945b | 386 | buf_get_int16(bufp); |
bd238fb4 | 387 | buf_get_stat(bufp, &rcall->params.rstat.stat, dotu); |
b8cf945b | 388 | break; |
bd238fb4 | 389 | case P9_RWSTAT: |
b8cf945b | 390 | break; |
bd238fb4 | 391 | case P9_RERROR: |
531b1094 | 392 | buf_get_str(bufp, &rcall->params.rerror.error); |
bd238fb4 | 393 | if (dotu) |
b8cf945b EVH |
394 | rcall->params.rerror.errno = buf_get_int16(bufp); |
395 | break; | |
396 | } | |
397 | ||
531b1094 | 398 | if (buf_check_overflow(bufp)) { |
bd238fb4 | 399 | P9_DPRINTK(P9_DEBUG_ERROR, "buffer overflow\n"); |
b8cf945b | 400 | return -EIO; |
3cf6429a | 401 | } |
b8cf945b | 402 | |
531b1094 LI |
403 | return bufp->p - bufp->sp; |
404 | } | |
bd238fb4 | 405 | EXPORT_SYMBOL(p9_deserialize_fcall); |
531b1094 | 406 | |
bd238fb4 | 407 | static inline void p9_put_int8(struct cbuf *bufp, u8 val, u8 * p) |
531b1094 LI |
408 | { |
409 | *p = val; | |
410 | buf_put_int8(bufp, val); | |
411 | } | |
412 | ||
bd238fb4 | 413 | static inline void p9_put_int16(struct cbuf *bufp, u16 val, u16 * p) |
531b1094 LI |
414 | { |
415 | *p = val; | |
416 | buf_put_int16(bufp, val); | |
417 | } | |
418 | ||
bd238fb4 | 419 | static inline void p9_put_int32(struct cbuf *bufp, u32 val, u32 * p) |
531b1094 LI |
420 | { |
421 | *p = val; | |
422 | buf_put_int32(bufp, val); | |
423 | } | |
424 | ||
bd238fb4 | 425 | static inline void p9_put_int64(struct cbuf *bufp, u64 val, u64 * p) |
531b1094 LI |
426 | { |
427 | *p = val; | |
428 | buf_put_int64(bufp, val); | |
429 | } | |
430 | ||
858119e1 | 431 | static void |
bd238fb4 | 432 | p9_put_str(struct cbuf *bufp, char *data, struct p9_str *str) |
531b1094 | 433 | { |
05818a00 LI |
434 | int len; |
435 | char *s; | |
436 | ||
437 | if (data) | |
438 | len = strlen(data); | |
439 | else | |
440 | len = 0; | |
531b1094 | 441 | |
05818a00 LI |
442 | s = buf_put_stringn(bufp, data, len); |
443 | if (str) { | |
444 | str->len = len; | |
445 | str->str = s; | |
446 | } | |
531b1094 LI |
447 | } |
448 | ||
858119e1 | 449 | static int |
bd238fb4 LI |
450 | p9_put_data(struct cbuf *bufp, const char *data, int count, |
451 | unsigned char **pdata) | |
452 | { | |
453 | *pdata = buf_alloc(bufp, count); | |
454 | memmove(*pdata, data, count); | |
455 | return count; | |
456 | } | |
457 | ||
458 | static int | |
459 | p9_put_user_data(struct cbuf *bufp, const char __user *data, int count, | |
531b1094 LI |
460 | unsigned char **pdata) |
461 | { | |
462 | *pdata = buf_alloc(bufp, count); | |
463 | return copy_from_user(*pdata, data, count); | |
464 | } | |
465 | ||
466 | static void | |
bd238fb4 LI |
467 | p9_put_wstat(struct cbuf *bufp, struct p9_wstat *wstat, |
468 | struct p9_stat *stat, int statsz, int dotu) | |
531b1094 | 469 | { |
bd238fb4 LI |
470 | p9_put_int16(bufp, statsz, &stat->size); |
471 | p9_put_int16(bufp, wstat->type, &stat->type); | |
472 | p9_put_int32(bufp, wstat->dev, &stat->dev); | |
473 | p9_put_int8(bufp, wstat->qid.type, &stat->qid.type); | |
474 | p9_put_int32(bufp, wstat->qid.version, &stat->qid.version); | |
475 | p9_put_int64(bufp, wstat->qid.path, &stat->qid.path); | |
476 | p9_put_int32(bufp, wstat->mode, &stat->mode); | |
477 | p9_put_int32(bufp, wstat->atime, &stat->atime); | |
478 | p9_put_int32(bufp, wstat->mtime, &stat->mtime); | |
479 | p9_put_int64(bufp, wstat->length, &stat->length); | |
531b1094 | 480 | |
bd238fb4 LI |
481 | p9_put_str(bufp, wstat->name, &stat->name); |
482 | p9_put_str(bufp, wstat->uid, &stat->uid); | |
483 | p9_put_str(bufp, wstat->gid, &stat->gid); | |
484 | p9_put_str(bufp, wstat->muid, &stat->muid); | |
531b1094 | 485 | |
bd238fb4 LI |
486 | if (dotu) { |
487 | p9_put_str(bufp, wstat->extension, &stat->extension); | |
488 | p9_put_int32(bufp, wstat->n_uid, &stat->n_uid); | |
489 | p9_put_int32(bufp, wstat->n_gid, &stat->n_gid); | |
490 | p9_put_int32(bufp, wstat->n_muid, &stat->n_muid); | |
531b1094 LI |
491 | } |
492 | } | |
493 | ||
bd238fb4 LI |
494 | static struct p9_fcall * |
495 | p9_create_common(struct cbuf *bufp, u32 size, u8 id) | |
531b1094 | 496 | { |
bd238fb4 | 497 | struct p9_fcall *fc; |
531b1094 LI |
498 | |
499 | size += 4 + 1 + 2; /* size[4] id[1] tag[2] */ | |
bd238fb4 | 500 | fc = kmalloc(sizeof(struct p9_fcall) + size, GFP_KERNEL); |
531b1094 LI |
501 | if (!fc) |
502 | return ERR_PTR(-ENOMEM); | |
503 | ||
504 | fc->sdata = (char *)fc + sizeof(*fc); | |
505 | ||
506 | buf_init(bufp, (char *)fc->sdata, size); | |
bd238fb4 LI |
507 | p9_put_int32(bufp, size, &fc->size); |
508 | p9_put_int8(bufp, id, &fc->id); | |
509 | p9_put_int16(bufp, P9_NOTAG, &fc->tag); | |
531b1094 LI |
510 | |
511 | return fc; | |
512 | } | |
513 | ||
ee443996 EVH |
514 | /** |
515 | * p9_set_tag - set the tag field of an &p9_fcall structure | |
516 | * @fc: fcall structure to set tag within | |
517 | * @tag: tag id to set | |
518 | */ | |
519 | ||
bd238fb4 | 520 | void p9_set_tag(struct p9_fcall *fc, u16 tag) |
531b1094 | 521 | { |
1dac06b2 | 522 | fc->tag = tag; |
531b1094 LI |
523 | *(__le16 *) (fc->sdata + 5) = cpu_to_le16(tag); |
524 | } | |
bd238fb4 | 525 | EXPORT_SYMBOL(p9_set_tag); |
531b1094 | 526 | |
ee443996 EVH |
527 | /** |
528 | * p9_create_tversion - allocates and creates a T_VERSION request | |
529 | * @msize: requested maximum data size | |
530 | * @version: version string to negotiate | |
531 | * | |
532 | */ | |
bd238fb4 | 533 | struct p9_fcall *p9_create_tversion(u32 msize, char *version) |
531b1094 LI |
534 | { |
535 | int size; | |
bd238fb4 | 536 | struct p9_fcall *fc; |
531b1094 LI |
537 | struct cbuf buffer; |
538 | struct cbuf *bufp = &buffer; | |
539 | ||
540 | size = 4 + 2 + strlen(version); /* msize[4] version[s] */ | |
bd238fb4 | 541 | fc = p9_create_common(bufp, size, P9_TVERSION); |
531b1094 LI |
542 | if (IS_ERR(fc)) |
543 | goto error; | |
544 | ||
bd238fb4 LI |
545 | p9_put_int32(bufp, msize, &fc->params.tversion.msize); |
546 | p9_put_str(bufp, version, &fc->params.tversion.version); | |
531b1094 LI |
547 | |
548 | if (buf_check_overflow(bufp)) { | |
549 | kfree(fc); | |
550 | fc = ERR_PTR(-ENOMEM); | |
551 | } | |
bd238fb4 | 552 | error: |
531b1094 LI |
553 | return fc; |
554 | } | |
bd238fb4 | 555 | EXPORT_SYMBOL(p9_create_tversion); |
531b1094 | 556 | |
ee443996 EVH |
557 | /** |
558 | * p9_create_tauth - allocates and creates a T_AUTH request | |
559 | * @afid: handle to use for authentication protocol | |
560 | * @uname: user name attempting to authenticate | |
561 | * @aname: mount specifier for remote server | |
562 | * @n_uname: numeric id for user attempting to authneticate | |
563 | * @dotu: 9P2000.u extension flag | |
564 | * | |
565 | */ | |
566 | ||
ba17674f LI |
567 | struct p9_fcall *p9_create_tauth(u32 afid, char *uname, char *aname, |
568 | u32 n_uname, int dotu) | |
531b1094 LI |
569 | { |
570 | int size; | |
bd238fb4 | 571 | struct p9_fcall *fc; |
531b1094 LI |
572 | struct cbuf buffer; |
573 | struct cbuf *bufp = &buffer; | |
574 | ||
bd238fb4 | 575 | /* afid[4] uname[s] aname[s] */ |
ba17674f LI |
576 | size = 4 + 2 + 2; |
577 | if (uname) | |
578 | size += strlen(uname); | |
579 | ||
580 | if (aname) | |
581 | size += strlen(aname); | |
582 | ||
583 | if (dotu) | |
584 | size += 4; /* n_uname */ | |
585 | ||
bd238fb4 | 586 | fc = p9_create_common(bufp, size, P9_TAUTH); |
531b1094 LI |
587 | if (IS_ERR(fc)) |
588 | goto error; | |
589 | ||
bd238fb4 LI |
590 | p9_put_int32(bufp, afid, &fc->params.tauth.afid); |
591 | p9_put_str(bufp, uname, &fc->params.tauth.uname); | |
592 | p9_put_str(bufp, aname, &fc->params.tauth.aname); | |
ba17674f LI |
593 | if (dotu) |
594 | p9_put_int32(bufp, n_uname, &fc->params.tauth.n_uname); | |
531b1094 LI |
595 | |
596 | if (buf_check_overflow(bufp)) { | |
597 | kfree(fc); | |
598 | fc = ERR_PTR(-ENOMEM); | |
599 | } | |
bd238fb4 | 600 | error: |
531b1094 LI |
601 | return fc; |
602 | } | |
bd238fb4 | 603 | EXPORT_SYMBOL(p9_create_tauth); |
531b1094 | 604 | |
ee443996 EVH |
605 | /** |
606 | * p9_create_tattach - allocates and creates a T_ATTACH request | |
607 | * @fid: handle to use for the new mount point | |
608 | * @afid: handle to use for authentication protocol | |
609 | * @uname: user name attempting to attach | |
610 | * @aname: mount specifier for remote server | |
611 | * @n_uname: numeric id for user attempting to attach | |
612 | * @n_uname: numeric id for user attempting to attach | |
613 | * @dotu: 9P2000.u extension flag | |
614 | * | |
615 | */ | |
616 | ||
bd238fb4 | 617 | struct p9_fcall * |
ba17674f LI |
618 | p9_create_tattach(u32 fid, u32 afid, char *uname, char *aname, |
619 | u32 n_uname, int dotu) | |
531b1094 LI |
620 | { |
621 | int size; | |
bd238fb4 | 622 | struct p9_fcall *fc; |
531b1094 LI |
623 | struct cbuf buffer; |
624 | struct cbuf *bufp = &buffer; | |
625 | ||
bd238fb4 | 626 | /* fid[4] afid[4] uname[s] aname[s] */ |
ba17674f LI |
627 | size = 4 + 4 + 2 + 2; |
628 | if (uname) | |
629 | size += strlen(uname); | |
630 | ||
631 | if (aname) | |
632 | size += strlen(aname); | |
633 | ||
634 | if (dotu) | |
635 | size += 4; /* n_uname */ | |
636 | ||
bd238fb4 | 637 | fc = p9_create_common(bufp, size, P9_TATTACH); |
531b1094 LI |
638 | if (IS_ERR(fc)) |
639 | goto error; | |
640 | ||
bd238fb4 LI |
641 | p9_put_int32(bufp, fid, &fc->params.tattach.fid); |
642 | p9_put_int32(bufp, afid, &fc->params.tattach.afid); | |
643 | p9_put_str(bufp, uname, &fc->params.tattach.uname); | |
644 | p9_put_str(bufp, aname, &fc->params.tattach.aname); | |
ba17674f LI |
645 | if (dotu) |
646 | p9_put_int32(bufp, n_uname, &fc->params.tattach.n_uname); | |
531b1094 | 647 | |
bd238fb4 | 648 | error: |
531b1094 LI |
649 | return fc; |
650 | } | |
bd238fb4 | 651 | EXPORT_SYMBOL(p9_create_tattach); |
531b1094 | 652 | |
ee443996 EVH |
653 | /** |
654 | * p9_create_tflush - allocates and creates a T_FLUSH request | |
655 | * @oldtag: tag id for the transaction we are attempting to cancel | |
656 | * | |
657 | */ | |
658 | ||
bd238fb4 | 659 | struct p9_fcall *p9_create_tflush(u16 oldtag) |
531b1094 LI |
660 | { |
661 | int size; | |
bd238fb4 | 662 | struct p9_fcall *fc; |
531b1094 LI |
663 | struct cbuf buffer; |
664 | struct cbuf *bufp = &buffer; | |
665 | ||
666 | size = 2; /* oldtag[2] */ | |
bd238fb4 | 667 | fc = p9_create_common(bufp, size, P9_TFLUSH); |
531b1094 LI |
668 | if (IS_ERR(fc)) |
669 | goto error; | |
670 | ||
bd238fb4 | 671 | p9_put_int16(bufp, oldtag, &fc->params.tflush.oldtag); |
531b1094 LI |
672 | |
673 | if (buf_check_overflow(bufp)) { | |
674 | kfree(fc); | |
675 | fc = ERR_PTR(-ENOMEM); | |
676 | } | |
bd238fb4 | 677 | error: |
531b1094 LI |
678 | return fc; |
679 | } | |
bd238fb4 | 680 | EXPORT_SYMBOL(p9_create_tflush); |
531b1094 | 681 | |
ee443996 EVH |
682 | /** |
683 | * p9_create_twalk - allocates and creates a T_FLUSH request | |
684 | * @fid: handle we are traversing from | |
685 | * @newfid: a new handle for this transaction | |
686 | * @nwname: number of path elements to traverse | |
687 | * @wnames: array of path elements | |
688 | * | |
689 | */ | |
690 | ||
bd238fb4 | 691 | struct p9_fcall *p9_create_twalk(u32 fid, u32 newfid, u16 nwname, |
531b1094 LI |
692 | char **wnames) |
693 | { | |
694 | int i, size; | |
bd238fb4 | 695 | struct p9_fcall *fc; |
531b1094 LI |
696 | struct cbuf buffer; |
697 | struct cbuf *bufp = &buffer; | |
698 | ||
bd238fb4 LI |
699 | if (nwname > P9_MAXWELEM) { |
700 | P9_DPRINTK(P9_DEBUG_ERROR, "nwname > %d\n", P9_MAXWELEM); | |
531b1094 LI |
701 | return NULL; |
702 | } | |
703 | ||
704 | size = 4 + 4 + 2; /* fid[4] newfid[4] nwname[2] ... */ | |
705 | for (i = 0; i < nwname; i++) { | |
706 | size += 2 + strlen(wnames[i]); /* wname[s] */ | |
707 | } | |
708 | ||
bd238fb4 | 709 | fc = p9_create_common(bufp, size, P9_TWALK); |
531b1094 LI |
710 | if (IS_ERR(fc)) |
711 | goto error; | |
712 | ||
bd238fb4 LI |
713 | p9_put_int32(bufp, fid, &fc->params.twalk.fid); |
714 | p9_put_int32(bufp, newfid, &fc->params.twalk.newfid); | |
715 | p9_put_int16(bufp, nwname, &fc->params.twalk.nwname); | |
531b1094 | 716 | for (i = 0; i < nwname; i++) { |
bd238fb4 | 717 | p9_put_str(bufp, wnames[i], &fc->params.twalk.wnames[i]); |
531b1094 LI |
718 | } |
719 | ||
720 | if (buf_check_overflow(bufp)) { | |
721 | kfree(fc); | |
722 | fc = ERR_PTR(-ENOMEM); | |
723 | } | |
bd238fb4 | 724 | error: |
531b1094 LI |
725 | return fc; |
726 | } | |
bd238fb4 | 727 | EXPORT_SYMBOL(p9_create_twalk); |
531b1094 | 728 | |
ee443996 EVH |
729 | /** |
730 | * p9_create_topen - allocates and creates a T_OPEN request | |
731 | * @fid: handle we are trying to open | |
732 | * @mode: what mode we are trying to open the file in | |
733 | * | |
734 | */ | |
735 | ||
bd238fb4 | 736 | struct p9_fcall *p9_create_topen(u32 fid, u8 mode) |
531b1094 LI |
737 | { |
738 | int size; | |
bd238fb4 | 739 | struct p9_fcall *fc; |
531b1094 LI |
740 | struct cbuf buffer; |
741 | struct cbuf *bufp = &buffer; | |
742 | ||
743 | size = 4 + 1; /* fid[4] mode[1] */ | |
bd238fb4 | 744 | fc = p9_create_common(bufp, size, P9_TOPEN); |
531b1094 LI |
745 | if (IS_ERR(fc)) |
746 | goto error; | |
747 | ||
bd238fb4 LI |
748 | p9_put_int32(bufp, fid, &fc->params.topen.fid); |
749 | p9_put_int8(bufp, mode, &fc->params.topen.mode); | |
531b1094 LI |
750 | |
751 | if (buf_check_overflow(bufp)) { | |
752 | kfree(fc); | |
753 | fc = ERR_PTR(-ENOMEM); | |
754 | } | |
bd238fb4 | 755 | error: |
531b1094 LI |
756 | return fc; |
757 | } | |
bd238fb4 | 758 | EXPORT_SYMBOL(p9_create_topen); |
531b1094 | 759 | |
ee443996 EVH |
760 | /** |
761 | * p9_create_tcreate - allocates and creates a T_CREATE request | |
762 | * @fid: handle of directory we are trying to create in | |
763 | * @name: name of the file we are trying to create | |
764 | * @perm: permissions for the file we are trying to create | |
765 | * @mode: what mode we are trying to open the file in | |
766 | * @extension: 9p2000.u extension string (for special files) | |
767 | * @dotu: 9p2000.u enabled flag | |
768 | * | |
769 | * Note: Plan 9 create semantics include opening the resulting file | |
770 | * which is why mode is included. | |
771 | */ | |
772 | ||
bd238fb4 LI |
773 | struct p9_fcall *p9_create_tcreate(u32 fid, char *name, u32 perm, u8 mode, |
774 | char *extension, int dotu) | |
531b1094 LI |
775 | { |
776 | int size; | |
bd238fb4 | 777 | struct p9_fcall *fc; |
531b1094 LI |
778 | struct cbuf buffer; |
779 | struct cbuf *bufp = &buffer; | |
780 | ||
bd238fb4 LI |
781 | /* fid[4] name[s] perm[4] mode[1] */ |
782 | size = 4 + 2 + strlen(name) + 4 + 1; | |
783 | if (dotu) { | |
4c90c68a RR |
784 | size += 2 + /* extension[s] */ |
785 | (extension == NULL ? 0 : strlen(extension)); | |
786 | } | |
16cce6d2 | 787 | |
bd238fb4 | 788 | fc = p9_create_common(bufp, size, P9_TCREATE); |
531b1094 LI |
789 | if (IS_ERR(fc)) |
790 | goto error; | |
791 | ||
bd238fb4 LI |
792 | p9_put_int32(bufp, fid, &fc->params.tcreate.fid); |
793 | p9_put_str(bufp, name, &fc->params.tcreate.name); | |
794 | p9_put_int32(bufp, perm, &fc->params.tcreate.perm); | |
795 | p9_put_int8(bufp, mode, &fc->params.tcreate.mode); | |
796 | if (dotu) | |
797 | p9_put_str(bufp, extension, &fc->params.tcreate.extension); | |
531b1094 LI |
798 | |
799 | if (buf_check_overflow(bufp)) { | |
800 | kfree(fc); | |
801 | fc = ERR_PTR(-ENOMEM); | |
802 | } | |
bd238fb4 | 803 | error: |
531b1094 LI |
804 | return fc; |
805 | } | |
bd238fb4 | 806 | EXPORT_SYMBOL(p9_create_tcreate); |
531b1094 | 807 | |
ee443996 EVH |
808 | /** |
809 | * p9_create_tread - allocates and creates a T_READ request | |
810 | * @fid: handle of the file we are trying to read | |
811 | * @offset: offset to start reading from | |
812 | * @count: how many bytes to read | |
813 | */ | |
814 | ||
bd238fb4 | 815 | struct p9_fcall *p9_create_tread(u32 fid, u64 offset, u32 count) |
531b1094 LI |
816 | { |
817 | int size; | |
bd238fb4 | 818 | struct p9_fcall *fc; |
531b1094 LI |
819 | struct cbuf buffer; |
820 | struct cbuf *bufp = &buffer; | |
821 | ||
822 | size = 4 + 8 + 4; /* fid[4] offset[8] count[4] */ | |
bd238fb4 LI |
823 | fc = p9_create_common(bufp, size, P9_TREAD); |
824 | if (IS_ERR(fc)) | |
825 | goto error; | |
826 | ||
827 | p9_put_int32(bufp, fid, &fc->params.tread.fid); | |
828 | p9_put_int64(bufp, offset, &fc->params.tread.offset); | |
829 | p9_put_int32(bufp, count, &fc->params.tread.count); | |
830 | ||
831 | if (buf_check_overflow(bufp)) { | |
832 | kfree(fc); | |
833 | fc = ERR_PTR(-ENOMEM); | |
834 | } | |
835 | error: | |
836 | return fc; | |
837 | } | |
838 | EXPORT_SYMBOL(p9_create_tread); | |
839 | ||
ee443996 EVH |
840 | /** |
841 | * p9_create_twrite - allocates and creates a T_WRITE request from the kernel | |
842 | * @fid: handle of the file we are trying to write | |
843 | * @offset: offset to start writing at | |
844 | * @count: how many bytes to write | |
845 | * @data: data to write | |
846 | * | |
847 | * This function will create a requst with data buffers from the kernel | |
848 | * such as the page cache. | |
849 | */ | |
850 | ||
bd238fb4 LI |
851 | struct p9_fcall *p9_create_twrite(u32 fid, u64 offset, u32 count, |
852 | const char *data) | |
853 | { | |
854 | int size, err; | |
855 | struct p9_fcall *fc; | |
856 | struct cbuf buffer; | |
857 | struct cbuf *bufp = &buffer; | |
858 | ||
859 | /* fid[4] offset[8] count[4] data[count] */ | |
860 | size = 4 + 8 + 4 + count; | |
861 | fc = p9_create_common(bufp, size, P9_TWRITE); | |
531b1094 LI |
862 | if (IS_ERR(fc)) |
863 | goto error; | |
864 | ||
bd238fb4 LI |
865 | p9_put_int32(bufp, fid, &fc->params.twrite.fid); |
866 | p9_put_int64(bufp, offset, &fc->params.twrite.offset); | |
867 | p9_put_int32(bufp, count, &fc->params.twrite.count); | |
868 | err = p9_put_data(bufp, data, count, &fc->params.twrite.data); | |
869 | if (err) { | |
870 | kfree(fc); | |
871 | fc = ERR_PTR(err); | |
bbe06f6b | 872 | goto error; |
bd238fb4 | 873 | } |
531b1094 LI |
874 | |
875 | if (buf_check_overflow(bufp)) { | |
876 | kfree(fc); | |
877 | fc = ERR_PTR(-ENOMEM); | |
878 | } | |
bd238fb4 | 879 | error: |
531b1094 LI |
880 | return fc; |
881 | } | |
bd238fb4 | 882 | EXPORT_SYMBOL(p9_create_twrite); |
531b1094 | 883 | |
ee443996 EVH |
884 | /** |
885 | * p9_create_twrite_u - allocates and creates a T_WRITE request from userspace | |
886 | * @fid: handle of the file we are trying to write | |
887 | * @offset: offset to start writing at | |
888 | * @count: how many bytes to write | |
889 | * @data: data to write | |
890 | * | |
891 | * This function will create a request with data buffers from userspace | |
892 | */ | |
893 | ||
bd238fb4 LI |
894 | struct p9_fcall *p9_create_twrite_u(u32 fid, u64 offset, u32 count, |
895 | const char __user *data) | |
531b1094 LI |
896 | { |
897 | int size, err; | |
bd238fb4 | 898 | struct p9_fcall *fc; |
531b1094 LI |
899 | struct cbuf buffer; |
900 | struct cbuf *bufp = &buffer; | |
901 | ||
bd238fb4 LI |
902 | /* fid[4] offset[8] count[4] data[count] */ |
903 | size = 4 + 8 + 4 + count; | |
904 | fc = p9_create_common(bufp, size, P9_TWRITE); | |
531b1094 LI |
905 | if (IS_ERR(fc)) |
906 | goto error; | |
907 | ||
bd238fb4 LI |
908 | p9_put_int32(bufp, fid, &fc->params.twrite.fid); |
909 | p9_put_int64(bufp, offset, &fc->params.twrite.offset); | |
910 | p9_put_int32(bufp, count, &fc->params.twrite.count); | |
911 | err = p9_put_user_data(bufp, data, count, &fc->params.twrite.data); | |
531b1094 LI |
912 | if (err) { |
913 | kfree(fc); | |
914 | fc = ERR_PTR(err); | |
02881d94 | 915 | goto error; |
531b1094 LI |
916 | } |
917 | ||
918 | if (buf_check_overflow(bufp)) { | |
919 | kfree(fc); | |
920 | fc = ERR_PTR(-ENOMEM); | |
921 | } | |
bd238fb4 | 922 | error: |
531b1094 LI |
923 | return fc; |
924 | } | |
bd238fb4 | 925 | EXPORT_SYMBOL(p9_create_twrite_u); |
531b1094 | 926 | |
ee443996 EVH |
927 | /** |
928 | * p9_create_tclunk - allocate a request to forget about a file handle | |
929 | * @fid: handle of the file we closing or forgetting about | |
930 | * | |
931 | * clunk is used both to close open files and to discard transient handles | |
932 | * which may be created during meta-data operations and hierarchy traversal. | |
933 | */ | |
934 | ||
bd238fb4 | 935 | struct p9_fcall *p9_create_tclunk(u32 fid) |
531b1094 LI |
936 | { |
937 | int size; | |
bd238fb4 | 938 | struct p9_fcall *fc; |
531b1094 LI |
939 | struct cbuf buffer; |
940 | struct cbuf *bufp = &buffer; | |
941 | ||
942 | size = 4; /* fid[4] */ | |
bd238fb4 | 943 | fc = p9_create_common(bufp, size, P9_TCLUNK); |
531b1094 LI |
944 | if (IS_ERR(fc)) |
945 | goto error; | |
946 | ||
bd238fb4 | 947 | p9_put_int32(bufp, fid, &fc->params.tclunk.fid); |
531b1094 LI |
948 | |
949 | if (buf_check_overflow(bufp)) { | |
950 | kfree(fc); | |
951 | fc = ERR_PTR(-ENOMEM); | |
952 | } | |
bd238fb4 | 953 | error: |
531b1094 LI |
954 | return fc; |
955 | } | |
bd238fb4 | 956 | EXPORT_SYMBOL(p9_create_tclunk); |
531b1094 | 957 | |
ee443996 EVH |
958 | /** |
959 | * p9_create_tremove - allocate and create a request to remove a file | |
960 | * @fid: handle of the file or directory we are removing | |
961 | * | |
962 | */ | |
963 | ||
bd238fb4 | 964 | struct p9_fcall *p9_create_tremove(u32 fid) |
531b1094 LI |
965 | { |
966 | int size; | |
bd238fb4 | 967 | struct p9_fcall *fc; |
531b1094 LI |
968 | struct cbuf buffer; |
969 | struct cbuf *bufp = &buffer; | |
970 | ||
971 | size = 4; /* fid[4] */ | |
bd238fb4 | 972 | fc = p9_create_common(bufp, size, P9_TREMOVE); |
531b1094 LI |
973 | if (IS_ERR(fc)) |
974 | goto error; | |
975 | ||
bd238fb4 | 976 | p9_put_int32(bufp, fid, &fc->params.tremove.fid); |
531b1094 LI |
977 | |
978 | if (buf_check_overflow(bufp)) { | |
979 | kfree(fc); | |
980 | fc = ERR_PTR(-ENOMEM); | |
981 | } | |
bd238fb4 | 982 | error: |
531b1094 LI |
983 | return fc; |
984 | } | |
bd238fb4 | 985 | EXPORT_SYMBOL(p9_create_tremove); |
531b1094 | 986 | |
ee443996 EVH |
987 | /** |
988 | * p9_create_tstat - allocate and populate a request for attributes | |
989 | * @fid: handle of the file or directory we are trying to get the attributes of | |
990 | * | |
991 | */ | |
992 | ||
bd238fb4 | 993 | struct p9_fcall *p9_create_tstat(u32 fid) |
531b1094 LI |
994 | { |
995 | int size; | |
bd238fb4 | 996 | struct p9_fcall *fc; |
531b1094 LI |
997 | struct cbuf buffer; |
998 | struct cbuf *bufp = &buffer; | |
999 | ||
1000 | size = 4; /* fid[4] */ | |
bd238fb4 | 1001 | fc = p9_create_common(bufp, size, P9_TSTAT); |
531b1094 LI |
1002 | if (IS_ERR(fc)) |
1003 | goto error; | |
1004 | ||
bd238fb4 | 1005 | p9_put_int32(bufp, fid, &fc->params.tstat.fid); |
531b1094 LI |
1006 | |
1007 | if (buf_check_overflow(bufp)) { | |
1008 | kfree(fc); | |
1009 | fc = ERR_PTR(-ENOMEM); | |
1010 | } | |
bd238fb4 | 1011 | error: |
531b1094 LI |
1012 | return fc; |
1013 | } | |
bd238fb4 | 1014 | EXPORT_SYMBOL(p9_create_tstat); |
531b1094 | 1015 | |
ee443996 EVH |
1016 | /** |
1017 | * p9_create_tstat - allocate and populate a request to change attributes | |
1018 | * @fid: handle of the file or directory we are trying to change | |
1019 | * @wstat: &p9_stat structure with attributes we wish to set | |
1020 | * @dotu: 9p2000.u enabled flag | |
1021 | * | |
1022 | */ | |
1023 | ||
bd238fb4 LI |
1024 | struct p9_fcall *p9_create_twstat(u32 fid, struct p9_wstat *wstat, |
1025 | int dotu) | |
531b1094 LI |
1026 | { |
1027 | int size, statsz; | |
bd238fb4 | 1028 | struct p9_fcall *fc; |
531b1094 LI |
1029 | struct cbuf buffer; |
1030 | struct cbuf *bufp = &buffer; | |
1031 | ||
bd238fb4 | 1032 | statsz = p9_size_wstat(wstat, dotu); |
531b1094 | 1033 | size = 4 + 2 + 2 + statsz; /* fid[4] stat[n] */ |
bd238fb4 | 1034 | fc = p9_create_common(bufp, size, P9_TWSTAT); |
531b1094 LI |
1035 | if (IS_ERR(fc)) |
1036 | goto error; | |
1037 | ||
bd238fb4 | 1038 | p9_put_int32(bufp, fid, &fc->params.twstat.fid); |
531b1094 | 1039 | buf_put_int16(bufp, statsz + 2); |
bd238fb4 | 1040 | p9_put_wstat(bufp, wstat, &fc->params.twstat.stat, statsz, dotu); |
531b1094 LI |
1041 | |
1042 | if (buf_check_overflow(bufp)) { | |
1043 | kfree(fc); | |
1044 | fc = ERR_PTR(-ENOMEM); | |
1045 | } | |
bd238fb4 | 1046 | error: |
531b1094 | 1047 | return fc; |
b8cf945b | 1048 | } |
bd238fb4 | 1049 | EXPORT_SYMBOL(p9_create_twstat); |
ee443996 | 1050 |