diff options
Diffstat (limited to 'fs/nfsd/nfs4xdr.c')
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 2536 |
1 files changed, 2536 insertions, 0 deletions
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c new file mode 100644 index 000000000000..36a058a112d5 --- /dev/null +++ b/fs/nfsd/nfs4xdr.c | |||
@@ -0,0 +1,2536 @@ | |||
1 | /* | ||
2 | * fs/nfs/nfs4xdr.c | ||
3 | * | ||
4 | * Server-side XDR for NFSv4 | ||
5 | * | ||
6 | * Copyright (c) 2002 The Regents of the University of Michigan. | ||
7 | * All rights reserved. | ||
8 | * | ||
9 | * Kendrick Smith <kmsmith@umich.edu> | ||
10 | * Andy Adamson <andros@umich.edu> | ||
11 | * | ||
12 | * Redistribution and use in source and binary forms, with or without | ||
13 | * modification, are permitted provided that the following conditions | ||
14 | * are met: | ||
15 | * | ||
16 | * 1. Redistributions of source code must retain the above copyright | ||
17 | * notice, this list of conditions and the following disclaimer. | ||
18 | * 2. Redistributions in binary form must reproduce the above copyright | ||
19 | * notice, this list of conditions and the following disclaimer in the | ||
20 | * documentation and/or other materials provided with the distribution. | ||
21 | * 3. Neither the name of the University nor the names of its | ||
22 | * contributors may be used to endorse or promote products derived | ||
23 | * from this software without specific prior written permission. | ||
24 | * | ||
25 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
26 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
27 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
28 | * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
29 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
30 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
31 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | ||
32 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
33 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
34 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
35 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
36 | * | ||
37 | * TODO: Neil Brown made the following observation: We currently | ||
38 | * initially reserve NFSD_BUFSIZE space on the transmit queue and | ||
39 | * never release any of that until the request is complete. | ||
40 | * It would be good to calculate a new maximum response size while | ||
41 | * decoding the COMPOUND, and call svc_reserve with this number | ||
42 | * at the end of nfs4svc_decode_compoundargs. | ||
43 | */ | ||
44 | |||
45 | #include <linux/param.h> | ||
46 | #include <linux/smp.h> | ||
47 | #include <linux/smp_lock.h> | ||
48 | #include <linux/fs.h> | ||
49 | #include <linux/namei.h> | ||
50 | #include <linux/vfs.h> | ||
51 | #include <linux/sunrpc/xdr.h> | ||
52 | #include <linux/sunrpc/svc.h> | ||
53 | #include <linux/sunrpc/clnt.h> | ||
54 | #include <linux/nfsd/nfsd.h> | ||
55 | #include <linux/nfsd/state.h> | ||
56 | #include <linux/nfsd/xdr4.h> | ||
57 | #include <linux/nfsd_idmap.h> | ||
58 | #include <linux/nfs4.h> | ||
59 | #include <linux/nfs4_acl.h> | ||
60 | |||
61 | #define NFSDDBG_FACILITY NFSDDBG_XDR | ||
62 | |||
63 | static int | ||
64 | check_filename(char *str, int len, int err) | ||
65 | { | ||
66 | int i; | ||
67 | |||
68 | if (len == 0) | ||
69 | return nfserr_inval; | ||
70 | if (isdotent(str, len)) | ||
71 | return err; | ||
72 | for (i = 0; i < len; i++) | ||
73 | if (str[i] == '/') | ||
74 | return err; | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | /* | ||
79 | * START OF "GENERIC" DECODE ROUTINES. | ||
80 | * These may look a little ugly since they are imported from a "generic" | ||
81 | * set of XDR encode/decode routines which are intended to be shared by | ||
82 | * all of our NFSv4 implementations (OpenBSD, MacOS X...). | ||
83 | * | ||
84 | * If the pain of reading these is too great, it should be a straightforward | ||
85 | * task to translate them into Linux-specific versions which are more | ||
86 | * consistent with the style used in NFSv2/v3... | ||
87 | */ | ||
88 | #define DECODE_HEAD \ | ||
89 | u32 *p; \ | ||
90 | int status | ||
91 | #define DECODE_TAIL \ | ||
92 | status = 0; \ | ||
93 | out: \ | ||
94 | return status; \ | ||
95 | xdr_error: \ | ||
96 | printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \ | ||
97 | status = nfserr_bad_xdr; \ | ||
98 | goto out | ||
99 | |||
100 | #define READ32(x) (x) = ntohl(*p++) | ||
101 | #define READ64(x) do { \ | ||
102 | (x) = (u64)ntohl(*p++) << 32; \ | ||
103 | (x) |= ntohl(*p++); \ | ||
104 | } while (0) | ||
105 | #define READTIME(x) do { \ | ||
106 | p++; \ | ||
107 | (x) = ntohl(*p++); \ | ||
108 | p++; \ | ||
109 | } while (0) | ||
110 | #define READMEM(x,nbytes) do { \ | ||
111 | x = (char *)p; \ | ||
112 | p += XDR_QUADLEN(nbytes); \ | ||
113 | } while (0) | ||
114 | #define SAVEMEM(x,nbytes) do { \ | ||
115 | if (!(x = (p==argp->tmp || p == argp->tmpp) ? \ | ||
116 | savemem(argp, p, nbytes) : \ | ||
117 | (char *)p)) { \ | ||
118 | printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \ | ||
119 | goto xdr_error; \ | ||
120 | } \ | ||
121 | p += XDR_QUADLEN(nbytes); \ | ||
122 | } while (0) | ||
123 | #define COPYMEM(x,nbytes) do { \ | ||
124 | memcpy((x), p, nbytes); \ | ||
125 | p += XDR_QUADLEN(nbytes); \ | ||
126 | } while (0) | ||
127 | |||
128 | /* READ_BUF, read_buf(): nbytes must be <= PAGE_SIZE */ | ||
129 | #define READ_BUF(nbytes) do { \ | ||
130 | if (nbytes <= (u32)((char *)argp->end - (char *)argp->p)) { \ | ||
131 | p = argp->p; \ | ||
132 | argp->p += XDR_QUADLEN(nbytes); \ | ||
133 | } else if (!(p = read_buf(argp, nbytes))) { \ | ||
134 | printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); \ | ||
135 | goto xdr_error; \ | ||
136 | } \ | ||
137 | } while (0) | ||
138 | |||
139 | u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes) | ||
140 | { | ||
141 | /* We want more bytes than seem to be available. | ||
142 | * Maybe we need a new page, maybe we have just run out | ||
143 | */ | ||
144 | int avail = (char*)argp->end - (char*)argp->p; | ||
145 | u32 *p; | ||
146 | if (avail + argp->pagelen < nbytes) | ||
147 | return NULL; | ||
148 | if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */ | ||
149 | return NULL; | ||
150 | /* ok, we can do it with the current plus the next page */ | ||
151 | if (nbytes <= sizeof(argp->tmp)) | ||
152 | p = argp->tmp; | ||
153 | else { | ||
154 | if (argp->tmpp) | ||
155 | kfree(argp->tmpp); | ||
156 | p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL); | ||
157 | if (!p) | ||
158 | return NULL; | ||
159 | |||
160 | } | ||
161 | memcpy(p, argp->p, avail); | ||
162 | /* step to next page */ | ||
163 | argp->p = page_address(argp->pagelist[0]); | ||
164 | argp->pagelist++; | ||
165 | if (argp->pagelen < PAGE_SIZE) { | ||
166 | argp->end = p + (argp->pagelen>>2); | ||
167 | argp->pagelen = 0; | ||
168 | } else { | ||
169 | argp->end = p + (PAGE_SIZE>>2); | ||
170 | argp->pagelen -= PAGE_SIZE; | ||
171 | } | ||
172 | memcpy(((char*)p)+avail, argp->p, (nbytes - avail)); | ||
173 | argp->p += XDR_QUADLEN(nbytes - avail); | ||
174 | return p; | ||
175 | } | ||
176 | |||
177 | static int | ||
178 | defer_free(struct nfsd4_compoundargs *argp, | ||
179 | void (*release)(const void *), void *p) | ||
180 | { | ||
181 | struct tmpbuf *tb; | ||
182 | |||
183 | tb = kmalloc(sizeof(*tb), GFP_KERNEL); | ||
184 | if (!tb) | ||
185 | return -ENOMEM; | ||
186 | tb->buf = p; | ||
187 | tb->release = release; | ||
188 | tb->next = argp->to_free; | ||
189 | argp->to_free = tb; | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes) | ||
194 | { | ||
195 | void *new = NULL; | ||
196 | if (p == argp->tmp) { | ||
197 | new = kmalloc(nbytes, GFP_KERNEL); | ||
198 | if (!new) return NULL; | ||
199 | p = new; | ||
200 | memcpy(p, argp->tmp, nbytes); | ||
201 | } else { | ||
202 | if (p != argp->tmpp) | ||
203 | BUG(); | ||
204 | argp->tmpp = NULL; | ||
205 | } | ||
206 | if (defer_free(argp, kfree, p)) { | ||
207 | kfree(new); | ||
208 | return NULL; | ||
209 | } else | ||
210 | return (char *)p; | ||
211 | } | ||
212 | |||
213 | |||
214 | static int | ||
215 | nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval) | ||
216 | { | ||
217 | u32 bmlen; | ||
218 | DECODE_HEAD; | ||
219 | |||
220 | bmval[0] = 0; | ||
221 | bmval[1] = 0; | ||
222 | |||
223 | READ_BUF(4); | ||
224 | READ32(bmlen); | ||
225 | if (bmlen > 1000) | ||
226 | goto xdr_error; | ||
227 | |||
228 | READ_BUF(bmlen << 2); | ||
229 | if (bmlen > 0) | ||
230 | READ32(bmval[0]); | ||
231 | if (bmlen > 1) | ||
232 | READ32(bmval[1]); | ||
233 | |||
234 | DECODE_TAIL; | ||
235 | } | ||
236 | |||
237 | static int | ||
238 | nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr, | ||
239 | struct nfs4_acl **acl) | ||
240 | { | ||
241 | int expected_len, len = 0; | ||
242 | u32 dummy32; | ||
243 | char *buf; | ||
244 | |||
245 | DECODE_HEAD; | ||
246 | iattr->ia_valid = 0; | ||
247 | if ((status = nfsd4_decode_bitmap(argp, bmval))) | ||
248 | return status; | ||
249 | |||
250 | /* | ||
251 | * According to spec, unsupported attributes return ERR_NOTSUPP; | ||
252 | * read-only attributes return ERR_INVAL. | ||
253 | */ | ||
254 | if ((bmval[0] & ~NFSD_SUPPORTED_ATTRS_WORD0) || (bmval[1] & ~NFSD_SUPPORTED_ATTRS_WORD1)) | ||
255 | return nfserr_attrnotsupp; | ||
256 | if ((bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0) || (bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1)) | ||
257 | return nfserr_inval; | ||
258 | |||
259 | READ_BUF(4); | ||
260 | READ32(expected_len); | ||
261 | |||
262 | if (bmval[0] & FATTR4_WORD0_SIZE) { | ||
263 | READ_BUF(8); | ||
264 | len += 8; | ||
265 | READ64(iattr->ia_size); | ||
266 | iattr->ia_valid |= ATTR_SIZE; | ||
267 | } | ||
268 | if (bmval[0] & FATTR4_WORD0_ACL) { | ||
269 | int nace, i; | ||
270 | struct nfs4_ace ace; | ||
271 | |||
272 | READ_BUF(4); len += 4; | ||
273 | READ32(nace); | ||
274 | |||
275 | *acl = nfs4_acl_new(); | ||
276 | if (*acl == NULL) { | ||
277 | status = -ENOMEM; | ||
278 | goto out_nfserr; | ||
279 | } | ||
280 | defer_free(argp, (void (*)(const void *))nfs4_acl_free, *acl); | ||
281 | |||
282 | for (i = 0; i < nace; i++) { | ||
283 | READ_BUF(16); len += 16; | ||
284 | READ32(ace.type); | ||
285 | READ32(ace.flag); | ||
286 | READ32(ace.access_mask); | ||
287 | READ32(dummy32); | ||
288 | READ_BUF(dummy32); | ||
289 | len += XDR_QUADLEN(dummy32) << 2; | ||
290 | READMEM(buf, dummy32); | ||
291 | ace.whotype = nfs4_acl_get_whotype(buf, dummy32); | ||
292 | status = 0; | ||
293 | if (ace.whotype != NFS4_ACL_WHO_NAMED) | ||
294 | ace.who = 0; | ||
295 | else if (ace.flag & NFS4_ACE_IDENTIFIER_GROUP) | ||
296 | status = nfsd_map_name_to_gid(argp->rqstp, | ||
297 | buf, dummy32, &ace.who); | ||
298 | else | ||
299 | status = nfsd_map_name_to_uid(argp->rqstp, | ||
300 | buf, dummy32, &ace.who); | ||
301 | if (status) | ||
302 | goto out_nfserr; | ||
303 | if (nfs4_acl_add_ace(*acl, ace.type, ace.flag, | ||
304 | ace.access_mask, ace.whotype, ace.who) != 0) { | ||
305 | status = -ENOMEM; | ||
306 | goto out_nfserr; | ||
307 | } | ||
308 | } | ||
309 | } else | ||
310 | *acl = NULL; | ||
311 | if (bmval[1] & FATTR4_WORD1_MODE) { | ||
312 | READ_BUF(4); | ||
313 | len += 4; | ||
314 | READ32(iattr->ia_mode); | ||
315 | iattr->ia_mode &= (S_IFMT | S_IALLUGO); | ||
316 | iattr->ia_valid |= ATTR_MODE; | ||
317 | } | ||
318 | if (bmval[1] & FATTR4_WORD1_OWNER) { | ||
319 | READ_BUF(4); | ||
320 | len += 4; | ||
321 | READ32(dummy32); | ||
322 | READ_BUF(dummy32); | ||
323 | len += (XDR_QUADLEN(dummy32) << 2); | ||
324 | READMEM(buf, dummy32); | ||
325 | if ((status = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &iattr->ia_uid))) | ||
326 | goto out_nfserr; | ||
327 | iattr->ia_valid |= ATTR_UID; | ||
328 | } | ||
329 | if (bmval[1] & FATTR4_WORD1_OWNER_GROUP) { | ||
330 | READ_BUF(4); | ||
331 | len += 4; | ||
332 | READ32(dummy32); | ||
333 | READ_BUF(dummy32); | ||
334 | len += (XDR_QUADLEN(dummy32) << 2); | ||
335 | READMEM(buf, dummy32); | ||
336 | if ((status = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &iattr->ia_gid))) | ||
337 | goto out_nfserr; | ||
338 | iattr->ia_valid |= ATTR_GID; | ||
339 | } | ||
340 | if (bmval[1] & FATTR4_WORD1_TIME_ACCESS_SET) { | ||
341 | READ_BUF(4); | ||
342 | len += 4; | ||
343 | READ32(dummy32); | ||
344 | switch (dummy32) { | ||
345 | case NFS4_SET_TO_CLIENT_TIME: | ||
346 | /* We require the high 32 bits of 'seconds' to be 0, and we ignore | ||
347 | all 32 bits of 'nseconds'. */ | ||
348 | READ_BUF(12); | ||
349 | len += 12; | ||
350 | READ32(dummy32); | ||
351 | if (dummy32) | ||
352 | return nfserr_inval; | ||
353 | READ32(iattr->ia_atime.tv_sec); | ||
354 | READ32(iattr->ia_atime.tv_nsec); | ||
355 | if (iattr->ia_atime.tv_nsec >= (u32)1000000000) | ||
356 | return nfserr_inval; | ||
357 | iattr->ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); | ||
358 | break; | ||
359 | case NFS4_SET_TO_SERVER_TIME: | ||
360 | iattr->ia_valid |= ATTR_ATIME; | ||
361 | break; | ||
362 | default: | ||
363 | goto xdr_error; | ||
364 | } | ||
365 | } | ||
366 | if (bmval[1] & FATTR4_WORD1_TIME_METADATA) { | ||
367 | /* We require the high 32 bits of 'seconds' to be 0, and we ignore | ||
368 | all 32 bits of 'nseconds'. */ | ||
369 | READ_BUF(12); | ||
370 | len += 12; | ||
371 | READ32(dummy32); | ||
372 | if (dummy32) | ||
373 | return nfserr_inval; | ||
374 | READ32(iattr->ia_ctime.tv_sec); | ||
375 | READ32(iattr->ia_ctime.tv_nsec); | ||
376 | if (iattr->ia_ctime.tv_nsec >= (u32)1000000000) | ||
377 | return nfserr_inval; | ||
378 | iattr->ia_valid |= ATTR_CTIME; | ||
379 | } | ||
380 | if (bmval[1] & FATTR4_WORD1_TIME_MODIFY_SET) { | ||
381 | READ_BUF(4); | ||
382 | len += 4; | ||
383 | READ32(dummy32); | ||
384 | switch (dummy32) { | ||
385 | case NFS4_SET_TO_CLIENT_TIME: | ||
386 | /* We require the high 32 bits of 'seconds' to be 0, and we ignore | ||
387 | all 32 bits of 'nseconds'. */ | ||
388 | READ_BUF(12); | ||
389 | len += 12; | ||
390 | READ32(dummy32); | ||
391 | if (dummy32) | ||
392 | return nfserr_inval; | ||
393 | READ32(iattr->ia_mtime.tv_sec); | ||
394 | READ32(iattr->ia_mtime.tv_nsec); | ||
395 | if (iattr->ia_mtime.tv_nsec >= (u32)1000000000) | ||
396 | return nfserr_inval; | ||
397 | iattr->ia_valid |= (ATTR_MTIME | ATTR_MTIME_SET); | ||
398 | break; | ||
399 | case NFS4_SET_TO_SERVER_TIME: | ||
400 | iattr->ia_valid |= ATTR_MTIME; | ||
401 | break; | ||
402 | default: | ||
403 | goto xdr_error; | ||
404 | } | ||
405 | } | ||
406 | if (len != expected_len) | ||
407 | goto xdr_error; | ||
408 | |||
409 | DECODE_TAIL; | ||
410 | |||
411 | out_nfserr: | ||
412 | status = nfserrno(status); | ||
413 | goto out; | ||
414 | } | ||
415 | |||
416 | static int | ||
417 | nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access) | ||
418 | { | ||
419 | DECODE_HEAD; | ||
420 | |||
421 | READ_BUF(4); | ||
422 | READ32(access->ac_req_access); | ||
423 | |||
424 | DECODE_TAIL; | ||
425 | } | ||
426 | |||
427 | static int | ||
428 | nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close) | ||
429 | { | ||
430 | DECODE_HEAD; | ||
431 | |||
432 | close->cl_stateowner = NULL; | ||
433 | READ_BUF(4 + sizeof(stateid_t)); | ||
434 | READ32(close->cl_seqid); | ||
435 | READ32(close->cl_stateid.si_generation); | ||
436 | COPYMEM(&close->cl_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
437 | |||
438 | DECODE_TAIL; | ||
439 | } | ||
440 | |||
441 | |||
442 | static int | ||
443 | nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit) | ||
444 | { | ||
445 | DECODE_HEAD; | ||
446 | |||
447 | READ_BUF(12); | ||
448 | READ64(commit->co_offset); | ||
449 | READ32(commit->co_count); | ||
450 | |||
451 | DECODE_TAIL; | ||
452 | } | ||
453 | |||
454 | static int | ||
455 | nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create) | ||
456 | { | ||
457 | DECODE_HEAD; | ||
458 | |||
459 | READ_BUF(4); | ||
460 | READ32(create->cr_type); | ||
461 | switch (create->cr_type) { | ||
462 | case NF4LNK: | ||
463 | READ_BUF(4); | ||
464 | READ32(create->cr_linklen); | ||
465 | READ_BUF(create->cr_linklen); | ||
466 | SAVEMEM(create->cr_linkname, create->cr_linklen); | ||
467 | break; | ||
468 | case NF4BLK: | ||
469 | case NF4CHR: | ||
470 | READ_BUF(8); | ||
471 | READ32(create->cr_specdata1); | ||
472 | READ32(create->cr_specdata2); | ||
473 | break; | ||
474 | case NF4SOCK: | ||
475 | case NF4FIFO: | ||
476 | case NF4DIR: | ||
477 | default: | ||
478 | break; | ||
479 | } | ||
480 | |||
481 | READ_BUF(4); | ||
482 | READ32(create->cr_namelen); | ||
483 | READ_BUF(create->cr_namelen); | ||
484 | SAVEMEM(create->cr_name, create->cr_namelen); | ||
485 | if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval))) | ||
486 | return status; | ||
487 | |||
488 | if ((status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr, &create->cr_acl))) | ||
489 | goto out; | ||
490 | |||
491 | DECODE_TAIL; | ||
492 | } | ||
493 | |||
494 | static inline int | ||
495 | nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr) | ||
496 | { | ||
497 | DECODE_HEAD; | ||
498 | |||
499 | READ_BUF(sizeof(stateid_t)); | ||
500 | READ32(dr->dr_stateid.si_generation); | ||
501 | COPYMEM(&dr->dr_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
502 | |||
503 | DECODE_TAIL; | ||
504 | } | ||
505 | |||
506 | static inline int | ||
507 | nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, struct nfsd4_getattr *getattr) | ||
508 | { | ||
509 | return nfsd4_decode_bitmap(argp, getattr->ga_bmval); | ||
510 | } | ||
511 | |||
512 | static int | ||
513 | nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link) | ||
514 | { | ||
515 | DECODE_HEAD; | ||
516 | |||
517 | READ_BUF(4); | ||
518 | READ32(link->li_namelen); | ||
519 | READ_BUF(link->li_namelen); | ||
520 | SAVEMEM(link->li_name, link->li_namelen); | ||
521 | if ((status = check_filename(link->li_name, link->li_namelen, nfserr_inval))) | ||
522 | return status; | ||
523 | |||
524 | DECODE_TAIL; | ||
525 | } | ||
526 | |||
527 | static int | ||
528 | nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock) | ||
529 | { | ||
530 | DECODE_HEAD; | ||
531 | |||
532 | lock->lk_stateowner = NULL; | ||
533 | /* | ||
534 | * type, reclaim(boolean), offset, length, new_lock_owner(boolean) | ||
535 | */ | ||
536 | READ_BUF(28); | ||
537 | READ32(lock->lk_type); | ||
538 | if ((lock->lk_type < NFS4_READ_LT) || (lock->lk_type > NFS4_WRITEW_LT)) | ||
539 | goto xdr_error; | ||
540 | READ32(lock->lk_reclaim); | ||
541 | READ64(lock->lk_offset); | ||
542 | READ64(lock->lk_length); | ||
543 | READ32(lock->lk_is_new); | ||
544 | |||
545 | if (lock->lk_is_new) { | ||
546 | READ_BUF(36); | ||
547 | READ32(lock->lk_new_open_seqid); | ||
548 | READ32(lock->lk_new_open_stateid.si_generation); | ||
549 | |||
550 | COPYMEM(&lock->lk_new_open_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
551 | READ32(lock->lk_new_lock_seqid); | ||
552 | COPYMEM(&lock->lk_new_clientid, sizeof(clientid_t)); | ||
553 | READ32(lock->lk_new_owner.len); | ||
554 | READ_BUF(lock->lk_new_owner.len); | ||
555 | READMEM(lock->lk_new_owner.data, lock->lk_new_owner.len); | ||
556 | } else { | ||
557 | READ_BUF(20); | ||
558 | READ32(lock->lk_old_lock_stateid.si_generation); | ||
559 | COPYMEM(&lock->lk_old_lock_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
560 | READ32(lock->lk_old_lock_seqid); | ||
561 | } | ||
562 | |||
563 | DECODE_TAIL; | ||
564 | } | ||
565 | |||
566 | static int | ||
567 | nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, struct nfsd4_lockt *lockt) | ||
568 | { | ||
569 | DECODE_HEAD; | ||
570 | |||
571 | READ_BUF(32); | ||
572 | READ32(lockt->lt_type); | ||
573 | if((lockt->lt_type < NFS4_READ_LT) || (lockt->lt_type > NFS4_WRITEW_LT)) | ||
574 | goto xdr_error; | ||
575 | READ64(lockt->lt_offset); | ||
576 | READ64(lockt->lt_length); | ||
577 | COPYMEM(&lockt->lt_clientid, 8); | ||
578 | READ32(lockt->lt_owner.len); | ||
579 | READ_BUF(lockt->lt_owner.len); | ||
580 | READMEM(lockt->lt_owner.data, lockt->lt_owner.len); | ||
581 | |||
582 | DECODE_TAIL; | ||
583 | } | ||
584 | |||
585 | static int | ||
586 | nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku) | ||
587 | { | ||
588 | DECODE_HEAD; | ||
589 | |||
590 | locku->lu_stateowner = NULL; | ||
591 | READ_BUF(24 + sizeof(stateid_t)); | ||
592 | READ32(locku->lu_type); | ||
593 | if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT)) | ||
594 | goto xdr_error; | ||
595 | READ32(locku->lu_seqid); | ||
596 | READ32(locku->lu_stateid.si_generation); | ||
597 | COPYMEM(&locku->lu_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
598 | READ64(locku->lu_offset); | ||
599 | READ64(locku->lu_length); | ||
600 | |||
601 | DECODE_TAIL; | ||
602 | } | ||
603 | |||
604 | static int | ||
605 | nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup) | ||
606 | { | ||
607 | DECODE_HEAD; | ||
608 | |||
609 | READ_BUF(4); | ||
610 | READ32(lookup->lo_len); | ||
611 | READ_BUF(lookup->lo_len); | ||
612 | SAVEMEM(lookup->lo_name, lookup->lo_len); | ||
613 | if ((status = check_filename(lookup->lo_name, lookup->lo_len, nfserr_noent))) | ||
614 | return status; | ||
615 | |||
616 | DECODE_TAIL; | ||
617 | } | ||
618 | |||
619 | static int | ||
620 | nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) | ||
621 | { | ||
622 | DECODE_HEAD; | ||
623 | |||
624 | memset(open->op_bmval, 0, sizeof(open->op_bmval)); | ||
625 | open->op_iattr.ia_valid = 0; | ||
626 | open->op_stateowner = NULL; | ||
627 | |||
628 | /* seqid, share_access, share_deny, clientid, ownerlen */ | ||
629 | READ_BUF(16 + sizeof(clientid_t)); | ||
630 | READ32(open->op_seqid); | ||
631 | READ32(open->op_share_access); | ||
632 | READ32(open->op_share_deny); | ||
633 | COPYMEM(&open->op_clientid, sizeof(clientid_t)); | ||
634 | READ32(open->op_owner.len); | ||
635 | |||
636 | /* owner, open_flag */ | ||
637 | READ_BUF(open->op_owner.len + 4); | ||
638 | SAVEMEM(open->op_owner.data, open->op_owner.len); | ||
639 | READ32(open->op_create); | ||
640 | switch (open->op_create) { | ||
641 | case NFS4_OPEN_NOCREATE: | ||
642 | break; | ||
643 | case NFS4_OPEN_CREATE: | ||
644 | READ_BUF(4); | ||
645 | READ32(open->op_createmode); | ||
646 | switch (open->op_createmode) { | ||
647 | case NFS4_CREATE_UNCHECKED: | ||
648 | case NFS4_CREATE_GUARDED: | ||
649 | if ((status = nfsd4_decode_fattr(argp, open->op_bmval, &open->op_iattr, &open->op_acl))) | ||
650 | goto out; | ||
651 | break; | ||
652 | case NFS4_CREATE_EXCLUSIVE: | ||
653 | READ_BUF(8); | ||
654 | COPYMEM(open->op_verf.data, 8); | ||
655 | break; | ||
656 | default: | ||
657 | goto xdr_error; | ||
658 | } | ||
659 | break; | ||
660 | default: | ||
661 | goto xdr_error; | ||
662 | } | ||
663 | |||
664 | /* open_claim */ | ||
665 | READ_BUF(4); | ||
666 | READ32(open->op_claim_type); | ||
667 | switch (open->op_claim_type) { | ||
668 | case NFS4_OPEN_CLAIM_NULL: | ||
669 | case NFS4_OPEN_CLAIM_DELEGATE_PREV: | ||
670 | READ_BUF(4); | ||
671 | READ32(open->op_fname.len); | ||
672 | READ_BUF(open->op_fname.len); | ||
673 | SAVEMEM(open->op_fname.data, open->op_fname.len); | ||
674 | if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval))) | ||
675 | return status; | ||
676 | break; | ||
677 | case NFS4_OPEN_CLAIM_PREVIOUS: | ||
678 | READ_BUF(4); | ||
679 | READ32(open->op_delegate_type); | ||
680 | break; | ||
681 | case NFS4_OPEN_CLAIM_DELEGATE_CUR: | ||
682 | READ_BUF(sizeof(stateid_t) + 4); | ||
683 | COPYMEM(&open->op_delegate_stateid, sizeof(stateid_t)); | ||
684 | READ32(open->op_fname.len); | ||
685 | READ_BUF(open->op_fname.len); | ||
686 | SAVEMEM(open->op_fname.data, open->op_fname.len); | ||
687 | if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval))) | ||
688 | return status; | ||
689 | break; | ||
690 | default: | ||
691 | goto xdr_error; | ||
692 | } | ||
693 | |||
694 | DECODE_TAIL; | ||
695 | } | ||
696 | |||
697 | static int | ||
698 | nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_confirm *open_conf) | ||
699 | { | ||
700 | DECODE_HEAD; | ||
701 | |||
702 | open_conf->oc_stateowner = NULL; | ||
703 | READ_BUF(4 + sizeof(stateid_t)); | ||
704 | READ32(open_conf->oc_req_stateid.si_generation); | ||
705 | COPYMEM(&open_conf->oc_req_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
706 | READ32(open_conf->oc_seqid); | ||
707 | |||
708 | DECODE_TAIL; | ||
709 | } | ||
710 | |||
711 | static int | ||
712 | nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_downgrade *open_down) | ||
713 | { | ||
714 | DECODE_HEAD; | ||
715 | |||
716 | open_down->od_stateowner = NULL; | ||
717 | READ_BUF(12 + sizeof(stateid_t)); | ||
718 | READ32(open_down->od_stateid.si_generation); | ||
719 | COPYMEM(&open_down->od_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
720 | READ32(open_down->od_seqid); | ||
721 | READ32(open_down->od_share_access); | ||
722 | READ32(open_down->od_share_deny); | ||
723 | |||
724 | DECODE_TAIL; | ||
725 | } | ||
726 | |||
727 | static int | ||
728 | nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh) | ||
729 | { | ||
730 | DECODE_HEAD; | ||
731 | |||
732 | READ_BUF(4); | ||
733 | READ32(putfh->pf_fhlen); | ||
734 | if (putfh->pf_fhlen > NFS4_FHSIZE) | ||
735 | goto xdr_error; | ||
736 | READ_BUF(putfh->pf_fhlen); | ||
737 | SAVEMEM(putfh->pf_fhval, putfh->pf_fhlen); | ||
738 | |||
739 | DECODE_TAIL; | ||
740 | } | ||
741 | |||
742 | static int | ||
743 | nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read) | ||
744 | { | ||
745 | DECODE_HEAD; | ||
746 | |||
747 | READ_BUF(sizeof(stateid_t) + 12); | ||
748 | READ32(read->rd_stateid.si_generation); | ||
749 | COPYMEM(&read->rd_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
750 | READ64(read->rd_offset); | ||
751 | READ32(read->rd_length); | ||
752 | |||
753 | DECODE_TAIL; | ||
754 | } | ||
755 | |||
756 | static int | ||
757 | nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, struct nfsd4_readdir *readdir) | ||
758 | { | ||
759 | DECODE_HEAD; | ||
760 | |||
761 | READ_BUF(24); | ||
762 | READ64(readdir->rd_cookie); | ||
763 | COPYMEM(readdir->rd_verf.data, sizeof(readdir->rd_verf.data)); | ||
764 | READ32(readdir->rd_dircount); /* just in case you needed a useless field... */ | ||
765 | READ32(readdir->rd_maxcount); | ||
766 | if ((status = nfsd4_decode_bitmap(argp, readdir->rd_bmval))) | ||
767 | goto out; | ||
768 | |||
769 | DECODE_TAIL; | ||
770 | } | ||
771 | |||
772 | static int | ||
773 | nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove) | ||
774 | { | ||
775 | DECODE_HEAD; | ||
776 | |||
777 | READ_BUF(4); | ||
778 | READ32(remove->rm_namelen); | ||
779 | READ_BUF(remove->rm_namelen); | ||
780 | SAVEMEM(remove->rm_name, remove->rm_namelen); | ||
781 | if ((status = check_filename(remove->rm_name, remove->rm_namelen, nfserr_noent))) | ||
782 | return status; | ||
783 | |||
784 | DECODE_TAIL; | ||
785 | } | ||
786 | |||
787 | static int | ||
788 | nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename) | ||
789 | { | ||
790 | DECODE_HEAD; | ||
791 | |||
792 | READ_BUF(4); | ||
793 | READ32(rename->rn_snamelen); | ||
794 | READ_BUF(rename->rn_snamelen + 4); | ||
795 | SAVEMEM(rename->rn_sname, rename->rn_snamelen); | ||
796 | READ32(rename->rn_tnamelen); | ||
797 | READ_BUF(rename->rn_tnamelen); | ||
798 | SAVEMEM(rename->rn_tname, rename->rn_tnamelen); | ||
799 | if ((status = check_filename(rename->rn_sname, rename->rn_snamelen, nfserr_noent))) | ||
800 | return status; | ||
801 | if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen, nfserr_inval))) | ||
802 | return status; | ||
803 | |||
804 | DECODE_TAIL; | ||
805 | } | ||
806 | |||
807 | static int | ||
808 | nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid) | ||
809 | { | ||
810 | DECODE_HEAD; | ||
811 | |||
812 | READ_BUF(sizeof(clientid_t)); | ||
813 | COPYMEM(clientid, sizeof(clientid_t)); | ||
814 | |||
815 | DECODE_TAIL; | ||
816 | } | ||
817 | |||
818 | static int | ||
819 | nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr) | ||
820 | { | ||
821 | DECODE_HEAD; | ||
822 | |||
823 | READ_BUF(sizeof(stateid_t)); | ||
824 | READ32(setattr->sa_stateid.si_generation); | ||
825 | COPYMEM(&setattr->sa_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
826 | if ((status = nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr, &setattr->sa_acl))) | ||
827 | goto out; | ||
828 | |||
829 | DECODE_TAIL; | ||
830 | } | ||
831 | |||
832 | static int | ||
833 | nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid *setclientid) | ||
834 | { | ||
835 | DECODE_HEAD; | ||
836 | |||
837 | READ_BUF(12); | ||
838 | COPYMEM(setclientid->se_verf.data, 8); | ||
839 | READ32(setclientid->se_namelen); | ||
840 | |||
841 | READ_BUF(setclientid->se_namelen + 8); | ||
842 | SAVEMEM(setclientid->se_name, setclientid->se_namelen); | ||
843 | READ32(setclientid->se_callback_prog); | ||
844 | READ32(setclientid->se_callback_netid_len); | ||
845 | |||
846 | READ_BUF(setclientid->se_callback_netid_len + 4); | ||
847 | SAVEMEM(setclientid->se_callback_netid_val, setclientid->se_callback_netid_len); | ||
848 | READ32(setclientid->se_callback_addr_len); | ||
849 | |||
850 | READ_BUF(setclientid->se_callback_addr_len + 4); | ||
851 | SAVEMEM(setclientid->se_callback_addr_val, setclientid->se_callback_addr_len); | ||
852 | READ32(setclientid->se_callback_ident); | ||
853 | |||
854 | DECODE_TAIL; | ||
855 | } | ||
856 | |||
857 | static int | ||
858 | nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid_confirm *scd_c) | ||
859 | { | ||
860 | DECODE_HEAD; | ||
861 | |||
862 | READ_BUF(8 + sizeof(nfs4_verifier)); | ||
863 | COPYMEM(&scd_c->sc_clientid, 8); | ||
864 | COPYMEM(&scd_c->sc_confirm, sizeof(nfs4_verifier)); | ||
865 | |||
866 | DECODE_TAIL; | ||
867 | } | ||
868 | |||
869 | /* Also used for NVERIFY */ | ||
870 | static int | ||
871 | nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify) | ||
872 | { | ||
873 | #if 0 | ||
874 | struct nfsd4_compoundargs save = { | ||
875 | .p = argp->p, | ||
876 | .end = argp->end, | ||
877 | .rqstp = argp->rqstp, | ||
878 | }; | ||
879 | u32 ve_bmval[2]; | ||
880 | struct iattr ve_iattr; /* request */ | ||
881 | struct nfs4_acl *ve_acl; /* request */ | ||
882 | #endif | ||
883 | DECODE_HEAD; | ||
884 | |||
885 | if ((status = nfsd4_decode_bitmap(argp, verify->ve_bmval))) | ||
886 | goto out; | ||
887 | |||
888 | /* For convenience's sake, we compare raw xdr'd attributes in | ||
889 | * nfsd4_proc_verify; however we still decode here just to return | ||
890 | * correct error in case of bad xdr. */ | ||
891 | #if 0 | ||
892 | status = nfsd4_decode_fattr(ve_bmval, &ve_iattr, &ve_acl); | ||
893 | if (status == nfserr_inval) { | ||
894 | status = nfserrno(status); | ||
895 | goto out; | ||
896 | } | ||
897 | #endif | ||
898 | READ_BUF(4); | ||
899 | READ32(verify->ve_attrlen); | ||
900 | READ_BUF(verify->ve_attrlen); | ||
901 | SAVEMEM(verify->ve_attrval, verify->ve_attrlen); | ||
902 | |||
903 | DECODE_TAIL; | ||
904 | } | ||
905 | |||
906 | static int | ||
907 | nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) | ||
908 | { | ||
909 | int avail; | ||
910 | int v; | ||
911 | int len; | ||
912 | DECODE_HEAD; | ||
913 | |||
914 | READ_BUF(sizeof(stateid_opaque_t) + 20); | ||
915 | READ32(write->wr_stateid.si_generation); | ||
916 | COPYMEM(&write->wr_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
917 | READ64(write->wr_offset); | ||
918 | READ32(write->wr_stable_how); | ||
919 | if (write->wr_stable_how > 2) | ||
920 | goto xdr_error; | ||
921 | READ32(write->wr_buflen); | ||
922 | |||
923 | /* Sorry .. no magic macros for this.. * | ||
924 | * READ_BUF(write->wr_buflen); | ||
925 | * SAVEMEM(write->wr_buf, write->wr_buflen); | ||
926 | */ | ||
927 | avail = (char*)argp->end - (char*)argp->p; | ||
928 | if (avail + argp->pagelen < write->wr_buflen) { | ||
929 | printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); | ||
930 | goto xdr_error; | ||
931 | } | ||
932 | write->wr_vec[0].iov_base = p; | ||
933 | write->wr_vec[0].iov_len = avail; | ||
934 | v = 0; | ||
935 | len = write->wr_buflen; | ||
936 | while (len > write->wr_vec[v].iov_len) { | ||
937 | len -= write->wr_vec[v].iov_len; | ||
938 | v++; | ||
939 | write->wr_vec[v].iov_base = page_address(argp->pagelist[0]); | ||
940 | argp->pagelist++; | ||
941 | if (argp->pagelen >= PAGE_SIZE) { | ||
942 | write->wr_vec[v].iov_len = PAGE_SIZE; | ||
943 | argp->pagelen -= PAGE_SIZE; | ||
944 | } else { | ||
945 | write->wr_vec[v].iov_len = argp->pagelen; | ||
946 | argp->pagelen -= len; | ||
947 | } | ||
948 | } | ||
949 | argp->end = (u32*) (write->wr_vec[v].iov_base + write->wr_vec[v].iov_len); | ||
950 | argp->p = (u32*) (write->wr_vec[v].iov_base + (XDR_QUADLEN(len) << 2)); | ||
951 | write->wr_vec[v].iov_len = len; | ||
952 | write->wr_vlen = v+1; | ||
953 | |||
954 | DECODE_TAIL; | ||
955 | } | ||
956 | |||
957 | static int | ||
958 | nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_release_lockowner *rlockowner) | ||
959 | { | ||
960 | DECODE_HEAD; | ||
961 | |||
962 | READ_BUF(12); | ||
963 | COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t)); | ||
964 | READ32(rlockowner->rl_owner.len); | ||
965 | READ_BUF(rlockowner->rl_owner.len); | ||
966 | READMEM(rlockowner->rl_owner.data, rlockowner->rl_owner.len); | ||
967 | |||
968 | DECODE_TAIL; | ||
969 | } | ||
970 | |||
971 | static int | ||
972 | nfsd4_decode_compound(struct nfsd4_compoundargs *argp) | ||
973 | { | ||
974 | DECODE_HEAD; | ||
975 | struct nfsd4_op *op; | ||
976 | int i; | ||
977 | |||
978 | /* | ||
979 | * XXX: According to spec, we should check the tag | ||
980 | * for UTF-8 compliance. I'm postponing this for | ||
981 | * now because it seems that some clients do use | ||
982 | * binary tags. | ||
983 | */ | ||
984 | READ_BUF(4); | ||
985 | READ32(argp->taglen); | ||
986 | READ_BUF(argp->taglen + 8); | ||
987 | SAVEMEM(argp->tag, argp->taglen); | ||
988 | READ32(argp->minorversion); | ||
989 | READ32(argp->opcnt); | ||
990 | |||
991 | if (argp->taglen > NFSD4_MAX_TAGLEN) | ||
992 | goto xdr_error; | ||
993 | if (argp->opcnt > 100) | ||
994 | goto xdr_error; | ||
995 | |||
996 | if (argp->opcnt > sizeof(argp->iops)/sizeof(argp->iops[0])) { | ||
997 | argp->ops = kmalloc(argp->opcnt * sizeof(*argp->ops), GFP_KERNEL); | ||
998 | if (!argp->ops) { | ||
999 | argp->ops = argp->iops; | ||
1000 | printk(KERN_INFO "nfsd: couldn't allocate room for COMPOUND\n"); | ||
1001 | goto xdr_error; | ||
1002 | } | ||
1003 | } | ||
1004 | |||
1005 | for (i = 0; i < argp->opcnt; i++) { | ||
1006 | op = &argp->ops[i]; | ||
1007 | op->replay = NULL; | ||
1008 | |||
1009 | /* | ||
1010 | * We can't use READ_BUF() here because we need to handle | ||
1011 | * a missing opcode as an OP_WRITE + 1. So we need to check | ||
1012 | * to see if we're truly at the end of our buffer or if there | ||
1013 | * is another page we need to flip to. | ||
1014 | */ | ||
1015 | |||
1016 | if (argp->p == argp->end) { | ||
1017 | if (argp->pagelen < 4) { | ||
1018 | /* There isn't an opcode still on the wire */ | ||
1019 | op->opnum = OP_WRITE + 1; | ||
1020 | op->status = nfserr_bad_xdr; | ||
1021 | argp->opcnt = i+1; | ||
1022 | break; | ||
1023 | } | ||
1024 | |||
1025 | /* | ||
1026 | * False alarm. We just hit a page boundary, but there | ||
1027 | * is still data available. Move pointer across page | ||
1028 | * boundary. *snip from READ_BUF* | ||
1029 | */ | ||
1030 | argp->p = page_address(argp->pagelist[0]); | ||
1031 | argp->pagelist++; | ||
1032 | if (argp->pagelen < PAGE_SIZE) { | ||
1033 | argp->end = p + (argp->pagelen>>2); | ||
1034 | argp->pagelen = 0; | ||
1035 | } else { | ||
1036 | argp->end = p + (PAGE_SIZE>>2); | ||
1037 | argp->pagelen -= PAGE_SIZE; | ||
1038 | } | ||
1039 | } | ||
1040 | op->opnum = ntohl(*argp->p++); | ||
1041 | |||
1042 | switch (op->opnum) { | ||
1043 | case 2: /* Reserved operation */ | ||
1044 | op->opnum = OP_ILLEGAL; | ||
1045 | if (argp->minorversion == 0) | ||
1046 | op->status = nfserr_op_illegal; | ||
1047 | else | ||
1048 | op->status = nfserr_minor_vers_mismatch; | ||
1049 | break; | ||
1050 | case OP_ACCESS: | ||
1051 | op->status = nfsd4_decode_access(argp, &op->u.access); | ||
1052 | break; | ||
1053 | case OP_CLOSE: | ||
1054 | op->status = nfsd4_decode_close(argp, &op->u.close); | ||
1055 | break; | ||
1056 | case OP_COMMIT: | ||
1057 | op->status = nfsd4_decode_commit(argp, &op->u.commit); | ||
1058 | break; | ||
1059 | case OP_CREATE: | ||
1060 | op->status = nfsd4_decode_create(argp, &op->u.create); | ||
1061 | break; | ||
1062 | case OP_DELEGRETURN: | ||
1063 | op->status = nfsd4_decode_delegreturn(argp, &op->u.delegreturn); | ||
1064 | break; | ||
1065 | case OP_GETATTR: | ||
1066 | op->status = nfsd4_decode_getattr(argp, &op->u.getattr); | ||
1067 | break; | ||
1068 | case OP_GETFH: | ||
1069 | op->status = nfs_ok; | ||
1070 | break; | ||
1071 | case OP_LINK: | ||
1072 | op->status = nfsd4_decode_link(argp, &op->u.link); | ||
1073 | break; | ||
1074 | case OP_LOCK: | ||
1075 | op->status = nfsd4_decode_lock(argp, &op->u.lock); | ||
1076 | break; | ||
1077 | case OP_LOCKT: | ||
1078 | op->status = nfsd4_decode_lockt(argp, &op->u.lockt); | ||
1079 | break; | ||
1080 | case OP_LOCKU: | ||
1081 | op->status = nfsd4_decode_locku(argp, &op->u.locku); | ||
1082 | break; | ||
1083 | case OP_LOOKUP: | ||
1084 | op->status = nfsd4_decode_lookup(argp, &op->u.lookup); | ||
1085 | break; | ||
1086 | case OP_LOOKUPP: | ||
1087 | op->status = nfs_ok; | ||
1088 | break; | ||
1089 | case OP_NVERIFY: | ||
1090 | op->status = nfsd4_decode_verify(argp, &op->u.nverify); | ||
1091 | break; | ||
1092 | case OP_OPEN: | ||
1093 | op->status = nfsd4_decode_open(argp, &op->u.open); | ||
1094 | break; | ||
1095 | case OP_OPEN_CONFIRM: | ||
1096 | op->status = nfsd4_decode_open_confirm(argp, &op->u.open_confirm); | ||
1097 | break; | ||
1098 | case OP_OPEN_DOWNGRADE: | ||
1099 | op->status = nfsd4_decode_open_downgrade(argp, &op->u.open_downgrade); | ||
1100 | break; | ||
1101 | case OP_PUTFH: | ||
1102 | op->status = nfsd4_decode_putfh(argp, &op->u.putfh); | ||
1103 | break; | ||
1104 | case OP_PUTROOTFH: | ||
1105 | op->status = nfs_ok; | ||
1106 | break; | ||
1107 | case OP_READ: | ||
1108 | op->status = nfsd4_decode_read(argp, &op->u.read); | ||
1109 | break; | ||
1110 | case OP_READDIR: | ||
1111 | op->status = nfsd4_decode_readdir(argp, &op->u.readdir); | ||
1112 | break; | ||
1113 | case OP_READLINK: | ||
1114 | op->status = nfs_ok; | ||
1115 | break; | ||
1116 | case OP_REMOVE: | ||
1117 | op->status = nfsd4_decode_remove(argp, &op->u.remove); | ||
1118 | break; | ||
1119 | case OP_RENAME: | ||
1120 | op->status = nfsd4_decode_rename(argp, &op->u.rename); | ||
1121 | break; | ||
1122 | case OP_RESTOREFH: | ||
1123 | op->status = nfs_ok; | ||
1124 | break; | ||
1125 | case OP_RENEW: | ||
1126 | op->status = nfsd4_decode_renew(argp, &op->u.renew); | ||
1127 | break; | ||
1128 | case OP_SAVEFH: | ||
1129 | op->status = nfs_ok; | ||
1130 | break; | ||
1131 | case OP_SETATTR: | ||
1132 | op->status = nfsd4_decode_setattr(argp, &op->u.setattr); | ||
1133 | break; | ||
1134 | case OP_SETCLIENTID: | ||
1135 | op->status = nfsd4_decode_setclientid(argp, &op->u.setclientid); | ||
1136 | break; | ||
1137 | case OP_SETCLIENTID_CONFIRM: | ||
1138 | op->status = nfsd4_decode_setclientid_confirm(argp, &op->u.setclientid_confirm); | ||
1139 | break; | ||
1140 | case OP_VERIFY: | ||
1141 | op->status = nfsd4_decode_verify(argp, &op->u.verify); | ||
1142 | break; | ||
1143 | case OP_WRITE: | ||
1144 | op->status = nfsd4_decode_write(argp, &op->u.write); | ||
1145 | break; | ||
1146 | case OP_RELEASE_LOCKOWNER: | ||
1147 | op->status = nfsd4_decode_release_lockowner(argp, &op->u.release_lockowner); | ||
1148 | break; | ||
1149 | default: | ||
1150 | op->opnum = OP_ILLEGAL; | ||
1151 | op->status = nfserr_op_illegal; | ||
1152 | break; | ||
1153 | } | ||
1154 | |||
1155 | if (op->status) { | ||
1156 | argp->opcnt = i+1; | ||
1157 | break; | ||
1158 | } | ||
1159 | } | ||
1160 | |||
1161 | DECODE_TAIL; | ||
1162 | } | ||
1163 | /* | ||
1164 | * END OF "GENERIC" DECODE ROUTINES. | ||
1165 | */ | ||
1166 | |||
1167 | /* | ||
1168 | * START OF "GENERIC" ENCODE ROUTINES. | ||
1169 | * These may look a little ugly since they are imported from a "generic" | ||
1170 | * set of XDR encode/decode routines which are intended to be shared by | ||
1171 | * all of our NFSv4 implementations (OpenBSD, MacOS X...). | ||
1172 | * | ||
1173 | * If the pain of reading these is too great, it should be a straightforward | ||
1174 | * task to translate them into Linux-specific versions which are more | ||
1175 | * consistent with the style used in NFSv2/v3... | ||
1176 | */ | ||
1177 | #define ENCODE_HEAD u32 *p | ||
1178 | |||
1179 | #define WRITE32(n) *p++ = htonl(n) | ||
1180 | #define WRITE64(n) do { \ | ||
1181 | *p++ = htonl((u32)((n) >> 32)); \ | ||
1182 | *p++ = htonl((u32)(n)); \ | ||
1183 | } while (0) | ||
1184 | #define WRITEMEM(ptr,nbytes) do { \ | ||
1185 | *(p + XDR_QUADLEN(nbytes) -1) = 0; \ | ||
1186 | memcpy(p, ptr, nbytes); \ | ||
1187 | p += XDR_QUADLEN(nbytes); \ | ||
1188 | } while (0) | ||
1189 | #define WRITECINFO(c) do { \ | ||
1190 | *p++ = htonl(c.atomic); \ | ||
1191 | *p++ = htonl(c.before_ctime_sec); \ | ||
1192 | *p++ = htonl(c.before_ctime_nsec); \ | ||
1193 | *p++ = htonl(c.after_ctime_sec); \ | ||
1194 | *p++ = htonl(c.after_ctime_nsec); \ | ||
1195 | } while (0) | ||
1196 | |||
1197 | #define RESERVE_SPACE(nbytes) do { \ | ||
1198 | p = resp->p; \ | ||
1199 | BUG_ON(p + XDR_QUADLEN(nbytes) > resp->end); \ | ||
1200 | } while (0) | ||
1201 | #define ADJUST_ARGS() resp->p = p | ||
1202 | |||
1203 | /* | ||
1204 | * Header routine to setup seqid operation replay cache | ||
1205 | */ | ||
1206 | #define ENCODE_SEQID_OP_HEAD \ | ||
1207 | u32 *p; \ | ||
1208 | u32 *save; \ | ||
1209 | \ | ||
1210 | save = resp->p; | ||
1211 | |||
1212 | /* | ||
1213 | * Routine for encoding the result of a | ||
1214 | * "seqid-mutating" NFSv4 operation. This is | ||
1215 | * where seqids are incremented, and the | ||
1216 | * replay cache is filled. | ||
1217 | */ | ||
1218 | |||
1219 | #define ENCODE_SEQID_OP_TAIL(stateowner) do { \ | ||
1220 | if (seqid_mutating_err(nfserr) && stateowner) { \ | ||
1221 | if (stateowner->so_confirmed) \ | ||
1222 | stateowner->so_seqid++; \ | ||
1223 | stateowner->so_replay.rp_status = nfserr; \ | ||
1224 | stateowner->so_replay.rp_buflen = \ | ||
1225 | (((char *)(resp)->p - (char *)save)); \ | ||
1226 | memcpy(stateowner->so_replay.rp_buf, save, \ | ||
1227 | stateowner->so_replay.rp_buflen); \ | ||
1228 | } } while (0); | ||
1229 | |||
1230 | |||
1231 | static u32 nfs4_ftypes[16] = { | ||
1232 | NF4BAD, NF4FIFO, NF4CHR, NF4BAD, | ||
1233 | NF4DIR, NF4BAD, NF4BLK, NF4BAD, | ||
1234 | NF4REG, NF4BAD, NF4LNK, NF4BAD, | ||
1235 | NF4SOCK, NF4BAD, NF4LNK, NF4BAD, | ||
1236 | }; | ||
1237 | |||
1238 | static int | ||
1239 | nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group, | ||
1240 | u32 **p, int *buflen) | ||
1241 | { | ||
1242 | int status; | ||
1243 | |||
1244 | if (*buflen < (XDR_QUADLEN(IDMAP_NAMESZ) << 2) + 4) | ||
1245 | return nfserr_resource; | ||
1246 | if (whotype != NFS4_ACL_WHO_NAMED) | ||
1247 | status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1)); | ||
1248 | else if (group) | ||
1249 | status = nfsd_map_gid_to_name(rqstp, id, (u8 *)(*p + 1)); | ||
1250 | else | ||
1251 | status = nfsd_map_uid_to_name(rqstp, id, (u8 *)(*p + 1)); | ||
1252 | if (status < 0) | ||
1253 | return nfserrno(status); | ||
1254 | *p = xdr_encode_opaque(*p, NULL, status); | ||
1255 | *buflen -= (XDR_QUADLEN(status) << 2) + 4; | ||
1256 | BUG_ON(*buflen < 0); | ||
1257 | return 0; | ||
1258 | } | ||
1259 | |||
1260 | static inline int | ||
1261 | nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, u32 **p, int *buflen) | ||
1262 | { | ||
1263 | return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, uid, 0, p, buflen); | ||
1264 | } | ||
1265 | |||
1266 | static inline int | ||
1267 | nfsd4_encode_group(struct svc_rqst *rqstp, uid_t gid, u32 **p, int *buflen) | ||
1268 | { | ||
1269 | return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, gid, 1, p, buflen); | ||
1270 | } | ||
1271 | |||
1272 | static inline int | ||
1273 | nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group, | ||
1274 | u32 **p, int *buflen) | ||
1275 | { | ||
1276 | return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen); | ||
1277 | } | ||
1278 | |||
1279 | |||
1280 | /* | ||
1281 | * Note: @fhp can be NULL; in this case, we might have to compose the filehandle | ||
1282 | * ourselves. | ||
1283 | * | ||
1284 | * @countp is the buffer size in _words_; upon successful return this becomes | ||
1285 | * replaced with the number of words written. | ||
1286 | */ | ||
1287 | int | ||
1288 | nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, | ||
1289 | struct dentry *dentry, u32 *buffer, int *countp, u32 *bmval, | ||
1290 | struct svc_rqst *rqstp) | ||
1291 | { | ||
1292 | u32 bmval0 = bmval[0]; | ||
1293 | u32 bmval1 = bmval[1]; | ||
1294 | struct kstat stat; | ||
1295 | struct svc_fh tempfh; | ||
1296 | struct kstatfs statfs; | ||
1297 | int buflen = *countp << 2; | ||
1298 | u32 *attrlenp; | ||
1299 | u32 dummy; | ||
1300 | u64 dummy64; | ||
1301 | u32 *p = buffer; | ||
1302 | int status; | ||
1303 | int aclsupport = 0; | ||
1304 | struct nfs4_acl *acl = NULL; | ||
1305 | |||
1306 | BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1); | ||
1307 | BUG_ON(bmval0 & ~NFSD_SUPPORTED_ATTRS_WORD0); | ||
1308 | BUG_ON(bmval1 & ~NFSD_SUPPORTED_ATTRS_WORD1); | ||
1309 | |||
1310 | status = vfs_getattr(exp->ex_mnt, dentry, &stat); | ||
1311 | if (status) | ||
1312 | goto out_nfserr; | ||
1313 | if ((bmval0 & (FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL)) || | ||
1314 | (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | | ||
1315 | FATTR4_WORD1_SPACE_TOTAL))) { | ||
1316 | status = vfs_statfs(dentry->d_inode->i_sb, &statfs); | ||
1317 | if (status) | ||
1318 | goto out_nfserr; | ||
1319 | } | ||
1320 | if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) { | ||
1321 | fh_init(&tempfh, NFS4_FHSIZE); | ||
1322 | status = fh_compose(&tempfh, exp, dentry, NULL); | ||
1323 | if (status) | ||
1324 | goto out; | ||
1325 | fhp = &tempfh; | ||
1326 | } | ||
1327 | if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT | ||
1328 | | FATTR4_WORD0_SUPPORTED_ATTRS)) { | ||
1329 | status = nfsd4_get_nfs4_acl(rqstp, dentry, &acl); | ||
1330 | aclsupport = (status == 0); | ||
1331 | if (bmval0 & FATTR4_WORD0_ACL) { | ||
1332 | if (status == -EOPNOTSUPP) | ||
1333 | bmval0 &= ~FATTR4_WORD0_ACL; | ||
1334 | else if (status == -EINVAL) { | ||
1335 | status = nfserr_attrnotsupp; | ||
1336 | goto out; | ||
1337 | } else if (status != 0) | ||
1338 | goto out_nfserr; | ||
1339 | } | ||
1340 | } | ||
1341 | if ((buflen -= 16) < 0) | ||
1342 | goto out_resource; | ||
1343 | |||
1344 | WRITE32(2); | ||
1345 | WRITE32(bmval0); | ||
1346 | WRITE32(bmval1); | ||
1347 | attrlenp = p++; /* to be backfilled later */ | ||
1348 | |||
1349 | if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) { | ||
1350 | if ((buflen -= 12) < 0) | ||
1351 | goto out_resource; | ||
1352 | WRITE32(2); | ||
1353 | WRITE32(aclsupport ? | ||
1354 | NFSD_SUPPORTED_ATTRS_WORD0 : | ||
1355 | NFSD_SUPPORTED_ATTRS_WORD0 & ~FATTR4_WORD0_ACL); | ||
1356 | WRITE32(NFSD_SUPPORTED_ATTRS_WORD1); | ||
1357 | } | ||
1358 | if (bmval0 & FATTR4_WORD0_TYPE) { | ||
1359 | if ((buflen -= 4) < 0) | ||
1360 | goto out_resource; | ||
1361 | dummy = nfs4_ftypes[(stat.mode & S_IFMT) >> 12]; | ||
1362 | if (dummy == NF4BAD) | ||
1363 | goto out_serverfault; | ||
1364 | WRITE32(dummy); | ||
1365 | } | ||
1366 | if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) { | ||
1367 | if ((buflen -= 4) < 0) | ||
1368 | goto out_resource; | ||
1369 | WRITE32( NFS4_FH_NOEXPIRE_WITH_OPEN | NFS4_FH_VOL_RENAME ); | ||
1370 | } | ||
1371 | if (bmval0 & FATTR4_WORD0_CHANGE) { | ||
1372 | /* | ||
1373 | * Note: This _must_ be consistent with the scheme for writing | ||
1374 | * change_info, so any changes made here must be reflected there | ||
1375 | * as well. (See xdr4.h:set_change_info() and the WRITECINFO() | ||
1376 | * macro above.) | ||
1377 | */ | ||
1378 | if ((buflen -= 8) < 0) | ||
1379 | goto out_resource; | ||
1380 | WRITE32(stat.ctime.tv_sec); | ||
1381 | WRITE32(stat.ctime.tv_nsec); | ||
1382 | } | ||
1383 | if (bmval0 & FATTR4_WORD0_SIZE) { | ||
1384 | if ((buflen -= 8) < 0) | ||
1385 | goto out_resource; | ||
1386 | WRITE64(stat.size); | ||
1387 | } | ||
1388 | if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) { | ||
1389 | if ((buflen -= 4) < 0) | ||
1390 | goto out_resource; | ||
1391 | WRITE32(1); | ||
1392 | } | ||
1393 | if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) { | ||
1394 | if ((buflen -= 4) < 0) | ||
1395 | goto out_resource; | ||
1396 | WRITE32(1); | ||
1397 | } | ||
1398 | if (bmval0 & FATTR4_WORD0_NAMED_ATTR) { | ||
1399 | if ((buflen -= 4) < 0) | ||
1400 | goto out_resource; | ||
1401 | WRITE32(0); | ||
1402 | } | ||
1403 | if (bmval0 & FATTR4_WORD0_FSID) { | ||
1404 | if ((buflen -= 16) < 0) | ||
1405 | goto out_resource; | ||
1406 | if (is_fsid(fhp, rqstp->rq_reffh)) { | ||
1407 | WRITE64((u64)exp->ex_fsid); | ||
1408 | WRITE64((u64)0); | ||
1409 | } else { | ||
1410 | WRITE32(0); | ||
1411 | WRITE32(MAJOR(stat.dev)); | ||
1412 | WRITE32(0); | ||
1413 | WRITE32(MINOR(stat.dev)); | ||
1414 | } | ||
1415 | } | ||
1416 | if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) { | ||
1417 | if ((buflen -= 4) < 0) | ||
1418 | goto out_resource; | ||
1419 | WRITE32(0); | ||
1420 | } | ||
1421 | if (bmval0 & FATTR4_WORD0_LEASE_TIME) { | ||
1422 | if ((buflen -= 4) < 0) | ||
1423 | goto out_resource; | ||
1424 | WRITE32(NFSD_LEASE_TIME); | ||
1425 | } | ||
1426 | if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) { | ||
1427 | if ((buflen -= 4) < 0) | ||
1428 | goto out_resource; | ||
1429 | WRITE32(0); | ||
1430 | } | ||
1431 | if (bmval0 & FATTR4_WORD0_ACL) { | ||
1432 | struct nfs4_ace *ace; | ||
1433 | struct list_head *h; | ||
1434 | |||
1435 | if (acl == NULL) { | ||
1436 | if ((buflen -= 4) < 0) | ||
1437 | goto out_resource; | ||
1438 | |||
1439 | WRITE32(0); | ||
1440 | goto out_acl; | ||
1441 | } | ||
1442 | if ((buflen -= 4) < 0) | ||
1443 | goto out_resource; | ||
1444 | WRITE32(acl->naces); | ||
1445 | |||
1446 | list_for_each(h, &acl->ace_head) { | ||
1447 | ace = list_entry(h, struct nfs4_ace, l_ace); | ||
1448 | |||
1449 | if ((buflen -= 4*3) < 0) | ||
1450 | goto out_resource; | ||
1451 | WRITE32(ace->type); | ||
1452 | WRITE32(ace->flag); | ||
1453 | WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL); | ||
1454 | status = nfsd4_encode_aclname(rqstp, ace->whotype, | ||
1455 | ace->who, ace->flag & NFS4_ACE_IDENTIFIER_GROUP, | ||
1456 | &p, &buflen); | ||
1457 | if (status == nfserr_resource) | ||
1458 | goto out_resource; | ||
1459 | if (status) | ||
1460 | goto out; | ||
1461 | } | ||
1462 | } | ||
1463 | out_acl: | ||
1464 | if (bmval0 & FATTR4_WORD0_ACLSUPPORT) { | ||
1465 | if ((buflen -= 4) < 0) | ||
1466 | goto out_resource; | ||
1467 | WRITE32(aclsupport ? | ||
1468 | ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0); | ||
1469 | } | ||
1470 | if (bmval0 & FATTR4_WORD0_CANSETTIME) { | ||
1471 | if ((buflen -= 4) < 0) | ||
1472 | goto out_resource; | ||
1473 | WRITE32(1); | ||
1474 | } | ||
1475 | if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) { | ||
1476 | if ((buflen -= 4) < 0) | ||
1477 | goto out_resource; | ||
1478 | WRITE32(1); | ||
1479 | } | ||
1480 | if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) { | ||
1481 | if ((buflen -= 4) < 0) | ||
1482 | goto out_resource; | ||
1483 | WRITE32(1); | ||
1484 | } | ||
1485 | if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) { | ||
1486 | if ((buflen -= 4) < 0) | ||
1487 | goto out_resource; | ||
1488 | WRITE32(1); | ||
1489 | } | ||
1490 | if (bmval0 & FATTR4_WORD0_FILEHANDLE) { | ||
1491 | buflen -= (XDR_QUADLEN(fhp->fh_handle.fh_size) << 2) + 4; | ||
1492 | if (buflen < 0) | ||
1493 | goto out_resource; | ||
1494 | WRITE32(fhp->fh_handle.fh_size); | ||
1495 | WRITEMEM(&fhp->fh_handle.fh_base, fhp->fh_handle.fh_size); | ||
1496 | } | ||
1497 | if (bmval0 & FATTR4_WORD0_FILEID) { | ||
1498 | if ((buflen -= 8) < 0) | ||
1499 | goto out_resource; | ||
1500 | WRITE64((u64) stat.ino); | ||
1501 | } | ||
1502 | if (bmval0 & FATTR4_WORD0_FILES_AVAIL) { | ||
1503 | if ((buflen -= 8) < 0) | ||
1504 | goto out_resource; | ||
1505 | WRITE64((u64) statfs.f_ffree); | ||
1506 | } | ||
1507 | if (bmval0 & FATTR4_WORD0_FILES_FREE) { | ||
1508 | if ((buflen -= 8) < 0) | ||
1509 | goto out_resource; | ||
1510 | WRITE64((u64) statfs.f_ffree); | ||
1511 | } | ||
1512 | if (bmval0 & FATTR4_WORD0_FILES_TOTAL) { | ||
1513 | if ((buflen -= 8) < 0) | ||
1514 | goto out_resource; | ||
1515 | WRITE64((u64) statfs.f_files); | ||
1516 | } | ||
1517 | if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) { | ||
1518 | if ((buflen -= 4) < 0) | ||
1519 | goto out_resource; | ||
1520 | WRITE32(1); | ||
1521 | } | ||
1522 | if (bmval0 & FATTR4_WORD0_MAXFILESIZE) { | ||
1523 | if ((buflen -= 8) < 0) | ||
1524 | goto out_resource; | ||
1525 | WRITE64(~(u64)0); | ||
1526 | } | ||
1527 | if (bmval0 & FATTR4_WORD0_MAXLINK) { | ||
1528 | if ((buflen -= 4) < 0) | ||
1529 | goto out_resource; | ||
1530 | WRITE32(255); | ||
1531 | } | ||
1532 | if (bmval0 & FATTR4_WORD0_MAXNAME) { | ||
1533 | if ((buflen -= 4) < 0) | ||
1534 | goto out_resource; | ||
1535 | WRITE32(~(u32) 0); | ||
1536 | } | ||
1537 | if (bmval0 & FATTR4_WORD0_MAXREAD) { | ||
1538 | if ((buflen -= 8) < 0) | ||
1539 | goto out_resource; | ||
1540 | WRITE64((u64) NFSSVC_MAXBLKSIZE); | ||
1541 | } | ||
1542 | if (bmval0 & FATTR4_WORD0_MAXWRITE) { | ||
1543 | if ((buflen -= 8) < 0) | ||
1544 | goto out_resource; | ||
1545 | WRITE64((u64) NFSSVC_MAXBLKSIZE); | ||
1546 | } | ||
1547 | if (bmval1 & FATTR4_WORD1_MODE) { | ||
1548 | if ((buflen -= 4) < 0) | ||
1549 | goto out_resource; | ||
1550 | WRITE32(stat.mode & S_IALLUGO); | ||
1551 | } | ||
1552 | if (bmval1 & FATTR4_WORD1_NO_TRUNC) { | ||
1553 | if ((buflen -= 4) < 0) | ||
1554 | goto out_resource; | ||
1555 | WRITE32(1); | ||
1556 | } | ||
1557 | if (bmval1 & FATTR4_WORD1_NUMLINKS) { | ||
1558 | if ((buflen -= 4) < 0) | ||
1559 | goto out_resource; | ||
1560 | WRITE32(stat.nlink); | ||
1561 | } | ||
1562 | if (bmval1 & FATTR4_WORD1_OWNER) { | ||
1563 | status = nfsd4_encode_user(rqstp, stat.uid, &p, &buflen); | ||
1564 | if (status == nfserr_resource) | ||
1565 | goto out_resource; | ||
1566 | if (status) | ||
1567 | goto out; | ||
1568 | } | ||
1569 | if (bmval1 & FATTR4_WORD1_OWNER_GROUP) { | ||
1570 | status = nfsd4_encode_group(rqstp, stat.gid, &p, &buflen); | ||
1571 | if (status == nfserr_resource) | ||
1572 | goto out_resource; | ||
1573 | if (status) | ||
1574 | goto out; | ||
1575 | } | ||
1576 | if (bmval1 & FATTR4_WORD1_RAWDEV) { | ||
1577 | if ((buflen -= 8) < 0) | ||
1578 | goto out_resource; | ||
1579 | WRITE32((u32) MAJOR(stat.rdev)); | ||
1580 | WRITE32((u32) MINOR(stat.rdev)); | ||
1581 | } | ||
1582 | if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) { | ||
1583 | if ((buflen -= 8) < 0) | ||
1584 | goto out_resource; | ||
1585 | dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize; | ||
1586 | WRITE64(dummy64); | ||
1587 | } | ||
1588 | if (bmval1 & FATTR4_WORD1_SPACE_FREE) { | ||
1589 | if ((buflen -= 8) < 0) | ||
1590 | goto out_resource; | ||
1591 | dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize; | ||
1592 | WRITE64(dummy64); | ||
1593 | } | ||
1594 | if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) { | ||
1595 | if ((buflen -= 8) < 0) | ||
1596 | goto out_resource; | ||
1597 | dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize; | ||
1598 | WRITE64(dummy64); | ||
1599 | } | ||
1600 | if (bmval1 & FATTR4_WORD1_SPACE_USED) { | ||
1601 | if ((buflen -= 8) < 0) | ||
1602 | goto out_resource; | ||
1603 | dummy64 = (u64)stat.blocks << 9; | ||
1604 | WRITE64(dummy64); | ||
1605 | } | ||
1606 | if (bmval1 & FATTR4_WORD1_TIME_ACCESS) { | ||
1607 | if ((buflen -= 12) < 0) | ||
1608 | goto out_resource; | ||
1609 | WRITE32(0); | ||
1610 | WRITE32(stat.atime.tv_sec); | ||
1611 | WRITE32(stat.atime.tv_nsec); | ||
1612 | } | ||
1613 | if (bmval1 & FATTR4_WORD1_TIME_DELTA) { | ||
1614 | if ((buflen -= 12) < 0) | ||
1615 | goto out_resource; | ||
1616 | WRITE32(0); | ||
1617 | WRITE32(1); | ||
1618 | WRITE32(0); | ||
1619 | } | ||
1620 | if (bmval1 & FATTR4_WORD1_TIME_METADATA) { | ||
1621 | if ((buflen -= 12) < 0) | ||
1622 | goto out_resource; | ||
1623 | WRITE32(0); | ||
1624 | WRITE32(stat.ctime.tv_sec); | ||
1625 | WRITE32(stat.ctime.tv_nsec); | ||
1626 | } | ||
1627 | if (bmval1 & FATTR4_WORD1_TIME_MODIFY) { | ||
1628 | if ((buflen -= 12) < 0) | ||
1629 | goto out_resource; | ||
1630 | WRITE32(0); | ||
1631 | WRITE32(stat.mtime.tv_sec); | ||
1632 | WRITE32(stat.mtime.tv_nsec); | ||
1633 | } | ||
1634 | if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) { | ||
1635 | struct dentry *mnt_pnt, *mnt_root; | ||
1636 | |||
1637 | if ((buflen -= 8) < 0) | ||
1638 | goto out_resource; | ||
1639 | mnt_root = exp->ex_mnt->mnt_root; | ||
1640 | if (mnt_root->d_inode == dentry->d_inode) { | ||
1641 | mnt_pnt = exp->ex_mnt->mnt_mountpoint; | ||
1642 | WRITE64((u64) mnt_pnt->d_inode->i_ino); | ||
1643 | } else | ||
1644 | WRITE64((u64) stat.ino); | ||
1645 | } | ||
1646 | *attrlenp = htonl((char *)p - (char *)attrlenp - 4); | ||
1647 | *countp = p - buffer; | ||
1648 | status = nfs_ok; | ||
1649 | |||
1650 | out: | ||
1651 | nfs4_acl_free(acl); | ||
1652 | if (fhp == &tempfh) | ||
1653 | fh_put(&tempfh); | ||
1654 | return status; | ||
1655 | out_nfserr: | ||
1656 | status = nfserrno(status); | ||
1657 | goto out; | ||
1658 | out_resource: | ||
1659 | *countp = 0; | ||
1660 | status = nfserr_resource; | ||
1661 | goto out; | ||
1662 | out_serverfault: | ||
1663 | status = nfserr_serverfault; | ||
1664 | goto out; | ||
1665 | } | ||
1666 | |||
1667 | static int | ||
1668 | nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, | ||
1669 | const char *name, int namlen, u32 *p, int *buflen) | ||
1670 | { | ||
1671 | struct svc_export *exp = cd->rd_fhp->fh_export; | ||
1672 | struct dentry *dentry; | ||
1673 | int nfserr; | ||
1674 | |||
1675 | dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen); | ||
1676 | if (IS_ERR(dentry)) | ||
1677 | return nfserrno(PTR_ERR(dentry)); | ||
1678 | |||
1679 | exp_get(exp); | ||
1680 | if (d_mountpoint(dentry)) { | ||
1681 | if (nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp)) { | ||
1682 | /* | ||
1683 | * -EAGAIN is the only error returned from | ||
1684 | * nfsd_cross_mnt() and it indicates that an | ||
1685 | * up-call has been initiated to fill in the export | ||
1686 | * options on exp. When the answer comes back, | ||
1687 | * this call will be retried. | ||
1688 | */ | ||
1689 | nfserr = nfserr_dropit; | ||
1690 | goto out_put; | ||
1691 | } | ||
1692 | |||
1693 | } | ||
1694 | nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval, | ||
1695 | cd->rd_rqstp); | ||
1696 | out_put: | ||
1697 | dput(dentry); | ||
1698 | exp_put(exp); | ||
1699 | return nfserr; | ||
1700 | } | ||
1701 | |||
1702 | static u32 * | ||
1703 | nfsd4_encode_rdattr_error(u32 *p, int buflen, int nfserr) | ||
1704 | { | ||
1705 | u32 *attrlenp; | ||
1706 | |||
1707 | if (buflen < 6) | ||
1708 | return NULL; | ||
1709 | *p++ = htonl(2); | ||
1710 | *p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */ | ||
1711 | *p++ = htonl(0); /* bmval1 */ | ||
1712 | |||
1713 | attrlenp = p++; | ||
1714 | *p++ = nfserr; /* no htonl */ | ||
1715 | *attrlenp = htonl((char *)p - (char *)attrlenp - 4); | ||
1716 | return p; | ||
1717 | } | ||
1718 | |||
1719 | static int | ||
1720 | nfsd4_encode_dirent(struct readdir_cd *ccd, const char *name, int namlen, | ||
1721 | loff_t offset, ino_t ino, unsigned int d_type) | ||
1722 | { | ||
1723 | struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common); | ||
1724 | int buflen; | ||
1725 | u32 *p = cd->buffer; | ||
1726 | int nfserr = nfserr_toosmall; | ||
1727 | |||
1728 | /* In nfsv4, "." and ".." never make it onto the wire.. */ | ||
1729 | if (name && isdotent(name, namlen)) { | ||
1730 | cd->common.err = nfs_ok; | ||
1731 | return 0; | ||
1732 | } | ||
1733 | |||
1734 | if (cd->offset) | ||
1735 | xdr_encode_hyper(cd->offset, (u64) offset); | ||
1736 | |||
1737 | buflen = cd->buflen - 4 - XDR_QUADLEN(namlen); | ||
1738 | if (buflen < 0) | ||
1739 | goto fail; | ||
1740 | |||
1741 | *p++ = xdr_one; /* mark entry present */ | ||
1742 | cd->offset = p; /* remember pointer */ | ||
1743 | p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */ | ||
1744 | p = xdr_encode_array(p, name, namlen); /* name length & name */ | ||
1745 | |||
1746 | nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, p, &buflen); | ||
1747 | switch (nfserr) { | ||
1748 | case nfs_ok: | ||
1749 | p += buflen; | ||
1750 | break; | ||
1751 | case nfserr_resource: | ||
1752 | nfserr = nfserr_toosmall; | ||
1753 | goto fail; | ||
1754 | case nfserr_dropit: | ||
1755 | goto fail; | ||
1756 | default: | ||
1757 | /* | ||
1758 | * If the client requested the RDATTR_ERROR attribute, | ||
1759 | * we stuff the error code into this attribute | ||
1760 | * and continue. If this attribute was not requested, | ||
1761 | * then in accordance with the spec, we fail the | ||
1762 | * entire READDIR operation(!) | ||
1763 | */ | ||
1764 | if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)) | ||
1765 | goto fail; | ||
1766 | nfserr = nfserr_toosmall; | ||
1767 | p = nfsd4_encode_rdattr_error(p, buflen, nfserr); | ||
1768 | if (p == NULL) | ||
1769 | goto fail; | ||
1770 | } | ||
1771 | cd->buflen -= (p - cd->buffer); | ||
1772 | cd->buffer = p; | ||
1773 | cd->common.err = nfs_ok; | ||
1774 | return 0; | ||
1775 | fail: | ||
1776 | cd->common.err = nfserr; | ||
1777 | return -EINVAL; | ||
1778 | } | ||
1779 | |||
1780 | static void | ||
1781 | nfsd4_encode_access(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_access *access) | ||
1782 | { | ||
1783 | ENCODE_HEAD; | ||
1784 | |||
1785 | if (!nfserr) { | ||
1786 | RESERVE_SPACE(8); | ||
1787 | WRITE32(access->ac_supported); | ||
1788 | WRITE32(access->ac_resp_access); | ||
1789 | ADJUST_ARGS(); | ||
1790 | } | ||
1791 | } | ||
1792 | |||
1793 | static void | ||
1794 | nfsd4_encode_close(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_close *close) | ||
1795 | { | ||
1796 | ENCODE_SEQID_OP_HEAD; | ||
1797 | |||
1798 | if (!nfserr) { | ||
1799 | RESERVE_SPACE(sizeof(stateid_t)); | ||
1800 | WRITE32(close->cl_stateid.si_generation); | ||
1801 | WRITEMEM(&close->cl_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
1802 | ADJUST_ARGS(); | ||
1803 | } | ||
1804 | ENCODE_SEQID_OP_TAIL(close->cl_stateowner); | ||
1805 | } | ||
1806 | |||
1807 | |||
1808 | static void | ||
1809 | nfsd4_encode_commit(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_commit *commit) | ||
1810 | { | ||
1811 | ENCODE_HEAD; | ||
1812 | |||
1813 | if (!nfserr) { | ||
1814 | RESERVE_SPACE(8); | ||
1815 | WRITEMEM(commit->co_verf.data, 8); | ||
1816 | ADJUST_ARGS(); | ||
1817 | } | ||
1818 | } | ||
1819 | |||
1820 | static void | ||
1821 | nfsd4_encode_create(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_create *create) | ||
1822 | { | ||
1823 | ENCODE_HEAD; | ||
1824 | |||
1825 | if (!nfserr) { | ||
1826 | RESERVE_SPACE(32); | ||
1827 | WRITECINFO(create->cr_cinfo); | ||
1828 | WRITE32(2); | ||
1829 | WRITE32(create->cr_bmval[0]); | ||
1830 | WRITE32(create->cr_bmval[1]); | ||
1831 | ADJUST_ARGS(); | ||
1832 | } | ||
1833 | } | ||
1834 | |||
1835 | static int | ||
1836 | nfsd4_encode_getattr(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_getattr *getattr) | ||
1837 | { | ||
1838 | struct svc_fh *fhp = getattr->ga_fhp; | ||
1839 | int buflen; | ||
1840 | |||
1841 | if (nfserr) | ||
1842 | return nfserr; | ||
1843 | |||
1844 | buflen = resp->end - resp->p - (COMPOUND_ERR_SLACK_SPACE >> 2); | ||
1845 | nfserr = nfsd4_encode_fattr(fhp, fhp->fh_export, fhp->fh_dentry, | ||
1846 | resp->p, &buflen, getattr->ga_bmval, | ||
1847 | resp->rqstp); | ||
1848 | |||
1849 | if (!nfserr) | ||
1850 | resp->p += buflen; | ||
1851 | return nfserr; | ||
1852 | } | ||
1853 | |||
1854 | static void | ||
1855 | nfsd4_encode_getfh(struct nfsd4_compoundres *resp, int nfserr, struct svc_fh *fhp) | ||
1856 | { | ||
1857 | unsigned int len; | ||
1858 | ENCODE_HEAD; | ||
1859 | |||
1860 | if (!nfserr) { | ||
1861 | len = fhp->fh_handle.fh_size; | ||
1862 | RESERVE_SPACE(len + 4); | ||
1863 | WRITE32(len); | ||
1864 | WRITEMEM(&fhp->fh_handle.fh_base, len); | ||
1865 | ADJUST_ARGS(); | ||
1866 | } | ||
1867 | } | ||
1868 | |||
1869 | /* | ||
1870 | * Including all fields other than the name, a LOCK4denied structure requires | ||
1871 | * 8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes. | ||
1872 | */ | ||
1873 | static void | ||
1874 | nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld) | ||
1875 | { | ||
1876 | ENCODE_HEAD; | ||
1877 | |||
1878 | RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop ? ld->ld_sop->so_owner.len : 0)); | ||
1879 | WRITE64(ld->ld_start); | ||
1880 | WRITE64(ld->ld_length); | ||
1881 | WRITE32(ld->ld_type); | ||
1882 | if (ld->ld_sop) { | ||
1883 | WRITEMEM(&ld->ld_clientid, 8); | ||
1884 | WRITE32(ld->ld_sop->so_owner.len); | ||
1885 | WRITEMEM(ld->ld_sop->so_owner.data, ld->ld_sop->so_owner.len); | ||
1886 | kref_put(&ld->ld_sop->so_ref, nfs4_free_stateowner); | ||
1887 | } else { /* non - nfsv4 lock in conflict, no clientid nor owner */ | ||
1888 | WRITE64((u64)0); /* clientid */ | ||
1889 | WRITE32(0); /* length of owner name */ | ||
1890 | } | ||
1891 | ADJUST_ARGS(); | ||
1892 | } | ||
1893 | |||
1894 | static void | ||
1895 | nfsd4_encode_lock(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_lock *lock) | ||
1896 | { | ||
1897 | |||
1898 | ENCODE_SEQID_OP_HEAD; | ||
1899 | |||
1900 | if (!nfserr) { | ||
1901 | RESERVE_SPACE(4 + sizeof(stateid_t)); | ||
1902 | WRITE32(lock->lk_resp_stateid.si_generation); | ||
1903 | WRITEMEM(&lock->lk_resp_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
1904 | ADJUST_ARGS(); | ||
1905 | } else if (nfserr == nfserr_denied) | ||
1906 | nfsd4_encode_lock_denied(resp, &lock->lk_denied); | ||
1907 | |||
1908 | ENCODE_SEQID_OP_TAIL(lock->lk_stateowner); | ||
1909 | } | ||
1910 | |||
1911 | static void | ||
1912 | nfsd4_encode_lockt(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_lockt *lockt) | ||
1913 | { | ||
1914 | if (nfserr == nfserr_denied) | ||
1915 | nfsd4_encode_lock_denied(resp, &lockt->lt_denied); | ||
1916 | } | ||
1917 | |||
1918 | static void | ||
1919 | nfsd4_encode_locku(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_locku *locku) | ||
1920 | { | ||
1921 | ENCODE_SEQID_OP_HEAD; | ||
1922 | |||
1923 | if (!nfserr) { | ||
1924 | RESERVE_SPACE(sizeof(stateid_t)); | ||
1925 | WRITE32(locku->lu_stateid.si_generation); | ||
1926 | WRITEMEM(&locku->lu_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
1927 | ADJUST_ARGS(); | ||
1928 | } | ||
1929 | |||
1930 | ENCODE_SEQID_OP_TAIL(locku->lu_stateowner); | ||
1931 | } | ||
1932 | |||
1933 | |||
1934 | static void | ||
1935 | nfsd4_encode_link(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_link *link) | ||
1936 | { | ||
1937 | ENCODE_HEAD; | ||
1938 | |||
1939 | if (!nfserr) { | ||
1940 | RESERVE_SPACE(20); | ||
1941 | WRITECINFO(link->li_cinfo); | ||
1942 | ADJUST_ARGS(); | ||
1943 | } | ||
1944 | } | ||
1945 | |||
1946 | |||
1947 | static void | ||
1948 | nfsd4_encode_open(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open *open) | ||
1949 | { | ||
1950 | ENCODE_SEQID_OP_HEAD; | ||
1951 | |||
1952 | if (nfserr) | ||
1953 | goto out; | ||
1954 | |||
1955 | RESERVE_SPACE(36 + sizeof(stateid_t)); | ||
1956 | WRITE32(open->op_stateid.si_generation); | ||
1957 | WRITEMEM(&open->op_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
1958 | WRITECINFO(open->op_cinfo); | ||
1959 | WRITE32(open->op_rflags); | ||
1960 | WRITE32(2); | ||
1961 | WRITE32(open->op_bmval[0]); | ||
1962 | WRITE32(open->op_bmval[1]); | ||
1963 | WRITE32(open->op_delegate_type); | ||
1964 | ADJUST_ARGS(); | ||
1965 | |||
1966 | switch (open->op_delegate_type) { | ||
1967 | case NFS4_OPEN_DELEGATE_NONE: | ||
1968 | break; | ||
1969 | case NFS4_OPEN_DELEGATE_READ: | ||
1970 | RESERVE_SPACE(20 + sizeof(stateid_t)); | ||
1971 | WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t)); | ||
1972 | WRITE32(0); | ||
1973 | |||
1974 | /* | ||
1975 | * TODO: ACE's in delegations | ||
1976 | */ | ||
1977 | WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE); | ||
1978 | WRITE32(0); | ||
1979 | WRITE32(0); | ||
1980 | WRITE32(0); /* XXX: is NULL principal ok? */ | ||
1981 | ADJUST_ARGS(); | ||
1982 | break; | ||
1983 | case NFS4_OPEN_DELEGATE_WRITE: | ||
1984 | RESERVE_SPACE(32 + sizeof(stateid_t)); | ||
1985 | WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t)); | ||
1986 | WRITE32(0); | ||
1987 | |||
1988 | /* | ||
1989 | * TODO: space_limit's in delegations | ||
1990 | */ | ||
1991 | WRITE32(NFS4_LIMIT_SIZE); | ||
1992 | WRITE32(~(u32)0); | ||
1993 | WRITE32(~(u32)0); | ||
1994 | |||
1995 | /* | ||
1996 | * TODO: ACE's in delegations | ||
1997 | */ | ||
1998 | WRITE32(NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE); | ||
1999 | WRITE32(0); | ||
2000 | WRITE32(0); | ||
2001 | WRITE32(0); /* XXX: is NULL principal ok? */ | ||
2002 | ADJUST_ARGS(); | ||
2003 | break; | ||
2004 | default: | ||
2005 | BUG(); | ||
2006 | } | ||
2007 | /* XXX save filehandle here */ | ||
2008 | out: | ||
2009 | ENCODE_SEQID_OP_TAIL(open->op_stateowner); | ||
2010 | } | ||
2011 | |||
2012 | static void | ||
2013 | nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open_confirm *oc) | ||
2014 | { | ||
2015 | ENCODE_SEQID_OP_HEAD; | ||
2016 | |||
2017 | if (!nfserr) { | ||
2018 | RESERVE_SPACE(sizeof(stateid_t)); | ||
2019 | WRITE32(oc->oc_resp_stateid.si_generation); | ||
2020 | WRITEMEM(&oc->oc_resp_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
2021 | ADJUST_ARGS(); | ||
2022 | } | ||
2023 | |||
2024 | ENCODE_SEQID_OP_TAIL(oc->oc_stateowner); | ||
2025 | } | ||
2026 | |||
2027 | static void | ||
2028 | nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open_downgrade *od) | ||
2029 | { | ||
2030 | ENCODE_SEQID_OP_HEAD; | ||
2031 | |||
2032 | if (!nfserr) { | ||
2033 | RESERVE_SPACE(sizeof(stateid_t)); | ||
2034 | WRITE32(od->od_stateid.si_generation); | ||
2035 | WRITEMEM(&od->od_stateid.si_opaque, sizeof(stateid_opaque_t)); | ||
2036 | ADJUST_ARGS(); | ||
2037 | } | ||
2038 | |||
2039 | ENCODE_SEQID_OP_TAIL(od->od_stateowner); | ||
2040 | } | ||
2041 | |||
2042 | static int | ||
2043 | nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read *read) | ||
2044 | { | ||
2045 | u32 eof; | ||
2046 | int v, pn; | ||
2047 | unsigned long maxcount; | ||
2048 | long len; | ||
2049 | ENCODE_HEAD; | ||
2050 | |||
2051 | if (nfserr) | ||
2052 | return nfserr; | ||
2053 | if (resp->xbuf->page_len) | ||
2054 | return nfserr_resource; | ||
2055 | |||
2056 | RESERVE_SPACE(8); /* eof flag and byte count */ | ||
2057 | |||
2058 | maxcount = NFSSVC_MAXBLKSIZE; | ||
2059 | if (maxcount > read->rd_length) | ||
2060 | maxcount = read->rd_length; | ||
2061 | |||
2062 | len = maxcount; | ||
2063 | v = 0; | ||
2064 | while (len > 0) { | ||
2065 | pn = resp->rqstp->rq_resused; | ||
2066 | svc_take_page(resp->rqstp); | ||
2067 | read->rd_iov[v].iov_base = page_address(resp->rqstp->rq_respages[pn]); | ||
2068 | read->rd_iov[v].iov_len = len < PAGE_SIZE ? len : PAGE_SIZE; | ||
2069 | v++; | ||
2070 | len -= PAGE_SIZE; | ||
2071 | } | ||
2072 | read->rd_vlen = v; | ||
2073 | |||
2074 | nfserr = nfsd_read(read->rd_rqstp, read->rd_fhp, read->rd_filp, | ||
2075 | read->rd_offset, read->rd_iov, read->rd_vlen, | ||
2076 | &maxcount); | ||
2077 | |||
2078 | if (nfserr == nfserr_symlink) | ||
2079 | nfserr = nfserr_inval; | ||
2080 | if (nfserr) | ||
2081 | return nfserr; | ||
2082 | eof = (read->rd_offset + maxcount >= read->rd_fhp->fh_dentry->d_inode->i_size); | ||
2083 | |||
2084 | WRITE32(eof); | ||
2085 | WRITE32(maxcount); | ||
2086 | ADJUST_ARGS(); | ||
2087 | resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base; | ||
2088 | |||
2089 | resp->xbuf->page_len = maxcount; | ||
2090 | |||
2091 | /* read zero bytes -> don't set up tail */ | ||
2092 | if(!maxcount) | ||
2093 | return 0; | ||
2094 | |||
2095 | /* set up page for remaining responses */ | ||
2096 | svc_take_page(resp->rqstp); | ||
2097 | resp->xbuf->tail[0].iov_base = | ||
2098 | page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); | ||
2099 | resp->rqstp->rq_restailpage = resp->rqstp->rq_resused-1; | ||
2100 | resp->xbuf->tail[0].iov_len = 0; | ||
2101 | resp->p = resp->xbuf->tail[0].iov_base; | ||
2102 | resp->end = resp->p + PAGE_SIZE/4; | ||
2103 | |||
2104 | if (maxcount&3) { | ||
2105 | *(resp->p)++ = 0; | ||
2106 | resp->xbuf->tail[0].iov_base += maxcount&3; | ||
2107 | resp->xbuf->tail[0].iov_len = 4 - (maxcount&3); | ||
2108 | } | ||
2109 | return 0; | ||
2110 | } | ||
2111 | |||
2112 | static int | ||
2113 | nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_readlink *readlink) | ||
2114 | { | ||
2115 | int maxcount; | ||
2116 | char *page; | ||
2117 | ENCODE_HEAD; | ||
2118 | |||
2119 | if (nfserr) | ||
2120 | return nfserr; | ||
2121 | if (resp->xbuf->page_len) | ||
2122 | return nfserr_resource; | ||
2123 | |||
2124 | svc_take_page(resp->rqstp); | ||
2125 | page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); | ||
2126 | |||
2127 | maxcount = PAGE_SIZE; | ||
2128 | RESERVE_SPACE(4); | ||
2129 | |||
2130 | /* | ||
2131 | * XXX: By default, the ->readlink() VFS op will truncate symlinks | ||
2132 | * if they would overflow the buffer. Is this kosher in NFSv4? If | ||
2133 | * not, one easy fix is: if ->readlink() precisely fills the buffer, | ||
2134 | * assume that truncation occurred, and return NFS4ERR_RESOURCE. | ||
2135 | */ | ||
2136 | nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, page, &maxcount); | ||
2137 | if (nfserr == nfserr_isdir) | ||
2138 | return nfserr_inval; | ||
2139 | if (nfserr) | ||
2140 | return nfserr; | ||
2141 | |||
2142 | WRITE32(maxcount); | ||
2143 | ADJUST_ARGS(); | ||
2144 | resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base; | ||
2145 | |||
2146 | svc_take_page(resp->rqstp); | ||
2147 | resp->xbuf->tail[0].iov_base = | ||
2148 | page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); | ||
2149 | resp->rqstp->rq_restailpage = resp->rqstp->rq_resused-1; | ||
2150 | resp->xbuf->tail[0].iov_len = 0; | ||
2151 | resp->p = resp->xbuf->tail[0].iov_base; | ||
2152 | resp->end = resp->p + PAGE_SIZE/4; | ||
2153 | |||
2154 | resp->xbuf->page_len = maxcount; | ||
2155 | if (maxcount&3) { | ||
2156 | *(resp->p)++ = 0; | ||
2157 | resp->xbuf->tail[0].iov_base += maxcount&3; | ||
2158 | resp->xbuf->tail[0].iov_len = 4 - (maxcount&3); | ||
2159 | } | ||
2160 | return 0; | ||
2161 | } | ||
2162 | |||
2163 | static int | ||
2164 | nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_readdir *readdir) | ||
2165 | { | ||
2166 | int maxcount; | ||
2167 | loff_t offset; | ||
2168 | u32 *page, *savep; | ||
2169 | ENCODE_HEAD; | ||
2170 | |||
2171 | if (nfserr) | ||
2172 | return nfserr; | ||
2173 | if (resp->xbuf->page_len) | ||
2174 | return nfserr_resource; | ||
2175 | |||
2176 | RESERVE_SPACE(8); /* verifier */ | ||
2177 | savep = p; | ||
2178 | |||
2179 | /* XXX: Following NFSv3, we ignore the READDIR verifier for now. */ | ||
2180 | WRITE32(0); | ||
2181 | WRITE32(0); | ||
2182 | ADJUST_ARGS(); | ||
2183 | resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base; | ||
2184 | |||
2185 | maxcount = PAGE_SIZE; | ||
2186 | if (maxcount > readdir->rd_maxcount) | ||
2187 | maxcount = readdir->rd_maxcount; | ||
2188 | |||
2189 | /* | ||
2190 | * Convert from bytes to words, account for the two words already | ||
2191 | * written, make sure to leave two words at the end for the next | ||
2192 | * pointer and eof field. | ||
2193 | */ | ||
2194 | maxcount = (maxcount >> 2) - 4; | ||
2195 | if (maxcount < 0) { | ||
2196 | nfserr = nfserr_toosmall; | ||
2197 | goto err_no_verf; | ||
2198 | } | ||
2199 | |||
2200 | svc_take_page(resp->rqstp); | ||
2201 | page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); | ||
2202 | readdir->common.err = 0; | ||
2203 | readdir->buflen = maxcount; | ||
2204 | readdir->buffer = page; | ||
2205 | readdir->offset = NULL; | ||
2206 | |||
2207 | offset = readdir->rd_cookie; | ||
2208 | nfserr = nfsd_readdir(readdir->rd_rqstp, readdir->rd_fhp, | ||
2209 | &offset, | ||
2210 | &readdir->common, nfsd4_encode_dirent); | ||
2211 | if (nfserr == nfs_ok && | ||
2212 | readdir->common.err == nfserr_toosmall && | ||
2213 | readdir->buffer == page) | ||
2214 | nfserr = nfserr_toosmall; | ||
2215 | if (nfserr == nfserr_symlink) | ||
2216 | nfserr = nfserr_notdir; | ||
2217 | if (nfserr) | ||
2218 | goto err_no_verf; | ||
2219 | |||
2220 | if (readdir->offset) | ||
2221 | xdr_encode_hyper(readdir->offset, offset); | ||
2222 | |||
2223 | p = readdir->buffer; | ||
2224 | *p++ = 0; /* no more entries */ | ||
2225 | *p++ = htonl(readdir->common.err == nfserr_eof); | ||
2226 | resp->xbuf->page_len = ((char*)p) - (char*)page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); | ||
2227 | |||
2228 | /* allocate a page for the tail */ | ||
2229 | svc_take_page(resp->rqstp); | ||
2230 | resp->xbuf->tail[0].iov_base = | ||
2231 | page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); | ||
2232 | resp->rqstp->rq_restailpage = resp->rqstp->rq_resused-1; | ||
2233 | resp->xbuf->tail[0].iov_len = 0; | ||
2234 | resp->p = resp->xbuf->tail[0].iov_base; | ||
2235 | resp->end = resp->p + PAGE_SIZE/4; | ||
2236 | |||
2237 | return 0; | ||
2238 | err_no_verf: | ||
2239 | p = savep; | ||
2240 | ADJUST_ARGS(); | ||
2241 | return nfserr; | ||
2242 | } | ||
2243 | |||
2244 | static void | ||
2245 | nfsd4_encode_remove(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_remove *remove) | ||
2246 | { | ||
2247 | ENCODE_HEAD; | ||
2248 | |||
2249 | if (!nfserr) { | ||
2250 | RESERVE_SPACE(20); | ||
2251 | WRITECINFO(remove->rm_cinfo); | ||
2252 | ADJUST_ARGS(); | ||
2253 | } | ||
2254 | } | ||
2255 | |||
2256 | static void | ||
2257 | nfsd4_encode_rename(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_rename *rename) | ||
2258 | { | ||
2259 | ENCODE_HEAD; | ||
2260 | |||
2261 | if (!nfserr) { | ||
2262 | RESERVE_SPACE(40); | ||
2263 | WRITECINFO(rename->rn_sinfo); | ||
2264 | WRITECINFO(rename->rn_tinfo); | ||
2265 | ADJUST_ARGS(); | ||
2266 | } | ||
2267 | } | ||
2268 | |||
2269 | /* | ||
2270 | * The SETATTR encode routine is special -- it always encodes a bitmap, | ||
2271 | * regardless of the error status. | ||
2272 | */ | ||
2273 | static void | ||
2274 | nfsd4_encode_setattr(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_setattr *setattr) | ||
2275 | { | ||
2276 | ENCODE_HEAD; | ||
2277 | |||
2278 | RESERVE_SPACE(12); | ||
2279 | if (nfserr) { | ||
2280 | WRITE32(2); | ||
2281 | WRITE32(0); | ||
2282 | WRITE32(0); | ||
2283 | } | ||
2284 | else { | ||
2285 | WRITE32(2); | ||
2286 | WRITE32(setattr->sa_bmval[0]); | ||
2287 | WRITE32(setattr->sa_bmval[1]); | ||
2288 | } | ||
2289 | ADJUST_ARGS(); | ||
2290 | } | ||
2291 | |||
2292 | static void | ||
2293 | nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_setclientid *scd) | ||
2294 | { | ||
2295 | ENCODE_HEAD; | ||
2296 | |||
2297 | if (!nfserr) { | ||
2298 | RESERVE_SPACE(8 + sizeof(nfs4_verifier)); | ||
2299 | WRITEMEM(&scd->se_clientid, 8); | ||
2300 | WRITEMEM(&scd->se_confirm, sizeof(nfs4_verifier)); | ||
2301 | ADJUST_ARGS(); | ||
2302 | } | ||
2303 | else if (nfserr == nfserr_clid_inuse) { | ||
2304 | RESERVE_SPACE(8); | ||
2305 | WRITE32(0); | ||
2306 | WRITE32(0); | ||
2307 | ADJUST_ARGS(); | ||
2308 | } | ||
2309 | } | ||
2310 | |||
2311 | static void | ||
2312 | nfsd4_encode_write(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_write *write) | ||
2313 | { | ||
2314 | ENCODE_HEAD; | ||
2315 | |||
2316 | if (!nfserr) { | ||
2317 | RESERVE_SPACE(16); | ||
2318 | WRITE32(write->wr_bytes_written); | ||
2319 | WRITE32(write->wr_how_written); | ||
2320 | WRITEMEM(write->wr_verifier.data, 8); | ||
2321 | ADJUST_ARGS(); | ||
2322 | } | ||
2323 | } | ||
2324 | |||
2325 | void | ||
2326 | nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) | ||
2327 | { | ||
2328 | u32 *statp; | ||
2329 | ENCODE_HEAD; | ||
2330 | |||
2331 | RESERVE_SPACE(8); | ||
2332 | WRITE32(op->opnum); | ||
2333 | statp = p++; /* to be backfilled at the end */ | ||
2334 | ADJUST_ARGS(); | ||
2335 | |||
2336 | switch (op->opnum) { | ||
2337 | case OP_ACCESS: | ||
2338 | nfsd4_encode_access(resp, op->status, &op->u.access); | ||
2339 | break; | ||
2340 | case OP_CLOSE: | ||
2341 | nfsd4_encode_close(resp, op->status, &op->u.close); | ||
2342 | break; | ||
2343 | case OP_COMMIT: | ||
2344 | nfsd4_encode_commit(resp, op->status, &op->u.commit); | ||
2345 | break; | ||
2346 | case OP_CREATE: | ||
2347 | nfsd4_encode_create(resp, op->status, &op->u.create); | ||
2348 | break; | ||
2349 | case OP_DELEGRETURN: | ||
2350 | break; | ||
2351 | case OP_GETATTR: | ||
2352 | op->status = nfsd4_encode_getattr(resp, op->status, &op->u.getattr); | ||
2353 | break; | ||
2354 | case OP_GETFH: | ||
2355 | nfsd4_encode_getfh(resp, op->status, op->u.getfh); | ||
2356 | break; | ||
2357 | case OP_LINK: | ||
2358 | nfsd4_encode_link(resp, op->status, &op->u.link); | ||
2359 | break; | ||
2360 | case OP_LOCK: | ||
2361 | nfsd4_encode_lock(resp, op->status, &op->u.lock); | ||
2362 | break; | ||
2363 | case OP_LOCKT: | ||
2364 | nfsd4_encode_lockt(resp, op->status, &op->u.lockt); | ||
2365 | break; | ||
2366 | case OP_LOCKU: | ||
2367 | nfsd4_encode_locku(resp, op->status, &op->u.locku); | ||
2368 | break; | ||
2369 | case OP_LOOKUP: | ||
2370 | break; | ||
2371 | case OP_LOOKUPP: | ||
2372 | break; | ||
2373 | case OP_NVERIFY: | ||
2374 | break; | ||
2375 | case OP_OPEN: | ||
2376 | nfsd4_encode_open(resp, op->status, &op->u.open); | ||
2377 | break; | ||
2378 | case OP_OPEN_CONFIRM: | ||
2379 | nfsd4_encode_open_confirm(resp, op->status, &op->u.open_confirm); | ||
2380 | break; | ||
2381 | case OP_OPEN_DOWNGRADE: | ||
2382 | nfsd4_encode_open_downgrade(resp, op->status, &op->u.open_downgrade); | ||
2383 | break; | ||
2384 | case OP_PUTFH: | ||
2385 | break; | ||
2386 | case OP_PUTROOTFH: | ||
2387 | break; | ||
2388 | case OP_READ: | ||
2389 | op->status = nfsd4_encode_read(resp, op->status, &op->u.read); | ||
2390 | break; | ||
2391 | case OP_READDIR: | ||
2392 | op->status = nfsd4_encode_readdir(resp, op->status, &op->u.readdir); | ||
2393 | break; | ||
2394 | case OP_READLINK: | ||
2395 | op->status = nfsd4_encode_readlink(resp, op->status, &op->u.readlink); | ||
2396 | break; | ||
2397 | case OP_REMOVE: | ||
2398 | nfsd4_encode_remove(resp, op->status, &op->u.remove); | ||
2399 | break; | ||
2400 | case OP_RENAME: | ||
2401 | nfsd4_encode_rename(resp, op->status, &op->u.rename); | ||
2402 | break; | ||
2403 | case OP_RENEW: | ||
2404 | break; | ||
2405 | case OP_RESTOREFH: | ||
2406 | break; | ||
2407 | case OP_SAVEFH: | ||
2408 | break; | ||
2409 | case OP_SETATTR: | ||
2410 | nfsd4_encode_setattr(resp, op->status, &op->u.setattr); | ||
2411 | break; | ||
2412 | case OP_SETCLIENTID: | ||
2413 | nfsd4_encode_setclientid(resp, op->status, &op->u.setclientid); | ||
2414 | break; | ||
2415 | case OP_SETCLIENTID_CONFIRM: | ||
2416 | break; | ||
2417 | case OP_VERIFY: | ||
2418 | break; | ||
2419 | case OP_WRITE: | ||
2420 | nfsd4_encode_write(resp, op->status, &op->u.write); | ||
2421 | break; | ||
2422 | case OP_RELEASE_LOCKOWNER: | ||
2423 | break; | ||
2424 | default: | ||
2425 | break; | ||
2426 | } | ||
2427 | |||
2428 | /* | ||
2429 | * Note: We write the status directly, instead of using WRITE32(), | ||
2430 | * since it is already in network byte order. | ||
2431 | */ | ||
2432 | *statp = op->status; | ||
2433 | } | ||
2434 | |||
2435 | /* | ||
2436 | * Encode the reply stored in the stateowner reply cache | ||
2437 | * | ||
2438 | * XDR note: do not encode rp->rp_buflen: the buffer contains the | ||
2439 | * previously sent already encoded operation. | ||
2440 | * | ||
2441 | * called with nfs4_lock_state() held | ||
2442 | */ | ||
2443 | void | ||
2444 | nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op) | ||
2445 | { | ||
2446 | ENCODE_HEAD; | ||
2447 | struct nfs4_replay *rp = op->replay; | ||
2448 | |||
2449 | BUG_ON(!rp); | ||
2450 | |||
2451 | RESERVE_SPACE(8); | ||
2452 | WRITE32(op->opnum); | ||
2453 | *p++ = rp->rp_status; /* already xdr'ed */ | ||
2454 | ADJUST_ARGS(); | ||
2455 | |||
2456 | RESERVE_SPACE(rp->rp_buflen); | ||
2457 | WRITEMEM(rp->rp_buf, rp->rp_buflen); | ||
2458 | ADJUST_ARGS(); | ||
2459 | } | ||
2460 | |||
2461 | /* | ||
2462 | * END OF "GENERIC" ENCODE ROUTINES. | ||
2463 | */ | ||
2464 | |||
2465 | int | ||
2466 | nfs4svc_encode_voidres(struct svc_rqst *rqstp, u32 *p, void *dummy) | ||
2467 | { | ||
2468 | return xdr_ressize_check(rqstp, p); | ||
2469 | } | ||
2470 | |||
2471 | void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args) | ||
2472 | { | ||
2473 | if (args->ops != args->iops) { | ||
2474 | kfree(args->ops); | ||
2475 | args->ops = args->iops; | ||
2476 | } | ||
2477 | if (args->tmpp) { | ||
2478 | kfree(args->tmpp); | ||
2479 | args->tmpp = NULL; | ||
2480 | } | ||
2481 | while (args->to_free) { | ||
2482 | struct tmpbuf *tb = args->to_free; | ||
2483 | args->to_free = tb->next; | ||
2484 | tb->release(tb->buf); | ||
2485 | kfree(tb); | ||
2486 | } | ||
2487 | } | ||
2488 | |||
2489 | int | ||
2490 | nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoundargs *args) | ||
2491 | { | ||
2492 | int status; | ||
2493 | |||
2494 | args->p = p; | ||
2495 | args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len; | ||
2496 | args->pagelist = rqstp->rq_arg.pages; | ||
2497 | args->pagelen = rqstp->rq_arg.page_len; | ||
2498 | args->tmpp = NULL; | ||
2499 | args->to_free = NULL; | ||
2500 | args->ops = args->iops; | ||
2501 | args->rqstp = rqstp; | ||
2502 | |||
2503 | status = nfsd4_decode_compound(args); | ||
2504 | if (status) { | ||
2505 | nfsd4_release_compoundargs(args); | ||
2506 | } | ||
2507 | return !status; | ||
2508 | } | ||
2509 | |||
2510 | int | ||
2511 | nfs4svc_encode_compoundres(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoundres *resp) | ||
2512 | { | ||
2513 | /* | ||
2514 | * All that remains is to write the tag and operation count... | ||
2515 | */ | ||
2516 | struct kvec *iov; | ||
2517 | p = resp->tagp; | ||
2518 | *p++ = htonl(resp->taglen); | ||
2519 | memcpy(p, resp->tag, resp->taglen); | ||
2520 | p += XDR_QUADLEN(resp->taglen); | ||
2521 | *p++ = htonl(resp->opcnt); | ||
2522 | |||
2523 | if (rqstp->rq_res.page_len) | ||
2524 | iov = &rqstp->rq_res.tail[0]; | ||
2525 | else | ||
2526 | iov = &rqstp->rq_res.head[0]; | ||
2527 | iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base; | ||
2528 | BUG_ON(iov->iov_len > PAGE_SIZE); | ||
2529 | return 1; | ||
2530 | } | ||
2531 | |||
2532 | /* | ||
2533 | * Local variables: | ||
2534 | * c-basic-offset: 8 | ||
2535 | * End: | ||
2536 | */ | ||