1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * XDR support for nfsd/protocol version 3. |
4 | * |
5 | * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de> |
6 | * |
7 | * 2003-08-09 Jamie Lokier: Use htonl() for nanoseconds, not htons()! |
8 | */ |
9 | |
10 | #include <linux/namei.h> |
11 | #include <linux/sunrpc/svc_xprt.h> |
12 | #include "xdr3.h" |
13 | #include "auth.h" |
14 | #include "netns.h" |
15 | #include "vfs.h" |
16 | |
17 | /* |
18 | * Force construction of an empty post-op attr |
19 | */ |
20 | static const struct svc_fh nfs3svc_null_fh = { |
21 | .fh_no_wcc = true, |
22 | }; |
23 | |
24 | /* |
25 | * time_delta. {1, 0} means the server is accurate only |
26 | * to the nearest second. |
27 | */ |
28 | static const struct timespec64 nfs3svc_time_delta = { |
29 | .tv_sec = 1, |
30 | .tv_nsec = 0, |
31 | }; |
32 | |
33 | /* |
34 | * Mapping of S_IF* types to NFS file types |
35 | */ |
36 | static const u32 nfs3_ftypes[] = { |
37 | NF3NON, NF3FIFO, NF3CHR, NF3BAD, |
38 | NF3DIR, NF3BAD, NF3BLK, NF3BAD, |
39 | NF3REG, NF3BAD, NF3LNK, NF3BAD, |
40 | NF3SOCK, NF3BAD, NF3LNK, NF3BAD, |
41 | }; |
42 | |
43 | |
44 | /* |
45 | * Basic NFSv3 data types (RFC 1813 Sections 2.5 and 2.6) |
46 | */ |
47 | |
48 | static __be32 * |
49 | encode_nfstime3(__be32 *p, const struct timespec64 *time) |
50 | { |
51 | *p++ = cpu_to_be32((u32)time->tv_sec); |
52 | *p++ = cpu_to_be32(time->tv_nsec); |
53 | |
54 | return p; |
55 | } |
56 | |
57 | static bool |
58 | svcxdr_decode_nfstime3(struct xdr_stream *xdr, struct timespec64 *timep) |
59 | { |
60 | __be32 *p; |
61 | |
62 | p = xdr_inline_decode(xdr, XDR_UNIT * 2); |
63 | if (!p) |
64 | return false; |
65 | timep->tv_sec = be32_to_cpup(p: p++); |
66 | timep->tv_nsec = be32_to_cpup(p); |
67 | |
68 | return true; |
69 | } |
70 | |
71 | /** |
72 | * svcxdr_decode_nfs_fh3 - Decode an NFSv3 file handle |
73 | * @xdr: XDR stream positioned at an undecoded NFSv3 FH |
74 | * @fhp: OUT: filled-in server file handle |
75 | * |
76 | * Return values: |
77 | * %false: The encoded file handle was not valid |
78 | * %true: @fhp has been initialized |
79 | */ |
80 | bool |
81 | svcxdr_decode_nfs_fh3(struct xdr_stream *xdr, struct svc_fh *fhp) |
82 | { |
83 | __be32 *p; |
84 | u32 size; |
85 | |
86 | if (xdr_stream_decode_u32(xdr, ptr: &size) < 0) |
87 | return false; |
88 | if (size == 0 || size > NFS3_FHSIZE) |
89 | return false; |
90 | p = xdr_inline_decode(xdr, nbytes: size); |
91 | if (!p) |
92 | return false; |
93 | fh_init(fhp, NFS3_FHSIZE); |
94 | fhp->fh_handle.fh_size = size; |
95 | memcpy(&fhp->fh_handle.fh_raw, p, size); |
96 | |
97 | return true; |
98 | } |
99 | |
100 | /** |
101 | * svcxdr_encode_nfsstat3 - Encode an NFSv3 status code |
102 | * @xdr: XDR stream |
103 | * @status: status value to encode |
104 | * |
105 | * Return values: |
106 | * %false: Send buffer space was exhausted |
107 | * %true: Success |
108 | */ |
109 | bool |
110 | svcxdr_encode_nfsstat3(struct xdr_stream *xdr, __be32 status) |
111 | { |
112 | __be32 *p; |
113 | |
114 | p = xdr_reserve_space(xdr, nbytes: sizeof(status)); |
115 | if (!p) |
116 | return false; |
117 | *p = status; |
118 | |
119 | return true; |
120 | } |
121 | |
122 | static bool |
123 | svcxdr_encode_nfs_fh3(struct xdr_stream *xdr, const struct svc_fh *fhp) |
124 | { |
125 | u32 size = fhp->fh_handle.fh_size; |
126 | __be32 *p; |
127 | |
128 | p = xdr_reserve_space(xdr, XDR_UNIT + size); |
129 | if (!p) |
130 | return false; |
131 | *p++ = cpu_to_be32(size); |
132 | if (size) |
133 | p[XDR_QUADLEN(size) - 1] = 0; |
134 | memcpy(p, &fhp->fh_handle.fh_raw, size); |
135 | |
136 | return true; |
137 | } |
138 | |
139 | static bool |
140 | svcxdr_encode_post_op_fh3(struct xdr_stream *xdr, const struct svc_fh *fhp) |
141 | { |
142 | if (xdr_stream_encode_item_present(xdr) < 0) |
143 | return false; |
144 | if (!svcxdr_encode_nfs_fh3(xdr, fhp)) |
145 | return false; |
146 | |
147 | return true; |
148 | } |
149 | |
150 | static bool |
151 | svcxdr_encode_cookieverf3(struct xdr_stream *xdr, const __be32 *verf) |
152 | { |
153 | __be32 *p; |
154 | |
155 | p = xdr_reserve_space(xdr, NFS3_COOKIEVERFSIZE); |
156 | if (!p) |
157 | return false; |
158 | memcpy(p, verf, NFS3_COOKIEVERFSIZE); |
159 | |
160 | return true; |
161 | } |
162 | |
163 | static bool |
164 | svcxdr_encode_writeverf3(struct xdr_stream *xdr, const __be32 *verf) |
165 | { |
166 | __be32 *p; |
167 | |
168 | p = xdr_reserve_space(xdr, NFS3_WRITEVERFSIZE); |
169 | if (!p) |
170 | return false; |
171 | memcpy(p, verf, NFS3_WRITEVERFSIZE); |
172 | |
173 | return true; |
174 | } |
175 | |
176 | static bool |
177 | svcxdr_decode_filename3(struct xdr_stream *xdr, char **name, unsigned int *len) |
178 | { |
179 | u32 size, i; |
180 | __be32 *p; |
181 | char *c; |
182 | |
183 | if (xdr_stream_decode_u32(xdr, ptr: &size) < 0) |
184 | return false; |
185 | if (size == 0 || size > NFS3_MAXNAMLEN) |
186 | return false; |
187 | p = xdr_inline_decode(xdr, nbytes: size); |
188 | if (!p) |
189 | return false; |
190 | |
191 | *len = size; |
192 | *name = (char *)p; |
193 | for (i = 0, c = *name; i < size; i++, c++) { |
194 | if (*c == '\0' || *c == '/') |
195 | return false; |
196 | } |
197 | |
198 | return true; |
199 | } |
200 | |
201 | static bool |
202 | svcxdr_decode_diropargs3(struct xdr_stream *xdr, struct svc_fh *fhp, |
203 | char **name, unsigned int *len) |
204 | { |
205 | return svcxdr_decode_nfs_fh3(xdr, fhp) && |
206 | svcxdr_decode_filename3(xdr, name, len); |
207 | } |
208 | |
209 | static bool |
210 | svcxdr_decode_sattr3(struct svc_rqst *rqstp, struct xdr_stream *xdr, |
211 | struct iattr *iap) |
212 | { |
213 | u32 set_it; |
214 | |
215 | iap->ia_valid = 0; |
216 | |
217 | if (xdr_stream_decode_bool(xdr, ptr: &set_it) < 0) |
218 | return false; |
219 | if (set_it) { |
220 | u32 mode; |
221 | |
222 | if (xdr_stream_decode_u32(xdr, ptr: &mode) < 0) |
223 | return false; |
224 | iap->ia_valid |= ATTR_MODE; |
225 | iap->ia_mode = mode; |
226 | } |
227 | if (xdr_stream_decode_bool(xdr, ptr: &set_it) < 0) |
228 | return false; |
229 | if (set_it) { |
230 | u32 uid; |
231 | |
232 | if (xdr_stream_decode_u32(xdr, ptr: &uid) < 0) |
233 | return false; |
234 | iap->ia_uid = make_kuid(from: nfsd_user_namespace(rqstp), uid); |
235 | if (uid_valid(uid: iap->ia_uid)) |
236 | iap->ia_valid |= ATTR_UID; |
237 | } |
238 | if (xdr_stream_decode_bool(xdr, ptr: &set_it) < 0) |
239 | return false; |
240 | if (set_it) { |
241 | u32 gid; |
242 | |
243 | if (xdr_stream_decode_u32(xdr, ptr: &gid) < 0) |
244 | return false; |
245 | iap->ia_gid = make_kgid(from: nfsd_user_namespace(rqstp), gid); |
246 | if (gid_valid(gid: iap->ia_gid)) |
247 | iap->ia_valid |= ATTR_GID; |
248 | } |
249 | if (xdr_stream_decode_bool(xdr, ptr: &set_it) < 0) |
250 | return false; |
251 | if (set_it) { |
252 | u64 newsize; |
253 | |
254 | if (xdr_stream_decode_u64(xdr, ptr: &newsize) < 0) |
255 | return false; |
256 | iap->ia_valid |= ATTR_SIZE; |
257 | iap->ia_size = newsize; |
258 | } |
259 | if (xdr_stream_decode_u32(xdr, ptr: &set_it) < 0) |
260 | return false; |
261 | switch (set_it) { |
262 | case DONT_CHANGE: |
263 | break; |
264 | case SET_TO_SERVER_TIME: |
265 | iap->ia_valid |= ATTR_ATIME; |
266 | break; |
267 | case SET_TO_CLIENT_TIME: |
268 | if (!svcxdr_decode_nfstime3(xdr, timep: &iap->ia_atime)) |
269 | return false; |
270 | iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET; |
271 | break; |
272 | default: |
273 | return false; |
274 | } |
275 | if (xdr_stream_decode_u32(xdr, ptr: &set_it) < 0) |
276 | return false; |
277 | switch (set_it) { |
278 | case DONT_CHANGE: |
279 | break; |
280 | case SET_TO_SERVER_TIME: |
281 | iap->ia_valid |= ATTR_MTIME; |
282 | break; |
283 | case SET_TO_CLIENT_TIME: |
284 | if (!svcxdr_decode_nfstime3(xdr, timep: &iap->ia_mtime)) |
285 | return false; |
286 | iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET; |
287 | break; |
288 | default: |
289 | return false; |
290 | } |
291 | |
292 | return true; |
293 | } |
294 | |
295 | static bool |
296 | svcxdr_decode_sattrguard3(struct xdr_stream *xdr, struct nfsd3_sattrargs *args) |
297 | { |
298 | __be32 *p; |
299 | u32 check; |
300 | |
301 | if (xdr_stream_decode_bool(xdr, ptr: &check) < 0) |
302 | return false; |
303 | if (check) { |
304 | p = xdr_inline_decode(xdr, XDR_UNIT * 2); |
305 | if (!p) |
306 | return false; |
307 | args->check_guard = 1; |
308 | args->guardtime = be32_to_cpup(p); |
309 | } else |
310 | args->check_guard = 0; |
311 | |
312 | return true; |
313 | } |
314 | |
315 | static bool |
316 | svcxdr_decode_specdata3(struct xdr_stream *xdr, struct nfsd3_mknodargs *args) |
317 | { |
318 | __be32 *p; |
319 | |
320 | p = xdr_inline_decode(xdr, XDR_UNIT * 2); |
321 | if (!p) |
322 | return false; |
323 | args->major = be32_to_cpup(p: p++); |
324 | args->minor = be32_to_cpup(p); |
325 | |
326 | return true; |
327 | } |
328 | |
329 | static bool |
330 | svcxdr_decode_devicedata3(struct svc_rqst *rqstp, struct xdr_stream *xdr, |
331 | struct nfsd3_mknodargs *args) |
332 | { |
333 | return svcxdr_decode_sattr3(rqstp, xdr, iap: &args->attrs) && |
334 | svcxdr_decode_specdata3(xdr, args); |
335 | } |
336 | |
337 | static bool |
338 | svcxdr_encode_fattr3(struct svc_rqst *rqstp, struct xdr_stream *xdr, |
339 | const struct svc_fh *fhp, const struct kstat *stat) |
340 | { |
341 | struct user_namespace *userns = nfsd_user_namespace(rqstp); |
342 | __be32 *p; |
343 | u64 fsid; |
344 | |
345 | p = xdr_reserve_space(xdr, XDR_UNIT * 21); |
346 | if (!p) |
347 | return false; |
348 | |
349 | *p++ = cpu_to_be32(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]); |
350 | *p++ = cpu_to_be32((u32)(stat->mode & S_IALLUGO)); |
351 | *p++ = cpu_to_be32((u32)stat->nlink); |
352 | *p++ = cpu_to_be32((u32)from_kuid_munged(userns, stat->uid)); |
353 | *p++ = cpu_to_be32((u32)from_kgid_munged(userns, stat->gid)); |
354 | if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) |
355 | p = xdr_encode_hyper(p, val: (u64)NFS3_MAXPATHLEN); |
356 | else |
357 | p = xdr_encode_hyper(p, val: (u64)stat->size); |
358 | |
359 | /* used */ |
360 | p = xdr_encode_hyper(p, val: ((u64)stat->blocks) << 9); |
361 | |
362 | /* rdev */ |
363 | *p++ = cpu_to_be32((u32)MAJOR(stat->rdev)); |
364 | *p++ = cpu_to_be32((u32)MINOR(stat->rdev)); |
365 | |
366 | switch(fsid_source(fhp)) { |
367 | case FSIDSOURCE_FSID: |
368 | fsid = (u64)fhp->fh_export->ex_fsid; |
369 | break; |
370 | case FSIDSOURCE_UUID: |
371 | fsid = ((u64 *)fhp->fh_export->ex_uuid)[0]; |
372 | fsid ^= ((u64 *)fhp->fh_export->ex_uuid)[1]; |
373 | break; |
374 | default: |
375 | fsid = (u64)huge_encode_dev(dev: fhp->fh_dentry->d_sb->s_dev); |
376 | } |
377 | p = xdr_encode_hyper(p, val: fsid); |
378 | |
379 | /* fileid */ |
380 | p = xdr_encode_hyper(p, val: stat->ino); |
381 | |
382 | p = encode_nfstime3(p, time: &stat->atime); |
383 | p = encode_nfstime3(p, time: &stat->mtime); |
384 | encode_nfstime3(p, time: &stat->ctime); |
385 | |
386 | return true; |
387 | } |
388 | |
389 | static bool |
390 | svcxdr_encode_wcc_attr(struct xdr_stream *xdr, const struct svc_fh *fhp) |
391 | { |
392 | __be32 *p; |
393 | |
394 | p = xdr_reserve_space(xdr, XDR_UNIT * 6); |
395 | if (!p) |
396 | return false; |
397 | p = xdr_encode_hyper(p, val: (u64)fhp->fh_pre_size); |
398 | p = encode_nfstime3(p, time: &fhp->fh_pre_mtime); |
399 | encode_nfstime3(p, time: &fhp->fh_pre_ctime); |
400 | |
401 | return true; |
402 | } |
403 | |
404 | static bool |
405 | svcxdr_encode_pre_op_attr(struct xdr_stream *xdr, const struct svc_fh *fhp) |
406 | { |
407 | if (!fhp->fh_pre_saved) { |
408 | if (xdr_stream_encode_item_absent(xdr) < 0) |
409 | return false; |
410 | return true; |
411 | } |
412 | |
413 | if (xdr_stream_encode_item_present(xdr) < 0) |
414 | return false; |
415 | return svcxdr_encode_wcc_attr(xdr, fhp); |
416 | } |
417 | |
418 | /** |
419 | * svcxdr_encode_post_op_attr - Encode NFSv3 post-op attributes |
420 | * @rqstp: Context of a completed RPC transaction |
421 | * @xdr: XDR stream |
422 | * @fhp: File handle to encode |
423 | * |
424 | * Return values: |
425 | * %false: Send buffer space was exhausted |
426 | * %true: Success |
427 | */ |
428 | bool |
429 | svcxdr_encode_post_op_attr(struct svc_rqst *rqstp, struct xdr_stream *xdr, |
430 | const struct svc_fh *fhp) |
431 | { |
432 | struct dentry *dentry = fhp->fh_dentry; |
433 | struct kstat stat; |
434 | |
435 | /* |
436 | * The inode may be NULL if the call failed because of a |
437 | * stale file handle. In this case, no attributes are |
438 | * returned. |
439 | */ |
440 | if (fhp->fh_no_wcc || !dentry || !d_really_is_positive(dentry)) |
441 | goto no_post_op_attrs; |
442 | if (fh_getattr(fh: fhp, stat: &stat) != nfs_ok) |
443 | goto no_post_op_attrs; |
444 | |
445 | if (xdr_stream_encode_item_present(xdr) < 0) |
446 | return false; |
447 | lease_get_mtime(d_inode(dentry), time: &stat.mtime); |
448 | if (!svcxdr_encode_fattr3(rqstp, xdr, fhp, stat: &stat)) |
449 | return false; |
450 | |
451 | return true; |
452 | |
453 | no_post_op_attrs: |
454 | return xdr_stream_encode_item_absent(xdr) > 0; |
455 | } |
456 | |
457 | /* |
458 | * Encode weak cache consistency data |
459 | */ |
460 | static bool |
461 | svcxdr_encode_wcc_data(struct svc_rqst *rqstp, struct xdr_stream *xdr, |
462 | const struct svc_fh *fhp) |
463 | { |
464 | struct dentry *dentry = fhp->fh_dentry; |
465 | |
466 | if (!dentry || !d_really_is_positive(dentry) || !fhp->fh_post_saved) |
467 | goto neither; |
468 | |
469 | /* before */ |
470 | if (!svcxdr_encode_pre_op_attr(xdr, fhp)) |
471 | return false; |
472 | |
473 | /* after */ |
474 | if (xdr_stream_encode_item_present(xdr) < 0) |
475 | return false; |
476 | if (!svcxdr_encode_fattr3(rqstp, xdr, fhp, stat: &fhp->fh_post_attr)) |
477 | return false; |
478 | |
479 | return true; |
480 | |
481 | neither: |
482 | if (xdr_stream_encode_item_absent(xdr) < 0) |
483 | return false; |
484 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp)) |
485 | return false; |
486 | |
487 | return true; |
488 | } |
489 | |
490 | /* |
491 | * XDR decode functions |
492 | */ |
493 | |
494 | bool |
495 | nfs3svc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
496 | { |
497 | struct nfsd_fhandle *args = rqstp->rq_argp; |
498 | |
499 | return svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh); |
500 | } |
501 | |
502 | bool |
503 | nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
504 | { |
505 | struct nfsd3_sattrargs *args = rqstp->rq_argp; |
506 | |
507 | return svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh) && |
508 | svcxdr_decode_sattr3(rqstp, xdr, iap: &args->attrs) && |
509 | svcxdr_decode_sattrguard3(xdr, args); |
510 | } |
511 | |
512 | bool |
513 | nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
514 | { |
515 | struct nfsd3_diropargs *args = rqstp->rq_argp; |
516 | |
517 | return svcxdr_decode_diropargs3(xdr, fhp: &args->fh, name: &args->name, len: &args->len); |
518 | } |
519 | |
520 | bool |
521 | nfs3svc_decode_accessargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
522 | { |
523 | struct nfsd3_accessargs *args = rqstp->rq_argp; |
524 | |
525 | if (!svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh)) |
526 | return false; |
527 | if (xdr_stream_decode_u32(xdr, ptr: &args->access) < 0) |
528 | return false; |
529 | |
530 | return true; |
531 | } |
532 | |
533 | bool |
534 | nfs3svc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
535 | { |
536 | struct nfsd3_readargs *args = rqstp->rq_argp; |
537 | |
538 | if (!svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh)) |
539 | return false; |
540 | if (xdr_stream_decode_u64(xdr, ptr: &args->offset) < 0) |
541 | return false; |
542 | if (xdr_stream_decode_u32(xdr, ptr: &args->count) < 0) |
543 | return false; |
544 | |
545 | return true; |
546 | } |
547 | |
548 | bool |
549 | nfs3svc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
550 | { |
551 | struct nfsd3_writeargs *args = rqstp->rq_argp; |
552 | u32 max_blocksize = svc_max_payload(rqstp); |
553 | |
554 | if (!svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh)) |
555 | return false; |
556 | if (xdr_stream_decode_u64(xdr, ptr: &args->offset) < 0) |
557 | return false; |
558 | if (xdr_stream_decode_u32(xdr, ptr: &args->count) < 0) |
559 | return false; |
560 | if (xdr_stream_decode_u32(xdr, ptr: &args->stable) < 0) |
561 | return false; |
562 | |
563 | /* opaque data */ |
564 | if (xdr_stream_decode_u32(xdr, ptr: &args->len) < 0) |
565 | return false; |
566 | |
567 | /* request sanity */ |
568 | if (args->count != args->len) |
569 | return false; |
570 | if (args->count > max_blocksize) { |
571 | args->count = max_blocksize; |
572 | args->len = max_blocksize; |
573 | } |
574 | |
575 | return xdr_stream_subsegment(xdr, subbuf: &args->payload, len: args->count); |
576 | } |
577 | |
578 | bool |
579 | nfs3svc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
580 | { |
581 | struct nfsd3_createargs *args = rqstp->rq_argp; |
582 | |
583 | if (!svcxdr_decode_diropargs3(xdr, fhp: &args->fh, name: &args->name, len: &args->len)) |
584 | return false; |
585 | if (xdr_stream_decode_u32(xdr, ptr: &args->createmode) < 0) |
586 | return false; |
587 | switch (args->createmode) { |
588 | case NFS3_CREATE_UNCHECKED: |
589 | case NFS3_CREATE_GUARDED: |
590 | return svcxdr_decode_sattr3(rqstp, xdr, iap: &args->attrs); |
591 | case NFS3_CREATE_EXCLUSIVE: |
592 | args->verf = xdr_inline_decode(xdr, NFS3_CREATEVERFSIZE); |
593 | if (!args->verf) |
594 | return false; |
595 | break; |
596 | default: |
597 | return false; |
598 | } |
599 | return true; |
600 | } |
601 | |
602 | bool |
603 | nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
604 | { |
605 | struct nfsd3_createargs *args = rqstp->rq_argp; |
606 | |
607 | return svcxdr_decode_diropargs3(xdr, fhp: &args->fh, |
608 | name: &args->name, len: &args->len) && |
609 | svcxdr_decode_sattr3(rqstp, xdr, iap: &args->attrs); |
610 | } |
611 | |
612 | bool |
613 | nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
614 | { |
615 | struct nfsd3_symlinkargs *args = rqstp->rq_argp; |
616 | struct kvec *head = rqstp->rq_arg.head; |
617 | |
618 | if (!svcxdr_decode_diropargs3(xdr, fhp: &args->ffh, name: &args->fname, len: &args->flen)) |
619 | return false; |
620 | if (!svcxdr_decode_sattr3(rqstp, xdr, iap: &args->attrs)) |
621 | return false; |
622 | if (xdr_stream_decode_u32(xdr, ptr: &args->tlen) < 0) |
623 | return false; |
624 | |
625 | /* symlink_data */ |
626 | args->first.iov_len = head->iov_len - xdr_stream_pos(xdr); |
627 | args->first.iov_base = xdr_inline_decode(xdr, nbytes: args->tlen); |
628 | return args->first.iov_base != NULL; |
629 | } |
630 | |
631 | bool |
632 | nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
633 | { |
634 | struct nfsd3_mknodargs *args = rqstp->rq_argp; |
635 | |
636 | if (!svcxdr_decode_diropargs3(xdr, fhp: &args->fh, name: &args->name, len: &args->len)) |
637 | return false; |
638 | if (xdr_stream_decode_u32(xdr, ptr: &args->ftype) < 0) |
639 | return false; |
640 | switch (args->ftype) { |
641 | case NF3CHR: |
642 | case NF3BLK: |
643 | return svcxdr_decode_devicedata3(rqstp, xdr, args); |
644 | case NF3SOCK: |
645 | case NF3FIFO: |
646 | return svcxdr_decode_sattr3(rqstp, xdr, iap: &args->attrs); |
647 | case NF3REG: |
648 | case NF3DIR: |
649 | case NF3LNK: |
650 | /* Valid XDR but illegal file types */ |
651 | break; |
652 | default: |
653 | return false; |
654 | } |
655 | |
656 | return true; |
657 | } |
658 | |
659 | bool |
660 | nfs3svc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
661 | { |
662 | struct nfsd3_renameargs *args = rqstp->rq_argp; |
663 | |
664 | return svcxdr_decode_diropargs3(xdr, fhp: &args->ffh, |
665 | name: &args->fname, len: &args->flen) && |
666 | svcxdr_decode_diropargs3(xdr, fhp: &args->tfh, |
667 | name: &args->tname, len: &args->tlen); |
668 | } |
669 | |
670 | bool |
671 | nfs3svc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
672 | { |
673 | struct nfsd3_linkargs *args = rqstp->rq_argp; |
674 | |
675 | return svcxdr_decode_nfs_fh3(xdr, fhp: &args->ffh) && |
676 | svcxdr_decode_diropargs3(xdr, fhp: &args->tfh, |
677 | name: &args->tname, len: &args->tlen); |
678 | } |
679 | |
680 | bool |
681 | nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
682 | { |
683 | struct nfsd3_readdirargs *args = rqstp->rq_argp; |
684 | |
685 | if (!svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh)) |
686 | return false; |
687 | if (xdr_stream_decode_u64(xdr, ptr: &args->cookie) < 0) |
688 | return false; |
689 | args->verf = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE); |
690 | if (!args->verf) |
691 | return false; |
692 | if (xdr_stream_decode_u32(xdr, ptr: &args->count) < 0) |
693 | return false; |
694 | |
695 | return true; |
696 | } |
697 | |
698 | bool |
699 | nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
700 | { |
701 | struct nfsd3_readdirargs *args = rqstp->rq_argp; |
702 | u32 dircount; |
703 | |
704 | if (!svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh)) |
705 | return false; |
706 | if (xdr_stream_decode_u64(xdr, ptr: &args->cookie) < 0) |
707 | return false; |
708 | args->verf = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE); |
709 | if (!args->verf) |
710 | return false; |
711 | /* dircount is ignored */ |
712 | if (xdr_stream_decode_u32(xdr, ptr: &dircount) < 0) |
713 | return false; |
714 | if (xdr_stream_decode_u32(xdr, ptr: &args->count) < 0) |
715 | return false; |
716 | |
717 | return true; |
718 | } |
719 | |
720 | bool |
721 | nfs3svc_decode_commitargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
722 | { |
723 | struct nfsd3_commitargs *args = rqstp->rq_argp; |
724 | |
725 | if (!svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh)) |
726 | return false; |
727 | if (xdr_stream_decode_u64(xdr, ptr: &args->offset) < 0) |
728 | return false; |
729 | if (xdr_stream_decode_u32(xdr, ptr: &args->count) < 0) |
730 | return false; |
731 | |
732 | return true; |
733 | } |
734 | |
735 | /* |
736 | * XDR encode functions |
737 | */ |
738 | |
739 | /* GETATTR */ |
740 | bool |
741 | nfs3svc_encode_getattrres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
742 | { |
743 | struct nfsd3_attrstat *resp = rqstp->rq_resp; |
744 | |
745 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
746 | return false; |
747 | switch (resp->status) { |
748 | case nfs_ok: |
749 | lease_get_mtime(d_inode(dentry: resp->fh.fh_dentry), time: &resp->stat.mtime); |
750 | if (!svcxdr_encode_fattr3(rqstp, xdr, fhp: &resp->fh, stat: &resp->stat)) |
751 | return false; |
752 | break; |
753 | } |
754 | |
755 | return true; |
756 | } |
757 | |
758 | /* SETATTR, REMOVE, RMDIR */ |
759 | bool |
760 | nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
761 | { |
762 | struct nfsd3_attrstat *resp = rqstp->rq_resp; |
763 | |
764 | return svcxdr_encode_nfsstat3(xdr, status: resp->status) && |
765 | svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->fh); |
766 | } |
767 | |
768 | /* LOOKUP */ |
769 | bool |
770 | nfs3svc_encode_lookupres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
771 | { |
772 | struct nfsd3_diropres *resp = rqstp->rq_resp; |
773 | |
774 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
775 | return false; |
776 | switch (resp->status) { |
777 | case nfs_ok: |
778 | if (!svcxdr_encode_nfs_fh3(xdr, fhp: &resp->fh)) |
779 | return false; |
780 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
781 | return false; |
782 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->dirfh)) |
783 | return false; |
784 | break; |
785 | default: |
786 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->dirfh)) |
787 | return false; |
788 | } |
789 | |
790 | return true; |
791 | } |
792 | |
793 | /* ACCESS */ |
794 | bool |
795 | nfs3svc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
796 | { |
797 | struct nfsd3_accessres *resp = rqstp->rq_resp; |
798 | |
799 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
800 | return false; |
801 | switch (resp->status) { |
802 | case nfs_ok: |
803 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
804 | return false; |
805 | if (xdr_stream_encode_u32(xdr, n: resp->access) < 0) |
806 | return false; |
807 | break; |
808 | default: |
809 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
810 | return false; |
811 | } |
812 | |
813 | return true; |
814 | } |
815 | |
816 | /* READLINK */ |
817 | bool |
818 | nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
819 | { |
820 | struct nfsd3_readlinkres *resp = rqstp->rq_resp; |
821 | struct kvec *head = rqstp->rq_res.head; |
822 | |
823 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
824 | return false; |
825 | switch (resp->status) { |
826 | case nfs_ok: |
827 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
828 | return false; |
829 | if (xdr_stream_encode_u32(xdr, n: resp->len) < 0) |
830 | return false; |
831 | svcxdr_encode_opaque_pages(rqstp, xdr, pages: resp->pages, base: 0, |
832 | len: resp->len); |
833 | if (svc_encode_result_payload(rqstp, offset: head->iov_len, length: resp->len) < 0) |
834 | return false; |
835 | break; |
836 | default: |
837 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
838 | return false; |
839 | } |
840 | |
841 | return true; |
842 | } |
843 | |
844 | /* READ */ |
845 | bool |
846 | nfs3svc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
847 | { |
848 | struct nfsd3_readres *resp = rqstp->rq_resp; |
849 | struct kvec *head = rqstp->rq_res.head; |
850 | |
851 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
852 | return false; |
853 | switch (resp->status) { |
854 | case nfs_ok: |
855 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
856 | return false; |
857 | if (xdr_stream_encode_u32(xdr, n: resp->count) < 0) |
858 | return false; |
859 | if (xdr_stream_encode_bool(xdr, n: resp->eof) < 0) |
860 | return false; |
861 | if (xdr_stream_encode_u32(xdr, n: resp->count) < 0) |
862 | return false; |
863 | svcxdr_encode_opaque_pages(rqstp, xdr, pages: resp->pages, |
864 | base: rqstp->rq_res.page_base, |
865 | len: resp->count); |
866 | if (svc_encode_result_payload(rqstp, offset: head->iov_len, length: resp->count) < 0) |
867 | return false; |
868 | break; |
869 | default: |
870 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
871 | return false; |
872 | } |
873 | |
874 | return true; |
875 | } |
876 | |
877 | /* WRITE */ |
878 | bool |
879 | nfs3svc_encode_writeres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
880 | { |
881 | struct nfsd3_writeres *resp = rqstp->rq_resp; |
882 | |
883 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
884 | return false; |
885 | switch (resp->status) { |
886 | case nfs_ok: |
887 | if (!svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->fh)) |
888 | return false; |
889 | if (xdr_stream_encode_u32(xdr, n: resp->count) < 0) |
890 | return false; |
891 | if (xdr_stream_encode_u32(xdr, n: resp->committed) < 0) |
892 | return false; |
893 | if (!svcxdr_encode_writeverf3(xdr, verf: resp->verf)) |
894 | return false; |
895 | break; |
896 | default: |
897 | if (!svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->fh)) |
898 | return false; |
899 | } |
900 | |
901 | return true; |
902 | } |
903 | |
904 | /* CREATE, MKDIR, SYMLINK, MKNOD */ |
905 | bool |
906 | nfs3svc_encode_createres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
907 | { |
908 | struct nfsd3_diropres *resp = rqstp->rq_resp; |
909 | |
910 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
911 | return false; |
912 | switch (resp->status) { |
913 | case nfs_ok: |
914 | if (!svcxdr_encode_post_op_fh3(xdr, fhp: &resp->fh)) |
915 | return false; |
916 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
917 | return false; |
918 | if (!svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->dirfh)) |
919 | return false; |
920 | break; |
921 | default: |
922 | if (!svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->dirfh)) |
923 | return false; |
924 | } |
925 | |
926 | return true; |
927 | } |
928 | |
929 | /* RENAME */ |
930 | bool |
931 | nfs3svc_encode_renameres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
932 | { |
933 | struct nfsd3_renameres *resp = rqstp->rq_resp; |
934 | |
935 | return svcxdr_encode_nfsstat3(xdr, status: resp->status) && |
936 | svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->ffh) && |
937 | svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->tfh); |
938 | } |
939 | |
940 | /* LINK */ |
941 | bool |
942 | nfs3svc_encode_linkres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
943 | { |
944 | struct nfsd3_linkres *resp = rqstp->rq_resp; |
945 | |
946 | return svcxdr_encode_nfsstat3(xdr, status: resp->status) && |
947 | svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh) && |
948 | svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->tfh); |
949 | } |
950 | |
951 | /* READDIR */ |
952 | bool |
953 | nfs3svc_encode_readdirres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
954 | { |
955 | struct nfsd3_readdirres *resp = rqstp->rq_resp; |
956 | struct xdr_buf *dirlist = &resp->dirlist; |
957 | |
958 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
959 | return false; |
960 | switch (resp->status) { |
961 | case nfs_ok: |
962 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
963 | return false; |
964 | if (!svcxdr_encode_cookieverf3(xdr, verf: resp->verf)) |
965 | return false; |
966 | svcxdr_encode_opaque_pages(rqstp, xdr, pages: dirlist->pages, base: 0, |
967 | len: dirlist->len); |
968 | /* no more entries */ |
969 | if (xdr_stream_encode_item_absent(xdr) < 0) |
970 | return false; |
971 | if (xdr_stream_encode_bool(xdr, n: resp->common.err == nfserr_eof) < 0) |
972 | return false; |
973 | break; |
974 | default: |
975 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
976 | return false; |
977 | } |
978 | |
979 | return true; |
980 | } |
981 | |
982 | static __be32 |
983 | compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp, |
984 | const char *name, int namlen, u64 ino) |
985 | { |
986 | struct svc_export *exp; |
987 | struct dentry *dparent, *dchild; |
988 | __be32 rv = nfserr_noent; |
989 | |
990 | dparent = cd->fh.fh_dentry; |
991 | exp = cd->fh.fh_export; |
992 | |
993 | if (isdotent(name, namlen)) { |
994 | if (namlen == 2) { |
995 | dchild = dget_parent(dentry: dparent); |
996 | /* |
997 | * Don't return filehandle for ".." if we're at |
998 | * the filesystem or export root: |
999 | */ |
1000 | if (dchild == dparent) |
1001 | goto out; |
1002 | if (dparent == exp->ex_path.dentry) |
1003 | goto out; |
1004 | } else |
1005 | dchild = dget(dentry: dparent); |
1006 | } else |
1007 | dchild = lookup_positive_unlocked(name, dparent, namlen); |
1008 | if (IS_ERR(ptr: dchild)) |
1009 | return rv; |
1010 | if (d_mountpoint(dentry: dchild)) |
1011 | goto out; |
1012 | if (dchild->d_inode->i_ino != ino) |
1013 | goto out; |
1014 | rv = fh_compose(fhp, exp, dchild, &cd->fh); |
1015 | out: |
1016 | dput(dchild); |
1017 | return rv; |
1018 | } |
1019 | |
1020 | /** |
1021 | * nfs3svc_encode_cookie3 - Encode a directory offset cookie |
1022 | * @resp: readdir result context |
1023 | * @offset: offset cookie to encode |
1024 | * |
1025 | * The buffer space for the offset cookie has already been reserved |
1026 | * by svcxdr_encode_entry3_common(). |
1027 | */ |
1028 | void nfs3svc_encode_cookie3(struct nfsd3_readdirres *resp, u64 offset) |
1029 | { |
1030 | __be64 cookie = cpu_to_be64(offset); |
1031 | |
1032 | if (!resp->cookie_offset) |
1033 | return; |
1034 | write_bytes_to_xdr_buf(&resp->dirlist, resp->cookie_offset, &cookie, |
1035 | sizeof(cookie)); |
1036 | resp->cookie_offset = 0; |
1037 | } |
1038 | |
1039 | static bool |
1040 | svcxdr_encode_entry3_common(struct nfsd3_readdirres *resp, const char *name, |
1041 | int namlen, loff_t offset, u64 ino) |
1042 | { |
1043 | struct xdr_buf *dirlist = &resp->dirlist; |
1044 | struct xdr_stream *xdr = &resp->xdr; |
1045 | |
1046 | if (xdr_stream_encode_item_present(xdr) < 0) |
1047 | return false; |
1048 | /* fileid */ |
1049 | if (xdr_stream_encode_u64(xdr, n: ino) < 0) |
1050 | return false; |
1051 | /* name */ |
1052 | if (xdr_stream_encode_opaque(xdr, ptr: name, min(namlen, NFS3_MAXNAMLEN)) < 0) |
1053 | return false; |
1054 | /* cookie */ |
1055 | resp->cookie_offset = dirlist->len; |
1056 | if (xdr_stream_encode_u64(xdr, OFFSET_MAX) < 0) |
1057 | return false; |
1058 | |
1059 | return true; |
1060 | } |
1061 | |
1062 | /** |
1063 | * nfs3svc_encode_entry3 - encode one NFSv3 READDIR entry |
1064 | * @data: directory context |
1065 | * @name: name of the object to be encoded |
1066 | * @namlen: length of that name, in bytes |
1067 | * @offset: the offset of the previous entry |
1068 | * @ino: the fileid of this entry |
1069 | * @d_type: unused |
1070 | * |
1071 | * Return values: |
1072 | * %0: Entry was successfully encoded. |
1073 | * %-EINVAL: An encoding problem occured, secondary status code in resp->common.err |
1074 | * |
1075 | * On exit, the following fields are updated: |
1076 | * - resp->xdr |
1077 | * - resp->common.err |
1078 | * - resp->cookie_offset |
1079 | */ |
1080 | int nfs3svc_encode_entry3(void *data, const char *name, int namlen, |
1081 | loff_t offset, u64 ino, unsigned int d_type) |
1082 | { |
1083 | struct readdir_cd *ccd = data; |
1084 | struct nfsd3_readdirres *resp = container_of(ccd, |
1085 | struct nfsd3_readdirres, |
1086 | common); |
1087 | unsigned int starting_length = resp->dirlist.len; |
1088 | |
1089 | /* The offset cookie for the previous entry */ |
1090 | nfs3svc_encode_cookie3(resp, offset); |
1091 | |
1092 | if (!svcxdr_encode_entry3_common(resp, name, namlen, offset, ino)) |
1093 | goto out_toosmall; |
1094 | |
1095 | xdr_commit_encode(xdr: &resp->xdr); |
1096 | resp->common.err = nfs_ok; |
1097 | return 0; |
1098 | |
1099 | out_toosmall: |
1100 | resp->cookie_offset = 0; |
1101 | resp->common.err = nfserr_toosmall; |
1102 | resp->dirlist.len = starting_length; |
1103 | return -EINVAL; |
1104 | } |
1105 | |
1106 | static bool |
1107 | svcxdr_encode_entry3_plus(struct nfsd3_readdirres *resp, const char *name, |
1108 | int namlen, u64 ino) |
1109 | { |
1110 | struct xdr_stream *xdr = &resp->xdr; |
1111 | struct svc_fh *fhp = &resp->scratch; |
1112 | bool result; |
1113 | |
1114 | result = false; |
1115 | fh_init(fhp, NFS3_FHSIZE); |
1116 | if (compose_entry_fh(cd: resp, fhp, name, namlen, ino) != nfs_ok) |
1117 | goto out_noattrs; |
1118 | |
1119 | if (!svcxdr_encode_post_op_attr(rqstp: resp->rqstp, xdr, fhp)) |
1120 | goto out; |
1121 | if (!svcxdr_encode_post_op_fh3(xdr, fhp)) |
1122 | goto out; |
1123 | result = true; |
1124 | |
1125 | out: |
1126 | fh_put(fhp); |
1127 | return result; |
1128 | |
1129 | out_noattrs: |
1130 | if (xdr_stream_encode_item_absent(xdr) < 0) |
1131 | return false; |
1132 | if (xdr_stream_encode_item_absent(xdr) < 0) |
1133 | return false; |
1134 | return true; |
1135 | } |
1136 | |
1137 | /** |
1138 | * nfs3svc_encode_entryplus3 - encode one NFSv3 READDIRPLUS entry |
1139 | * @data: directory context |
1140 | * @name: name of the object to be encoded |
1141 | * @namlen: length of that name, in bytes |
1142 | * @offset: the offset of the previous entry |
1143 | * @ino: the fileid of this entry |
1144 | * @d_type: unused |
1145 | * |
1146 | * Return values: |
1147 | * %0: Entry was successfully encoded. |
1148 | * %-EINVAL: An encoding problem occured, secondary status code in resp->common.err |
1149 | * |
1150 | * On exit, the following fields are updated: |
1151 | * - resp->xdr |
1152 | * - resp->common.err |
1153 | * - resp->cookie_offset |
1154 | */ |
1155 | int nfs3svc_encode_entryplus3(void *data, const char *name, int namlen, |
1156 | loff_t offset, u64 ino, unsigned int d_type) |
1157 | { |
1158 | struct readdir_cd *ccd = data; |
1159 | struct nfsd3_readdirres *resp = container_of(ccd, |
1160 | struct nfsd3_readdirres, |
1161 | common); |
1162 | unsigned int starting_length = resp->dirlist.len; |
1163 | |
1164 | /* The offset cookie for the previous entry */ |
1165 | nfs3svc_encode_cookie3(resp, offset); |
1166 | |
1167 | if (!svcxdr_encode_entry3_common(resp, name, namlen, offset, ino)) |
1168 | goto out_toosmall; |
1169 | if (!svcxdr_encode_entry3_plus(resp, name, namlen, ino)) |
1170 | goto out_toosmall; |
1171 | |
1172 | xdr_commit_encode(xdr: &resp->xdr); |
1173 | resp->common.err = nfs_ok; |
1174 | return 0; |
1175 | |
1176 | out_toosmall: |
1177 | resp->cookie_offset = 0; |
1178 | resp->common.err = nfserr_toosmall; |
1179 | resp->dirlist.len = starting_length; |
1180 | return -EINVAL; |
1181 | } |
1182 | |
1183 | static bool |
1184 | svcxdr_encode_fsstat3resok(struct xdr_stream *xdr, |
1185 | const struct nfsd3_fsstatres *resp) |
1186 | { |
1187 | const struct kstatfs *s = &resp->stats; |
1188 | u64 bs = s->f_bsize; |
1189 | __be32 *p; |
1190 | |
1191 | p = xdr_reserve_space(xdr, XDR_UNIT * 13); |
1192 | if (!p) |
1193 | return false; |
1194 | p = xdr_encode_hyper(p, val: bs * s->f_blocks); /* total bytes */ |
1195 | p = xdr_encode_hyper(p, val: bs * s->f_bfree); /* free bytes */ |
1196 | p = xdr_encode_hyper(p, val: bs * s->f_bavail); /* user available bytes */ |
1197 | p = xdr_encode_hyper(p, val: s->f_files); /* total inodes */ |
1198 | p = xdr_encode_hyper(p, val: s->f_ffree); /* free inodes */ |
1199 | p = xdr_encode_hyper(p, val: s->f_ffree); /* user available inodes */ |
1200 | *p = cpu_to_be32(resp->invarsec); /* mean unchanged time */ |
1201 | |
1202 | return true; |
1203 | } |
1204 | |
1205 | /* FSSTAT */ |
1206 | bool |
1207 | nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
1208 | { |
1209 | struct nfsd3_fsstatres *resp = rqstp->rq_resp; |
1210 | |
1211 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
1212 | return false; |
1213 | switch (resp->status) { |
1214 | case nfs_ok: |
1215 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &nfs3svc_null_fh)) |
1216 | return false; |
1217 | if (!svcxdr_encode_fsstat3resok(xdr, resp)) |
1218 | return false; |
1219 | break; |
1220 | default: |
1221 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &nfs3svc_null_fh)) |
1222 | return false; |
1223 | } |
1224 | |
1225 | return true; |
1226 | } |
1227 | |
1228 | static bool |
1229 | svcxdr_encode_fsinfo3resok(struct xdr_stream *xdr, |
1230 | const struct nfsd3_fsinfores *resp) |
1231 | { |
1232 | __be32 *p; |
1233 | |
1234 | p = xdr_reserve_space(xdr, XDR_UNIT * 12); |
1235 | if (!p) |
1236 | return false; |
1237 | *p++ = cpu_to_be32(resp->f_rtmax); |
1238 | *p++ = cpu_to_be32(resp->f_rtpref); |
1239 | *p++ = cpu_to_be32(resp->f_rtmult); |
1240 | *p++ = cpu_to_be32(resp->f_wtmax); |
1241 | *p++ = cpu_to_be32(resp->f_wtpref); |
1242 | *p++ = cpu_to_be32(resp->f_wtmult); |
1243 | *p++ = cpu_to_be32(resp->f_dtpref); |
1244 | p = xdr_encode_hyper(p, val: resp->f_maxfilesize); |
1245 | p = encode_nfstime3(p, time: &nfs3svc_time_delta); |
1246 | *p = cpu_to_be32(resp->f_properties); |
1247 | |
1248 | return true; |
1249 | } |
1250 | |
1251 | /* FSINFO */ |
1252 | bool |
1253 | nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
1254 | { |
1255 | struct nfsd3_fsinfores *resp = rqstp->rq_resp; |
1256 | |
1257 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
1258 | return false; |
1259 | switch (resp->status) { |
1260 | case nfs_ok: |
1261 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &nfs3svc_null_fh)) |
1262 | return false; |
1263 | if (!svcxdr_encode_fsinfo3resok(xdr, resp)) |
1264 | return false; |
1265 | break; |
1266 | default: |
1267 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &nfs3svc_null_fh)) |
1268 | return false; |
1269 | } |
1270 | |
1271 | return true; |
1272 | } |
1273 | |
1274 | static bool |
1275 | svcxdr_encode_pathconf3resok(struct xdr_stream *xdr, |
1276 | const struct nfsd3_pathconfres *resp) |
1277 | { |
1278 | __be32 *p; |
1279 | |
1280 | p = xdr_reserve_space(xdr, XDR_UNIT * 6); |
1281 | if (!p) |
1282 | return false; |
1283 | *p++ = cpu_to_be32(resp->p_link_max); |
1284 | *p++ = cpu_to_be32(resp->p_name_max); |
1285 | p = xdr_encode_bool(p, n: resp->p_no_trunc); |
1286 | p = xdr_encode_bool(p, n: resp->p_chown_restricted); |
1287 | p = xdr_encode_bool(p, n: resp->p_case_insensitive); |
1288 | xdr_encode_bool(p, n: resp->p_case_preserving); |
1289 | |
1290 | return true; |
1291 | } |
1292 | |
1293 | /* PATHCONF */ |
1294 | bool |
1295 | nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
1296 | { |
1297 | struct nfsd3_pathconfres *resp = rqstp->rq_resp; |
1298 | |
1299 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
1300 | return false; |
1301 | switch (resp->status) { |
1302 | case nfs_ok: |
1303 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &nfs3svc_null_fh)) |
1304 | return false; |
1305 | if (!svcxdr_encode_pathconf3resok(xdr, resp)) |
1306 | return false; |
1307 | break; |
1308 | default: |
1309 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &nfs3svc_null_fh)) |
1310 | return false; |
1311 | } |
1312 | |
1313 | return true; |
1314 | } |
1315 | |
1316 | /* COMMIT */ |
1317 | bool |
1318 | nfs3svc_encode_commitres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
1319 | { |
1320 | struct nfsd3_commitres *resp = rqstp->rq_resp; |
1321 | |
1322 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
1323 | return false; |
1324 | switch (resp->status) { |
1325 | case nfs_ok: |
1326 | if (!svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->fh)) |
1327 | return false; |
1328 | if (!svcxdr_encode_writeverf3(xdr, verf: resp->verf)) |
1329 | return false; |
1330 | break; |
1331 | default: |
1332 | if (!svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->fh)) |
1333 | return false; |
1334 | } |
1335 | |
1336 | return true; |
1337 | } |
1338 | |
1339 | /* |
1340 | * XDR release functions |
1341 | */ |
1342 | void |
1343 | nfs3svc_release_fhandle(struct svc_rqst *rqstp) |
1344 | { |
1345 | struct nfsd3_attrstat *resp = rqstp->rq_resp; |
1346 | |
1347 | fh_put(&resp->fh); |
1348 | } |
1349 | |
1350 | void |
1351 | nfs3svc_release_fhandle2(struct svc_rqst *rqstp) |
1352 | { |
1353 | struct nfsd3_fhandle_pair *resp = rqstp->rq_resp; |
1354 | |
1355 | fh_put(&resp->fh1); |
1356 | fh_put(&resp->fh2); |
1357 | } |
1358 | |