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 <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 | |
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); | |
16ec4700 EVH |
454 | if (*pdata == NULL) |
455 | return -ENOMEM; | |
bd238fb4 | 456 | memmove(*pdata, data, count); |
16ec4700 | 457 | return 0; |
bd238fb4 LI |
458 | } |
459 | ||
460 | static int | |
461 | p9_put_user_data(struct cbuf *bufp, const char __user *data, int count, | |
531b1094 LI |
462 | unsigned char **pdata) |
463 | { | |
464 | *pdata = buf_alloc(bufp, count); | |
16ec4700 EVH |
465 | if (*pdata == NULL) |
466 | return -ENOMEM; | |
531b1094 LI |
467 | return copy_from_user(*pdata, data, count); |
468 | } | |
469 | ||
470 | static void | |
bd238fb4 LI |
471 | p9_put_wstat(struct cbuf *bufp, struct p9_wstat *wstat, |
472 | struct p9_stat *stat, int statsz, int dotu) | |
531b1094 | 473 | { |
bd238fb4 LI |
474 | p9_put_int16(bufp, statsz, &stat->size); |
475 | p9_put_int16(bufp, wstat->type, &stat->type); | |
476 | p9_put_int32(bufp, wstat->dev, &stat->dev); | |
477 | p9_put_int8(bufp, wstat->qid.type, &stat->qid.type); | |
478 | p9_put_int32(bufp, wstat->qid.version, &stat->qid.version); | |
479 | p9_put_int64(bufp, wstat->qid.path, &stat->qid.path); | |
480 | p9_put_int32(bufp, wstat->mode, &stat->mode); | |
481 | p9_put_int32(bufp, wstat->atime, &stat->atime); | |
482 | p9_put_int32(bufp, wstat->mtime, &stat->mtime); | |
483 | p9_put_int64(bufp, wstat->length, &stat->length); | |
531b1094 | 484 | |
bd238fb4 LI |
485 | p9_put_str(bufp, wstat->name, &stat->name); |
486 | p9_put_str(bufp, wstat->uid, &stat->uid); | |
487 | p9_put_str(bufp, wstat->gid, &stat->gid); | |
488 | p9_put_str(bufp, wstat->muid, &stat->muid); | |
531b1094 | 489 | |
bd238fb4 LI |
490 | if (dotu) { |
491 | p9_put_str(bufp, wstat->extension, &stat->extension); | |
492 | p9_put_int32(bufp, wstat->n_uid, &stat->n_uid); | |
493 | p9_put_int32(bufp, wstat->n_gid, &stat->n_gid); | |
494 | p9_put_int32(bufp, wstat->n_muid, &stat->n_muid); | |
531b1094 LI |
495 | } |
496 | } | |
497 | ||
bd238fb4 LI |
498 | static struct p9_fcall * |
499 | p9_create_common(struct cbuf *bufp, u32 size, u8 id) | |
531b1094 | 500 | { |
bd238fb4 | 501 | struct p9_fcall *fc; |
531b1094 LI |
502 | |
503 | size += 4 + 1 + 2; /* size[4] id[1] tag[2] */ | |
bd238fb4 | 504 | fc = kmalloc(sizeof(struct p9_fcall) + size, GFP_KERNEL); |
531b1094 LI |
505 | if (!fc) |
506 | return ERR_PTR(-ENOMEM); | |
507 | ||
508 | fc->sdata = (char *)fc + sizeof(*fc); | |
509 | ||
510 | buf_init(bufp, (char *)fc->sdata, size); | |
bd238fb4 LI |
511 | p9_put_int32(bufp, size, &fc->size); |
512 | p9_put_int8(bufp, id, &fc->id); | |
513 | p9_put_int16(bufp, P9_NOTAG, &fc->tag); | |
531b1094 LI |
514 | |
515 | return fc; | |
516 | } | |
517 | ||
ee443996 EVH |
518 | /** |
519 | * p9_set_tag - set the tag field of an &p9_fcall structure | |
520 | * @fc: fcall structure to set tag within | |
521 | * @tag: tag id to set | |
522 | */ | |
523 | ||
bd238fb4 | 524 | void p9_set_tag(struct p9_fcall *fc, u16 tag) |
531b1094 | 525 | { |
1dac06b2 | 526 | fc->tag = tag; |
531b1094 LI |
527 | *(__le16 *) (fc->sdata + 5) = cpu_to_le16(tag); |
528 | } | |
bd238fb4 | 529 | EXPORT_SYMBOL(p9_set_tag); |
531b1094 | 530 | |
ee443996 EVH |
531 | /** |
532 | * p9_create_tversion - allocates and creates a T_VERSION request | |
533 | * @msize: requested maximum data size | |
534 | * @version: version string to negotiate | |
535 | * | |
536 | */ | |
bd238fb4 | 537 | struct p9_fcall *p9_create_tversion(u32 msize, char *version) |
531b1094 LI |
538 | { |
539 | int size; | |
bd238fb4 | 540 | struct p9_fcall *fc; |
531b1094 LI |
541 | struct cbuf buffer; |
542 | struct cbuf *bufp = &buffer; | |
543 | ||
544 | size = 4 + 2 + strlen(version); /* msize[4] version[s] */ | |
bd238fb4 | 545 | fc = p9_create_common(bufp, size, P9_TVERSION); |
531b1094 LI |
546 | if (IS_ERR(fc)) |
547 | goto error; | |
548 | ||
bd238fb4 LI |
549 | p9_put_int32(bufp, msize, &fc->params.tversion.msize); |
550 | p9_put_str(bufp, version, &fc->params.tversion.version); | |
531b1094 LI |
551 | |
552 | if (buf_check_overflow(bufp)) { | |
553 | kfree(fc); | |
554 | fc = ERR_PTR(-ENOMEM); | |
555 | } | |
bd238fb4 | 556 | error: |
531b1094 LI |
557 | return fc; |
558 | } | |
bd238fb4 | 559 | EXPORT_SYMBOL(p9_create_tversion); |
531b1094 | 560 | |
ee443996 EVH |
561 | /** |
562 | * p9_create_tauth - allocates and creates a T_AUTH request | |
563 | * @afid: handle to use for authentication protocol | |
564 | * @uname: user name attempting to authenticate | |
565 | * @aname: mount specifier for remote server | |
566 | * @n_uname: numeric id for user attempting to authneticate | |
567 | * @dotu: 9P2000.u extension flag | |
568 | * | |
569 | */ | |
570 | ||
ba17674f LI |
571 | struct p9_fcall *p9_create_tauth(u32 afid, char *uname, char *aname, |
572 | u32 n_uname, int dotu) | |
531b1094 LI |
573 | { |
574 | int size; | |
bd238fb4 | 575 | struct p9_fcall *fc; |
531b1094 LI |
576 | struct cbuf buffer; |
577 | struct cbuf *bufp = &buffer; | |
578 | ||
bd238fb4 | 579 | /* afid[4] uname[s] aname[s] */ |
ba17674f LI |
580 | size = 4 + 2 + 2; |
581 | if (uname) | |
582 | size += strlen(uname); | |
583 | ||
584 | if (aname) | |
585 | size += strlen(aname); | |
586 | ||
587 | if (dotu) | |
588 | size += 4; /* n_uname */ | |
589 | ||
bd238fb4 | 590 | fc = p9_create_common(bufp, size, P9_TAUTH); |
531b1094 LI |
591 | if (IS_ERR(fc)) |
592 | goto error; | |
593 | ||
bd238fb4 LI |
594 | p9_put_int32(bufp, afid, &fc->params.tauth.afid); |
595 | p9_put_str(bufp, uname, &fc->params.tauth.uname); | |
596 | p9_put_str(bufp, aname, &fc->params.tauth.aname); | |
ba17674f LI |
597 | if (dotu) |
598 | p9_put_int32(bufp, n_uname, &fc->params.tauth.n_uname); | |
531b1094 LI |
599 | |
600 | if (buf_check_overflow(bufp)) { | |
601 | kfree(fc); | |
602 | fc = ERR_PTR(-ENOMEM); | |
603 | } | |
bd238fb4 | 604 | error: |
531b1094 LI |
605 | return fc; |
606 | } | |
bd238fb4 | 607 | EXPORT_SYMBOL(p9_create_tauth); |
531b1094 | 608 | |
ee443996 EVH |
609 | /** |
610 | * p9_create_tattach - allocates and creates a T_ATTACH request | |
611 | * @fid: handle to use for the new mount point | |
612 | * @afid: handle to use for authentication protocol | |
613 | * @uname: user name attempting to attach | |
614 | * @aname: mount specifier for remote server | |
615 | * @n_uname: numeric id for user attempting to attach | |
616 | * @n_uname: numeric id for user attempting to attach | |
617 | * @dotu: 9P2000.u extension flag | |
618 | * | |
619 | */ | |
620 | ||
bd238fb4 | 621 | struct p9_fcall * |
ba17674f LI |
622 | p9_create_tattach(u32 fid, u32 afid, char *uname, char *aname, |
623 | u32 n_uname, int dotu) | |
531b1094 LI |
624 | { |
625 | int size; | |
bd238fb4 | 626 | struct p9_fcall *fc; |
531b1094 LI |
627 | struct cbuf buffer; |
628 | struct cbuf *bufp = &buffer; | |
629 | ||
bd238fb4 | 630 | /* fid[4] afid[4] uname[s] aname[s] */ |
ba17674f LI |
631 | size = 4 + 4 + 2 + 2; |
632 | if (uname) | |
633 | size += strlen(uname); | |
634 | ||
635 | if (aname) | |
636 | size += strlen(aname); | |
637 | ||
638 | if (dotu) | |
639 | size += 4; /* n_uname */ | |
640 | ||
bd238fb4 | 641 | fc = p9_create_common(bufp, size, P9_TATTACH); |
531b1094 LI |
642 | if (IS_ERR(fc)) |
643 | goto error; | |
644 | ||
bd238fb4 LI |
645 | p9_put_int32(bufp, fid, &fc->params.tattach.fid); |
646 | p9_put_int32(bufp, afid, &fc->params.tattach.afid); | |
647 | p9_put_str(bufp, uname, &fc->params.tattach.uname); | |
648 | p9_put_str(bufp, aname, &fc->params.tattach.aname); | |
ba17674f LI |
649 | if (dotu) |
650 | p9_put_int32(bufp, n_uname, &fc->params.tattach.n_uname); | |
531b1094 | 651 | |
bd238fb4 | 652 | error: |
531b1094 LI |
653 | return fc; |
654 | } | |
bd238fb4 | 655 | EXPORT_SYMBOL(p9_create_tattach); |
531b1094 | 656 | |
ee443996 EVH |
657 | /** |
658 | * p9_create_tflush - allocates and creates a T_FLUSH request | |
659 | * @oldtag: tag id for the transaction we are attempting to cancel | |
660 | * | |
661 | */ | |
662 | ||
bd238fb4 | 663 | struct p9_fcall *p9_create_tflush(u16 oldtag) |
531b1094 LI |
664 | { |
665 | int size; | |
bd238fb4 | 666 | struct p9_fcall *fc; |
531b1094 LI |
667 | struct cbuf buffer; |
668 | struct cbuf *bufp = &buffer; | |
669 | ||
670 | size = 2; /* oldtag[2] */ | |
bd238fb4 | 671 | fc = p9_create_common(bufp, size, P9_TFLUSH); |
531b1094 LI |
672 | if (IS_ERR(fc)) |
673 | goto error; | |
674 | ||
bd238fb4 | 675 | p9_put_int16(bufp, oldtag, &fc->params.tflush.oldtag); |
531b1094 LI |
676 | |
677 | if (buf_check_overflow(bufp)) { | |
678 | kfree(fc); | |
679 | fc = ERR_PTR(-ENOMEM); | |
680 | } | |
bd238fb4 | 681 | error: |
531b1094 LI |
682 | return fc; |
683 | } | |
bd238fb4 | 684 | EXPORT_SYMBOL(p9_create_tflush); |
531b1094 | 685 | |
ee443996 EVH |
686 | /** |
687 | * p9_create_twalk - allocates and creates a T_FLUSH request | |
688 | * @fid: handle we are traversing from | |
689 | * @newfid: a new handle for this transaction | |
690 | * @nwname: number of path elements to traverse | |
691 | * @wnames: array of path elements | |
692 | * | |
693 | */ | |
694 | ||
bd238fb4 | 695 | struct p9_fcall *p9_create_twalk(u32 fid, u32 newfid, u16 nwname, |
531b1094 LI |
696 | char **wnames) |
697 | { | |
698 | int i, size; | |
bd238fb4 | 699 | struct p9_fcall *fc; |
531b1094 LI |
700 | struct cbuf buffer; |
701 | struct cbuf *bufp = &buffer; | |
702 | ||
bd238fb4 LI |
703 | if (nwname > P9_MAXWELEM) { |
704 | P9_DPRINTK(P9_DEBUG_ERROR, "nwname > %d\n", P9_MAXWELEM); | |
531b1094 LI |
705 | return NULL; |
706 | } | |
707 | ||
708 | size = 4 + 4 + 2; /* fid[4] newfid[4] nwname[2] ... */ | |
709 | for (i = 0; i < nwname; i++) { | |
710 | size += 2 + strlen(wnames[i]); /* wname[s] */ | |
711 | } | |
712 | ||
bd238fb4 | 713 | fc = p9_create_common(bufp, size, P9_TWALK); |
531b1094 LI |
714 | if (IS_ERR(fc)) |
715 | goto error; | |
716 | ||
bd238fb4 LI |
717 | p9_put_int32(bufp, fid, &fc->params.twalk.fid); |
718 | p9_put_int32(bufp, newfid, &fc->params.twalk.newfid); | |
719 | p9_put_int16(bufp, nwname, &fc->params.twalk.nwname); | |
531b1094 | 720 | for (i = 0; i < nwname; i++) { |
bd238fb4 | 721 | p9_put_str(bufp, wnames[i], &fc->params.twalk.wnames[i]); |
531b1094 LI |
722 | } |
723 | ||
724 | if (buf_check_overflow(bufp)) { | |
725 | kfree(fc); | |
726 | fc = ERR_PTR(-ENOMEM); | |
727 | } | |
bd238fb4 | 728 | error: |
531b1094 LI |
729 | return fc; |
730 | } | |
bd238fb4 | 731 | EXPORT_SYMBOL(p9_create_twalk); |
531b1094 | 732 | |
ee443996 EVH |
733 | /** |
734 | * p9_create_topen - allocates and creates a T_OPEN request | |
735 | * @fid: handle we are trying to open | |
736 | * @mode: what mode we are trying to open the file in | |
737 | * | |
738 | */ | |
739 | ||
bd238fb4 | 740 | struct p9_fcall *p9_create_topen(u32 fid, u8 mode) |
531b1094 LI |
741 | { |
742 | int size; | |
bd238fb4 | 743 | struct p9_fcall *fc; |
531b1094 LI |
744 | struct cbuf buffer; |
745 | struct cbuf *bufp = &buffer; | |
746 | ||
747 | size = 4 + 1; /* fid[4] mode[1] */ | |
bd238fb4 | 748 | fc = p9_create_common(bufp, size, P9_TOPEN); |
531b1094 LI |
749 | if (IS_ERR(fc)) |
750 | goto error; | |
751 | ||
bd238fb4 LI |
752 | p9_put_int32(bufp, fid, &fc->params.topen.fid); |
753 | p9_put_int8(bufp, mode, &fc->params.topen.mode); | |
531b1094 LI |
754 | |
755 | if (buf_check_overflow(bufp)) { | |
756 | kfree(fc); | |
757 | fc = ERR_PTR(-ENOMEM); | |
758 | } | |
bd238fb4 | 759 | error: |
531b1094 LI |
760 | return fc; |
761 | } | |
bd238fb4 | 762 | EXPORT_SYMBOL(p9_create_topen); |
531b1094 | 763 | |
ee443996 EVH |
764 | /** |
765 | * p9_create_tcreate - allocates and creates a T_CREATE request | |
766 | * @fid: handle of directory we are trying to create in | |
767 | * @name: name of the file we are trying to create | |
768 | * @perm: permissions for the file we are trying to create | |
769 | * @mode: what mode we are trying to open the file in | |
770 | * @extension: 9p2000.u extension string (for special files) | |
771 | * @dotu: 9p2000.u enabled flag | |
772 | * | |
773 | * Note: Plan 9 create semantics include opening the resulting file | |
774 | * which is why mode is included. | |
775 | */ | |
776 | ||
bd238fb4 LI |
777 | struct p9_fcall *p9_create_tcreate(u32 fid, char *name, u32 perm, u8 mode, |
778 | char *extension, int dotu) | |
531b1094 LI |
779 | { |
780 | int size; | |
bd238fb4 | 781 | struct p9_fcall *fc; |
531b1094 LI |
782 | struct cbuf buffer; |
783 | struct cbuf *bufp = &buffer; | |
784 | ||
bd238fb4 LI |
785 | /* fid[4] name[s] perm[4] mode[1] */ |
786 | size = 4 + 2 + strlen(name) + 4 + 1; | |
787 | if (dotu) { | |
4c90c68a RR |
788 | size += 2 + /* extension[s] */ |
789 | (extension == NULL ? 0 : strlen(extension)); | |
790 | } | |
16cce6d2 | 791 | |
bd238fb4 | 792 | fc = p9_create_common(bufp, size, P9_TCREATE); |
531b1094 LI |
793 | if (IS_ERR(fc)) |
794 | goto error; | |
795 | ||
bd238fb4 LI |
796 | p9_put_int32(bufp, fid, &fc->params.tcreate.fid); |
797 | p9_put_str(bufp, name, &fc->params.tcreate.name); | |
798 | p9_put_int32(bufp, perm, &fc->params.tcreate.perm); | |
799 | p9_put_int8(bufp, mode, &fc->params.tcreate.mode); | |
800 | if (dotu) | |
801 | p9_put_str(bufp, extension, &fc->params.tcreate.extension); | |
531b1094 LI |
802 | |
803 | if (buf_check_overflow(bufp)) { | |
804 | kfree(fc); | |
805 | fc = ERR_PTR(-ENOMEM); | |
806 | } | |
bd238fb4 | 807 | error: |
531b1094 LI |
808 | return fc; |
809 | } | |
bd238fb4 | 810 | EXPORT_SYMBOL(p9_create_tcreate); |
531b1094 | 811 | |
ee443996 EVH |
812 | /** |
813 | * p9_create_tread - allocates and creates a T_READ request | |
814 | * @fid: handle of the file we are trying to read | |
815 | * @offset: offset to start reading from | |
816 | * @count: how many bytes to read | |
817 | */ | |
818 | ||
bd238fb4 | 819 | struct p9_fcall *p9_create_tread(u32 fid, u64 offset, u32 count) |
531b1094 LI |
820 | { |
821 | int size; | |
bd238fb4 | 822 | struct p9_fcall *fc; |
531b1094 LI |
823 | struct cbuf buffer; |
824 | struct cbuf *bufp = &buffer; | |
825 | ||
826 | size = 4 + 8 + 4; /* fid[4] offset[8] count[4] */ | |
bd238fb4 LI |
827 | fc = p9_create_common(bufp, size, P9_TREAD); |
828 | if (IS_ERR(fc)) | |
829 | goto error; | |
830 | ||
831 | p9_put_int32(bufp, fid, &fc->params.tread.fid); | |
832 | p9_put_int64(bufp, offset, &fc->params.tread.offset); | |
833 | p9_put_int32(bufp, count, &fc->params.tread.count); | |
834 | ||
835 | if (buf_check_overflow(bufp)) { | |
836 | kfree(fc); | |
837 | fc = ERR_PTR(-ENOMEM); | |
838 | } | |
839 | error: | |
840 | return fc; | |
841 | } | |
842 | EXPORT_SYMBOL(p9_create_tread); | |
843 | ||
ee443996 EVH |
844 | /** |
845 | * p9_create_twrite - allocates and creates a T_WRITE request from the kernel | |
846 | * @fid: handle of the file we are trying to write | |
847 | * @offset: offset to start writing at | |
848 | * @count: how many bytes to write | |
849 | * @data: data to write | |
850 | * | |
851 | * This function will create a requst with data buffers from the kernel | |
852 | * such as the page cache. | |
853 | */ | |
854 | ||
bd238fb4 LI |
855 | struct p9_fcall *p9_create_twrite(u32 fid, u64 offset, u32 count, |
856 | const char *data) | |
857 | { | |
858 | int size, err; | |
859 | struct p9_fcall *fc; | |
860 | struct cbuf buffer; | |
861 | struct cbuf *bufp = &buffer; | |
862 | ||
863 | /* fid[4] offset[8] count[4] data[count] */ | |
864 | size = 4 + 8 + 4 + count; | |
865 | fc = p9_create_common(bufp, size, P9_TWRITE); | |
531b1094 LI |
866 | if (IS_ERR(fc)) |
867 | goto error; | |
868 | ||
bd238fb4 LI |
869 | p9_put_int32(bufp, fid, &fc->params.twrite.fid); |
870 | p9_put_int64(bufp, offset, &fc->params.twrite.offset); | |
871 | p9_put_int32(bufp, count, &fc->params.twrite.count); | |
872 | err = p9_put_data(bufp, data, count, &fc->params.twrite.data); | |
873 | if (err) { | |
874 | kfree(fc); | |
875 | fc = ERR_PTR(err); | |
bbe06f6b | 876 | goto error; |
bd238fb4 | 877 | } |
531b1094 LI |
878 | |
879 | if (buf_check_overflow(bufp)) { | |
880 | kfree(fc); | |
881 | fc = ERR_PTR(-ENOMEM); | |
882 | } | |
bd238fb4 | 883 | error: |
531b1094 LI |
884 | return fc; |
885 | } | |
bd238fb4 | 886 | EXPORT_SYMBOL(p9_create_twrite); |
531b1094 | 887 | |
ee443996 EVH |
888 | /** |
889 | * p9_create_twrite_u - allocates and creates a T_WRITE request from userspace | |
890 | * @fid: handle of the file we are trying to write | |
891 | * @offset: offset to start writing at | |
892 | * @count: how many bytes to write | |
893 | * @data: data to write | |
894 | * | |
895 | * This function will create a request with data buffers from userspace | |
896 | */ | |
897 | ||
bd238fb4 LI |
898 | struct p9_fcall *p9_create_twrite_u(u32 fid, u64 offset, u32 count, |
899 | const char __user *data) | |
531b1094 LI |
900 | { |
901 | int size, err; | |
bd238fb4 | 902 | struct p9_fcall *fc; |
531b1094 LI |
903 | struct cbuf buffer; |
904 | struct cbuf *bufp = &buffer; | |
905 | ||
bd238fb4 LI |
906 | /* fid[4] offset[8] count[4] data[count] */ |
907 | size = 4 + 8 + 4 + count; | |
908 | fc = p9_create_common(bufp, size, P9_TWRITE); | |
531b1094 LI |
909 | if (IS_ERR(fc)) |
910 | goto error; | |
911 | ||
bd238fb4 LI |
912 | p9_put_int32(bufp, fid, &fc->params.twrite.fid); |
913 | p9_put_int64(bufp, offset, &fc->params.twrite.offset); | |
914 | p9_put_int32(bufp, count, &fc->params.twrite.count); | |
915 | err = p9_put_user_data(bufp, data, count, &fc->params.twrite.data); | |
531b1094 LI |
916 | if (err) { |
917 | kfree(fc); | |
918 | fc = ERR_PTR(err); | |
02881d94 | 919 | goto error; |
531b1094 LI |
920 | } |
921 | ||
922 | if (buf_check_overflow(bufp)) { | |
923 | kfree(fc); | |
924 | fc = ERR_PTR(-ENOMEM); | |
925 | } | |
bd238fb4 | 926 | error: |
531b1094 LI |
927 | return fc; |
928 | } | |
bd238fb4 | 929 | EXPORT_SYMBOL(p9_create_twrite_u); |
531b1094 | 930 | |
ee443996 EVH |
931 | /** |
932 | * p9_create_tclunk - allocate a request to forget about a file handle | |
933 | * @fid: handle of the file we closing or forgetting about | |
934 | * | |
935 | * clunk is used both to close open files and to discard transient handles | |
936 | * which may be created during meta-data operations and hierarchy traversal. | |
937 | */ | |
938 | ||
bd238fb4 | 939 | struct p9_fcall *p9_create_tclunk(u32 fid) |
531b1094 LI |
940 | { |
941 | int size; | |
bd238fb4 | 942 | struct p9_fcall *fc; |
531b1094 LI |
943 | struct cbuf buffer; |
944 | struct cbuf *bufp = &buffer; | |
945 | ||
946 | size = 4; /* fid[4] */ | |
bd238fb4 | 947 | fc = p9_create_common(bufp, size, P9_TCLUNK); |
531b1094 LI |
948 | if (IS_ERR(fc)) |
949 | goto error; | |
950 | ||
bd238fb4 | 951 | p9_put_int32(bufp, fid, &fc->params.tclunk.fid); |
531b1094 LI |
952 | |
953 | if (buf_check_overflow(bufp)) { | |
954 | kfree(fc); | |
955 | fc = ERR_PTR(-ENOMEM); | |
956 | } | |
bd238fb4 | 957 | error: |
531b1094 LI |
958 | return fc; |
959 | } | |
bd238fb4 | 960 | EXPORT_SYMBOL(p9_create_tclunk); |
531b1094 | 961 | |
ee443996 EVH |
962 | /** |
963 | * p9_create_tremove - allocate and create a request to remove a file | |
964 | * @fid: handle of the file or directory we are removing | |
965 | * | |
966 | */ | |
967 | ||
bd238fb4 | 968 | struct p9_fcall *p9_create_tremove(u32 fid) |
531b1094 LI |
969 | { |
970 | int size; | |
bd238fb4 | 971 | struct p9_fcall *fc; |
531b1094 LI |
972 | struct cbuf buffer; |
973 | struct cbuf *bufp = &buffer; | |
974 | ||
975 | size = 4; /* fid[4] */ | |
bd238fb4 | 976 | fc = p9_create_common(bufp, size, P9_TREMOVE); |
531b1094 LI |
977 | if (IS_ERR(fc)) |
978 | goto error; | |
979 | ||
bd238fb4 | 980 | p9_put_int32(bufp, fid, &fc->params.tremove.fid); |
531b1094 LI |
981 | |
982 | if (buf_check_overflow(bufp)) { | |
983 | kfree(fc); | |
984 | fc = ERR_PTR(-ENOMEM); | |
985 | } | |
bd238fb4 | 986 | error: |
531b1094 LI |
987 | return fc; |
988 | } | |
bd238fb4 | 989 | EXPORT_SYMBOL(p9_create_tremove); |
531b1094 | 990 | |
ee443996 EVH |
991 | /** |
992 | * p9_create_tstat - allocate and populate a request for attributes | |
993 | * @fid: handle of the file or directory we are trying to get the attributes of | |
994 | * | |
995 | */ | |
996 | ||
bd238fb4 | 997 | struct p9_fcall *p9_create_tstat(u32 fid) |
531b1094 LI |
998 | { |
999 | int size; | |
bd238fb4 | 1000 | struct p9_fcall *fc; |
531b1094 LI |
1001 | struct cbuf buffer; |
1002 | struct cbuf *bufp = &buffer; | |
1003 | ||
1004 | size = 4; /* fid[4] */ | |
bd238fb4 | 1005 | fc = p9_create_common(bufp, size, P9_TSTAT); |
531b1094 LI |
1006 | if (IS_ERR(fc)) |
1007 | goto error; | |
1008 | ||
bd238fb4 | 1009 | p9_put_int32(bufp, fid, &fc->params.tstat.fid); |
531b1094 LI |
1010 | |
1011 | if (buf_check_overflow(bufp)) { | |
1012 | kfree(fc); | |
1013 | fc = ERR_PTR(-ENOMEM); | |
1014 | } | |
bd238fb4 | 1015 | error: |
531b1094 LI |
1016 | return fc; |
1017 | } | |
bd238fb4 | 1018 | EXPORT_SYMBOL(p9_create_tstat); |
531b1094 | 1019 | |
ee443996 EVH |
1020 | /** |
1021 | * p9_create_tstat - allocate and populate a request to change attributes | |
1022 | * @fid: handle of the file or directory we are trying to change | |
1023 | * @wstat: &p9_stat structure with attributes we wish to set | |
1024 | * @dotu: 9p2000.u enabled flag | |
1025 | * | |
1026 | */ | |
1027 | ||
bd238fb4 LI |
1028 | struct p9_fcall *p9_create_twstat(u32 fid, struct p9_wstat *wstat, |
1029 | int dotu) | |
531b1094 LI |
1030 | { |
1031 | int size, statsz; | |
bd238fb4 | 1032 | struct p9_fcall *fc; |
531b1094 LI |
1033 | struct cbuf buffer; |
1034 | struct cbuf *bufp = &buffer; | |
1035 | ||
bd238fb4 | 1036 | statsz = p9_size_wstat(wstat, dotu); |
531b1094 | 1037 | size = 4 + 2 + 2 + statsz; /* fid[4] stat[n] */ |
bd238fb4 | 1038 | fc = p9_create_common(bufp, size, P9_TWSTAT); |
531b1094 LI |
1039 | if (IS_ERR(fc)) |
1040 | goto error; | |
1041 | ||
bd238fb4 | 1042 | p9_put_int32(bufp, fid, &fc->params.twstat.fid); |
531b1094 | 1043 | buf_put_int16(bufp, statsz + 2); |
bd238fb4 | 1044 | p9_put_wstat(bufp, wstat, &fc->params.twstat.stat, statsz, dotu); |
531b1094 LI |
1045 | |
1046 | if (buf_check_overflow(bufp)) { | |
1047 | kfree(fc); | |
1048 | fc = ERR_PTR(-ENOMEM); | |
1049 | } | |
bd238fb4 | 1050 | error: |
531b1094 | 1051 | return fc; |
b8cf945b | 1052 | } |
bd238fb4 | 1053 | EXPORT_SYMBOL(p9_create_twstat); |
ee443996 | 1054 |