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 | u32 check; |
299 | |
300 | if (xdr_stream_decode_bool(xdr, ptr: &check) < 0) |
301 | return false; |
302 | if (check) { |
303 | if (!svcxdr_decode_nfstime3(xdr, timep: &args->guardtime)) |
304 | return false; |
305 | args->check_guard = 1; |
306 | } else |
307 | args->check_guard = 0; |
308 | |
309 | return true; |
310 | } |
311 | |
312 | static bool |
313 | svcxdr_decode_specdata3(struct xdr_stream *xdr, struct nfsd3_mknodargs *args) |
314 | { |
315 | __be32 *p; |
316 | |
317 | p = xdr_inline_decode(xdr, XDR_UNIT * 2); |
318 | if (!p) |
319 | return false; |
320 | args->major = be32_to_cpup(p: p++); |
321 | args->minor = be32_to_cpup(p); |
322 | |
323 | return true; |
324 | } |
325 | |
326 | static bool |
327 | svcxdr_decode_devicedata3(struct svc_rqst *rqstp, struct xdr_stream *xdr, |
328 | struct nfsd3_mknodargs *args) |
329 | { |
330 | return svcxdr_decode_sattr3(rqstp, xdr, iap: &args->attrs) && |
331 | svcxdr_decode_specdata3(xdr, args); |
332 | } |
333 | |
334 | static bool |
335 | svcxdr_encode_fattr3(struct svc_rqst *rqstp, struct xdr_stream *xdr, |
336 | const struct svc_fh *fhp, const struct kstat *stat) |
337 | { |
338 | struct user_namespace *userns = nfsd_user_namespace(rqstp); |
339 | __be32 *p; |
340 | u64 fsid; |
341 | |
342 | p = xdr_reserve_space(xdr, XDR_UNIT * 21); |
343 | if (!p) |
344 | return false; |
345 | |
346 | *p++ = cpu_to_be32(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]); |
347 | *p++ = cpu_to_be32((u32)(stat->mode & S_IALLUGO)); |
348 | *p++ = cpu_to_be32((u32)stat->nlink); |
349 | *p++ = cpu_to_be32((u32)from_kuid_munged(userns, stat->uid)); |
350 | *p++ = cpu_to_be32((u32)from_kgid_munged(userns, stat->gid)); |
351 | if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) |
352 | p = xdr_encode_hyper(p, val: (u64)NFS3_MAXPATHLEN); |
353 | else |
354 | p = xdr_encode_hyper(p, val: (u64)stat->size); |
355 | |
356 | /* used */ |
357 | p = xdr_encode_hyper(p, val: ((u64)stat->blocks) << 9); |
358 | |
359 | /* rdev */ |
360 | *p++ = cpu_to_be32((u32)MAJOR(stat->rdev)); |
361 | *p++ = cpu_to_be32((u32)MINOR(stat->rdev)); |
362 | |
363 | switch(fsid_source(fhp)) { |
364 | case FSIDSOURCE_FSID: |
365 | fsid = (u64)fhp->fh_export->ex_fsid; |
366 | break; |
367 | case FSIDSOURCE_UUID: |
368 | fsid = ((u64 *)fhp->fh_export->ex_uuid)[0]; |
369 | fsid ^= ((u64 *)fhp->fh_export->ex_uuid)[1]; |
370 | break; |
371 | default: |
372 | fsid = (u64)huge_encode_dev(dev: fhp->fh_dentry->d_sb->s_dev); |
373 | } |
374 | p = xdr_encode_hyper(p, val: fsid); |
375 | |
376 | /* fileid */ |
377 | p = xdr_encode_hyper(p, val: stat->ino); |
378 | |
379 | p = encode_nfstime3(p, time: &stat->atime); |
380 | p = encode_nfstime3(p, time: &stat->mtime); |
381 | encode_nfstime3(p, time: &stat->ctime); |
382 | |
383 | return true; |
384 | } |
385 | |
386 | static bool |
387 | svcxdr_encode_wcc_attr(struct xdr_stream *xdr, const struct svc_fh *fhp) |
388 | { |
389 | __be32 *p; |
390 | |
391 | p = xdr_reserve_space(xdr, XDR_UNIT * 6); |
392 | if (!p) |
393 | return false; |
394 | p = xdr_encode_hyper(p, val: (u64)fhp->fh_pre_size); |
395 | p = encode_nfstime3(p, time: &fhp->fh_pre_mtime); |
396 | encode_nfstime3(p, time: &fhp->fh_pre_ctime); |
397 | |
398 | return true; |
399 | } |
400 | |
401 | static bool |
402 | svcxdr_encode_pre_op_attr(struct xdr_stream *xdr, const struct svc_fh *fhp) |
403 | { |
404 | if (!fhp->fh_pre_saved) { |
405 | if (xdr_stream_encode_item_absent(xdr) < 0) |
406 | return false; |
407 | return true; |
408 | } |
409 | |
410 | if (xdr_stream_encode_item_present(xdr) < 0) |
411 | return false; |
412 | return svcxdr_encode_wcc_attr(xdr, fhp); |
413 | } |
414 | |
415 | /** |
416 | * svcxdr_encode_post_op_attr - Encode NFSv3 post-op attributes |
417 | * @rqstp: Context of a completed RPC transaction |
418 | * @xdr: XDR stream |
419 | * @fhp: File handle to encode |
420 | * |
421 | * Return values: |
422 | * %false: Send buffer space was exhausted |
423 | * %true: Success |
424 | */ |
425 | bool |
426 | svcxdr_encode_post_op_attr(struct svc_rqst *rqstp, struct xdr_stream *xdr, |
427 | const struct svc_fh *fhp) |
428 | { |
429 | struct dentry *dentry = fhp->fh_dentry; |
430 | struct kstat stat; |
431 | |
432 | /* |
433 | * The inode may be NULL if the call failed because of a |
434 | * stale file handle. In this case, no attributes are |
435 | * returned. |
436 | */ |
437 | if (fhp->fh_no_wcc || !dentry || !d_really_is_positive(dentry)) |
438 | goto no_post_op_attrs; |
439 | if (fh_getattr(fh: fhp, stat: &stat) != nfs_ok) |
440 | goto no_post_op_attrs; |
441 | |
442 | if (xdr_stream_encode_item_present(xdr) < 0) |
443 | return false; |
444 | lease_get_mtime(d_inode(dentry), time: &stat.mtime); |
445 | if (!svcxdr_encode_fattr3(rqstp, xdr, fhp, stat: &stat)) |
446 | return false; |
447 | |
448 | return true; |
449 | |
450 | no_post_op_attrs: |
451 | return xdr_stream_encode_item_absent(xdr) > 0; |
452 | } |
453 | |
454 | /* |
455 | * Encode weak cache consistency data |
456 | */ |
457 | static bool |
458 | svcxdr_encode_wcc_data(struct svc_rqst *rqstp, struct xdr_stream *xdr, |
459 | const struct svc_fh *fhp) |
460 | { |
461 | struct dentry *dentry = fhp->fh_dentry; |
462 | |
463 | if (!dentry || !d_really_is_positive(dentry) || !fhp->fh_post_saved) |
464 | goto neither; |
465 | |
466 | /* before */ |
467 | if (!svcxdr_encode_pre_op_attr(xdr, fhp)) |
468 | return false; |
469 | |
470 | /* after */ |
471 | if (xdr_stream_encode_item_present(xdr) < 0) |
472 | return false; |
473 | if (!svcxdr_encode_fattr3(rqstp, xdr, fhp, stat: &fhp->fh_post_attr)) |
474 | return false; |
475 | |
476 | return true; |
477 | |
478 | neither: |
479 | if (xdr_stream_encode_item_absent(xdr) < 0) |
480 | return false; |
481 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp)) |
482 | return false; |
483 | |
484 | return true; |
485 | } |
486 | |
487 | /* |
488 | * XDR decode functions |
489 | */ |
490 | |
491 | bool |
492 | nfs3svc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
493 | { |
494 | struct nfsd_fhandle *args = rqstp->rq_argp; |
495 | |
496 | return svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh); |
497 | } |
498 | |
499 | bool |
500 | nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
501 | { |
502 | struct nfsd3_sattrargs *args = rqstp->rq_argp; |
503 | |
504 | return svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh) && |
505 | svcxdr_decode_sattr3(rqstp, xdr, iap: &args->attrs) && |
506 | svcxdr_decode_sattrguard3(xdr, args); |
507 | } |
508 | |
509 | bool |
510 | nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
511 | { |
512 | struct nfsd3_diropargs *args = rqstp->rq_argp; |
513 | |
514 | return svcxdr_decode_diropargs3(xdr, fhp: &args->fh, name: &args->name, len: &args->len); |
515 | } |
516 | |
517 | bool |
518 | nfs3svc_decode_accessargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
519 | { |
520 | struct nfsd3_accessargs *args = rqstp->rq_argp; |
521 | |
522 | if (!svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh)) |
523 | return false; |
524 | if (xdr_stream_decode_u32(xdr, ptr: &args->access) < 0) |
525 | return false; |
526 | |
527 | return true; |
528 | } |
529 | |
530 | bool |
531 | nfs3svc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
532 | { |
533 | struct nfsd3_readargs *args = rqstp->rq_argp; |
534 | |
535 | if (!svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh)) |
536 | return false; |
537 | if (xdr_stream_decode_u64(xdr, ptr: &args->offset) < 0) |
538 | return false; |
539 | if (xdr_stream_decode_u32(xdr, ptr: &args->count) < 0) |
540 | return false; |
541 | |
542 | return true; |
543 | } |
544 | |
545 | bool |
546 | nfs3svc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
547 | { |
548 | struct nfsd3_writeargs *args = rqstp->rq_argp; |
549 | u32 max_blocksize = svc_max_payload(rqstp); |
550 | |
551 | if (!svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh)) |
552 | return false; |
553 | if (xdr_stream_decode_u64(xdr, ptr: &args->offset) < 0) |
554 | return false; |
555 | if (xdr_stream_decode_u32(xdr, ptr: &args->count) < 0) |
556 | return false; |
557 | if (xdr_stream_decode_u32(xdr, ptr: &args->stable) < 0) |
558 | return false; |
559 | |
560 | /* opaque data */ |
561 | if (xdr_stream_decode_u32(xdr, ptr: &args->len) < 0) |
562 | return false; |
563 | |
564 | /* request sanity */ |
565 | if (args->count != args->len) |
566 | return false; |
567 | if (args->count > max_blocksize) { |
568 | args->count = max_blocksize; |
569 | args->len = max_blocksize; |
570 | } |
571 | |
572 | return xdr_stream_subsegment(xdr, subbuf: &args->payload, len: args->count); |
573 | } |
574 | |
575 | bool |
576 | nfs3svc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
577 | { |
578 | struct nfsd3_createargs *args = rqstp->rq_argp; |
579 | |
580 | if (!svcxdr_decode_diropargs3(xdr, fhp: &args->fh, name: &args->name, len: &args->len)) |
581 | return false; |
582 | if (xdr_stream_decode_u32(xdr, ptr: &args->createmode) < 0) |
583 | return false; |
584 | switch (args->createmode) { |
585 | case NFS3_CREATE_UNCHECKED: |
586 | case NFS3_CREATE_GUARDED: |
587 | return svcxdr_decode_sattr3(rqstp, xdr, iap: &args->attrs); |
588 | case NFS3_CREATE_EXCLUSIVE: |
589 | args->verf = xdr_inline_decode(xdr, NFS3_CREATEVERFSIZE); |
590 | if (!args->verf) |
591 | return false; |
592 | break; |
593 | default: |
594 | return false; |
595 | } |
596 | return true; |
597 | } |
598 | |
599 | bool |
600 | nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
601 | { |
602 | struct nfsd3_createargs *args = rqstp->rq_argp; |
603 | |
604 | return svcxdr_decode_diropargs3(xdr, fhp: &args->fh, |
605 | name: &args->name, len: &args->len) && |
606 | svcxdr_decode_sattr3(rqstp, xdr, iap: &args->attrs); |
607 | } |
608 | |
609 | bool |
610 | nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
611 | { |
612 | struct nfsd3_symlinkargs *args = rqstp->rq_argp; |
613 | struct kvec *head = rqstp->rq_arg.head; |
614 | |
615 | if (!svcxdr_decode_diropargs3(xdr, fhp: &args->ffh, name: &args->fname, len: &args->flen)) |
616 | return false; |
617 | if (!svcxdr_decode_sattr3(rqstp, xdr, iap: &args->attrs)) |
618 | return false; |
619 | if (xdr_stream_decode_u32(xdr, ptr: &args->tlen) < 0) |
620 | return false; |
621 | |
622 | /* symlink_data */ |
623 | args->first.iov_len = head->iov_len - xdr_stream_pos(xdr); |
624 | args->first.iov_base = xdr_inline_decode(xdr, nbytes: args->tlen); |
625 | return args->first.iov_base != NULL; |
626 | } |
627 | |
628 | bool |
629 | nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
630 | { |
631 | struct nfsd3_mknodargs *args = rqstp->rq_argp; |
632 | |
633 | if (!svcxdr_decode_diropargs3(xdr, fhp: &args->fh, name: &args->name, len: &args->len)) |
634 | return false; |
635 | if (xdr_stream_decode_u32(xdr, ptr: &args->ftype) < 0) |
636 | return false; |
637 | switch (args->ftype) { |
638 | case NF3CHR: |
639 | case NF3BLK: |
640 | return svcxdr_decode_devicedata3(rqstp, xdr, args); |
641 | case NF3SOCK: |
642 | case NF3FIFO: |
643 | return svcxdr_decode_sattr3(rqstp, xdr, iap: &args->attrs); |
644 | case NF3REG: |
645 | case NF3DIR: |
646 | case NF3LNK: |
647 | /* Valid XDR but illegal file types */ |
648 | break; |
649 | default: |
650 | return false; |
651 | } |
652 | |
653 | return true; |
654 | } |
655 | |
656 | bool |
657 | nfs3svc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
658 | { |
659 | struct nfsd3_renameargs *args = rqstp->rq_argp; |
660 | |
661 | return svcxdr_decode_diropargs3(xdr, fhp: &args->ffh, |
662 | name: &args->fname, len: &args->flen) && |
663 | svcxdr_decode_diropargs3(xdr, fhp: &args->tfh, |
664 | name: &args->tname, len: &args->tlen); |
665 | } |
666 | |
667 | bool |
668 | nfs3svc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
669 | { |
670 | struct nfsd3_linkargs *args = rqstp->rq_argp; |
671 | |
672 | return svcxdr_decode_nfs_fh3(xdr, fhp: &args->ffh) && |
673 | svcxdr_decode_diropargs3(xdr, fhp: &args->tfh, |
674 | name: &args->tname, len: &args->tlen); |
675 | } |
676 | |
677 | bool |
678 | nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
679 | { |
680 | struct nfsd3_readdirargs *args = rqstp->rq_argp; |
681 | |
682 | if (!svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh)) |
683 | return false; |
684 | if (xdr_stream_decode_u64(xdr, ptr: &args->cookie) < 0) |
685 | return false; |
686 | args->verf = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE); |
687 | if (!args->verf) |
688 | return false; |
689 | if (xdr_stream_decode_u32(xdr, ptr: &args->count) < 0) |
690 | return false; |
691 | |
692 | return true; |
693 | } |
694 | |
695 | bool |
696 | nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
697 | { |
698 | struct nfsd3_readdirargs *args = rqstp->rq_argp; |
699 | u32 dircount; |
700 | |
701 | if (!svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh)) |
702 | return false; |
703 | if (xdr_stream_decode_u64(xdr, ptr: &args->cookie) < 0) |
704 | return false; |
705 | args->verf = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE); |
706 | if (!args->verf) |
707 | return false; |
708 | /* dircount is ignored */ |
709 | if (xdr_stream_decode_u32(xdr, ptr: &dircount) < 0) |
710 | return false; |
711 | if (xdr_stream_decode_u32(xdr, ptr: &args->count) < 0) |
712 | return false; |
713 | |
714 | return true; |
715 | } |
716 | |
717 | bool |
718 | nfs3svc_decode_commitargs(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
719 | { |
720 | struct nfsd3_commitargs *args = rqstp->rq_argp; |
721 | |
722 | if (!svcxdr_decode_nfs_fh3(xdr, fhp: &args->fh)) |
723 | return false; |
724 | if (xdr_stream_decode_u64(xdr, ptr: &args->offset) < 0) |
725 | return false; |
726 | if (xdr_stream_decode_u32(xdr, ptr: &args->count) < 0) |
727 | return false; |
728 | |
729 | return true; |
730 | } |
731 | |
732 | /* |
733 | * XDR encode functions |
734 | */ |
735 | |
736 | /* GETATTR */ |
737 | bool |
738 | nfs3svc_encode_getattrres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
739 | { |
740 | struct nfsd3_attrstat *resp = rqstp->rq_resp; |
741 | |
742 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
743 | return false; |
744 | switch (resp->status) { |
745 | case nfs_ok: |
746 | lease_get_mtime(d_inode(dentry: resp->fh.fh_dentry), time: &resp->stat.mtime); |
747 | if (!svcxdr_encode_fattr3(rqstp, xdr, fhp: &resp->fh, stat: &resp->stat)) |
748 | return false; |
749 | break; |
750 | } |
751 | |
752 | return true; |
753 | } |
754 | |
755 | /* SETATTR, REMOVE, RMDIR */ |
756 | bool |
757 | nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
758 | { |
759 | struct nfsd3_attrstat *resp = rqstp->rq_resp; |
760 | |
761 | return svcxdr_encode_nfsstat3(xdr, status: resp->status) && |
762 | svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->fh); |
763 | } |
764 | |
765 | /* LOOKUP */ |
766 | bool |
767 | nfs3svc_encode_lookupres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
768 | { |
769 | struct nfsd3_diropres *resp = rqstp->rq_resp; |
770 | |
771 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
772 | return false; |
773 | switch (resp->status) { |
774 | case nfs_ok: |
775 | if (!svcxdr_encode_nfs_fh3(xdr, fhp: &resp->fh)) |
776 | return false; |
777 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
778 | return false; |
779 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->dirfh)) |
780 | return false; |
781 | break; |
782 | default: |
783 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->dirfh)) |
784 | return false; |
785 | } |
786 | |
787 | return true; |
788 | } |
789 | |
790 | /* ACCESS */ |
791 | bool |
792 | nfs3svc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
793 | { |
794 | struct nfsd3_accessres *resp = rqstp->rq_resp; |
795 | |
796 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
797 | return false; |
798 | switch (resp->status) { |
799 | case nfs_ok: |
800 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
801 | return false; |
802 | if (xdr_stream_encode_u32(xdr, n: resp->access) < 0) |
803 | return false; |
804 | break; |
805 | default: |
806 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
807 | return false; |
808 | } |
809 | |
810 | return true; |
811 | } |
812 | |
813 | /* READLINK */ |
814 | bool |
815 | nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
816 | { |
817 | struct nfsd3_readlinkres *resp = rqstp->rq_resp; |
818 | struct kvec *head = rqstp->rq_res.head; |
819 | |
820 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
821 | return false; |
822 | switch (resp->status) { |
823 | case nfs_ok: |
824 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
825 | return false; |
826 | if (xdr_stream_encode_u32(xdr, n: resp->len) < 0) |
827 | return false; |
828 | svcxdr_encode_opaque_pages(rqstp, xdr, pages: resp->pages, base: 0, |
829 | len: resp->len); |
830 | if (svc_encode_result_payload(rqstp, offset: head->iov_len, length: resp->len) < 0) |
831 | return false; |
832 | break; |
833 | default: |
834 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
835 | return false; |
836 | } |
837 | |
838 | return true; |
839 | } |
840 | |
841 | /* READ */ |
842 | bool |
843 | nfs3svc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
844 | { |
845 | struct nfsd3_readres *resp = rqstp->rq_resp; |
846 | struct kvec *head = rqstp->rq_res.head; |
847 | |
848 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
849 | return false; |
850 | switch (resp->status) { |
851 | case nfs_ok: |
852 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
853 | return false; |
854 | if (xdr_stream_encode_u32(xdr, n: resp->count) < 0) |
855 | return false; |
856 | if (xdr_stream_encode_bool(xdr, n: resp->eof) < 0) |
857 | return false; |
858 | if (xdr_stream_encode_u32(xdr, n: resp->count) < 0) |
859 | return false; |
860 | svcxdr_encode_opaque_pages(rqstp, xdr, pages: resp->pages, |
861 | base: rqstp->rq_res.page_base, |
862 | len: resp->count); |
863 | if (svc_encode_result_payload(rqstp, offset: head->iov_len, length: resp->count) < 0) |
864 | return false; |
865 | break; |
866 | default: |
867 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
868 | return false; |
869 | } |
870 | |
871 | return true; |
872 | } |
873 | |
874 | /* WRITE */ |
875 | bool |
876 | nfs3svc_encode_writeres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
877 | { |
878 | struct nfsd3_writeres *resp = rqstp->rq_resp; |
879 | |
880 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
881 | return false; |
882 | switch (resp->status) { |
883 | case nfs_ok: |
884 | if (!svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->fh)) |
885 | return false; |
886 | if (xdr_stream_encode_u32(xdr, n: resp->count) < 0) |
887 | return false; |
888 | if (xdr_stream_encode_u32(xdr, n: resp->committed) < 0) |
889 | return false; |
890 | if (!svcxdr_encode_writeverf3(xdr, verf: resp->verf)) |
891 | return false; |
892 | break; |
893 | default: |
894 | if (!svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->fh)) |
895 | return false; |
896 | } |
897 | |
898 | return true; |
899 | } |
900 | |
901 | /* CREATE, MKDIR, SYMLINK, MKNOD */ |
902 | bool |
903 | nfs3svc_encode_createres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
904 | { |
905 | struct nfsd3_diropres *resp = rqstp->rq_resp; |
906 | |
907 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
908 | return false; |
909 | switch (resp->status) { |
910 | case nfs_ok: |
911 | if (!svcxdr_encode_post_op_fh3(xdr, fhp: &resp->fh)) |
912 | return false; |
913 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
914 | return false; |
915 | if (!svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->dirfh)) |
916 | return false; |
917 | break; |
918 | default: |
919 | if (!svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->dirfh)) |
920 | return false; |
921 | } |
922 | |
923 | return true; |
924 | } |
925 | |
926 | /* RENAME */ |
927 | bool |
928 | nfs3svc_encode_renameres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
929 | { |
930 | struct nfsd3_renameres *resp = rqstp->rq_resp; |
931 | |
932 | return svcxdr_encode_nfsstat3(xdr, status: resp->status) && |
933 | svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->ffh) && |
934 | svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->tfh); |
935 | } |
936 | |
937 | /* LINK */ |
938 | bool |
939 | nfs3svc_encode_linkres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
940 | { |
941 | struct nfsd3_linkres *resp = rqstp->rq_resp; |
942 | |
943 | return svcxdr_encode_nfsstat3(xdr, status: resp->status) && |
944 | svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh) && |
945 | svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->tfh); |
946 | } |
947 | |
948 | /* READDIR */ |
949 | bool |
950 | nfs3svc_encode_readdirres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
951 | { |
952 | struct nfsd3_readdirres *resp = rqstp->rq_resp; |
953 | struct xdr_buf *dirlist = &resp->dirlist; |
954 | |
955 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
956 | return false; |
957 | switch (resp->status) { |
958 | case nfs_ok: |
959 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
960 | return false; |
961 | if (!svcxdr_encode_cookieverf3(xdr, verf: resp->verf)) |
962 | return false; |
963 | svcxdr_encode_opaque_pages(rqstp, xdr, pages: dirlist->pages, base: 0, |
964 | len: dirlist->len); |
965 | /* no more entries */ |
966 | if (xdr_stream_encode_item_absent(xdr) < 0) |
967 | return false; |
968 | if (xdr_stream_encode_bool(xdr, n: resp->common.err == nfserr_eof) < 0) |
969 | return false; |
970 | break; |
971 | default: |
972 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &resp->fh)) |
973 | return false; |
974 | } |
975 | |
976 | return true; |
977 | } |
978 | |
979 | static __be32 |
980 | compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp, |
981 | const char *name, int namlen, u64 ino) |
982 | { |
983 | struct svc_export *exp; |
984 | struct dentry *dparent, *dchild; |
985 | __be32 rv = nfserr_noent; |
986 | |
987 | dparent = cd->fh.fh_dentry; |
988 | exp = cd->fh.fh_export; |
989 | |
990 | if (isdotent(name, namlen)) { |
991 | if (namlen == 2) { |
992 | dchild = dget_parent(dentry: dparent); |
993 | /* |
994 | * Don't return filehandle for ".." if we're at |
995 | * the filesystem or export root: |
996 | */ |
997 | if (dchild == dparent) |
998 | goto out; |
999 | if (dparent == exp->ex_path.dentry) |
1000 | goto out; |
1001 | } else |
1002 | dchild = dget(dentry: dparent); |
1003 | } else |
1004 | dchild = lookup_positive_unlocked(name, dparent, namlen); |
1005 | if (IS_ERR(ptr: dchild)) |
1006 | return rv; |
1007 | if (d_mountpoint(dentry: dchild)) |
1008 | goto out; |
1009 | if (dchild->d_inode->i_ino != ino) |
1010 | goto out; |
1011 | rv = fh_compose(fhp, exp, dchild, &cd->fh); |
1012 | out: |
1013 | dput(dchild); |
1014 | return rv; |
1015 | } |
1016 | |
1017 | /** |
1018 | * nfs3svc_encode_cookie3 - Encode a directory offset cookie |
1019 | * @resp: readdir result context |
1020 | * @offset: offset cookie to encode |
1021 | * |
1022 | * The buffer space for the offset cookie has already been reserved |
1023 | * by svcxdr_encode_entry3_common(). |
1024 | */ |
1025 | void nfs3svc_encode_cookie3(struct nfsd3_readdirres *resp, u64 offset) |
1026 | { |
1027 | __be64 cookie = cpu_to_be64(offset); |
1028 | |
1029 | if (!resp->cookie_offset) |
1030 | return; |
1031 | write_bytes_to_xdr_buf(&resp->dirlist, resp->cookie_offset, &cookie, |
1032 | sizeof(cookie)); |
1033 | resp->cookie_offset = 0; |
1034 | } |
1035 | |
1036 | static bool |
1037 | svcxdr_encode_entry3_common(struct nfsd3_readdirres *resp, const char *name, |
1038 | int namlen, loff_t offset, u64 ino) |
1039 | { |
1040 | struct xdr_buf *dirlist = &resp->dirlist; |
1041 | struct xdr_stream *xdr = &resp->xdr; |
1042 | |
1043 | if (xdr_stream_encode_item_present(xdr) < 0) |
1044 | return false; |
1045 | /* fileid */ |
1046 | if (xdr_stream_encode_u64(xdr, n: ino) < 0) |
1047 | return false; |
1048 | /* name */ |
1049 | if (xdr_stream_encode_opaque(xdr, ptr: name, min(namlen, NFS3_MAXNAMLEN)) < 0) |
1050 | return false; |
1051 | /* cookie */ |
1052 | resp->cookie_offset = dirlist->len; |
1053 | if (xdr_stream_encode_u64(xdr, OFFSET_MAX) < 0) |
1054 | return false; |
1055 | |
1056 | return true; |
1057 | } |
1058 | |
1059 | /** |
1060 | * nfs3svc_encode_entry3 - encode one NFSv3 READDIR entry |
1061 | * @data: directory context |
1062 | * @name: name of the object to be encoded |
1063 | * @namlen: length of that name, in bytes |
1064 | * @offset: the offset of the previous entry |
1065 | * @ino: the fileid of this entry |
1066 | * @d_type: unused |
1067 | * |
1068 | * Return values: |
1069 | * %0: Entry was successfully encoded. |
1070 | * %-EINVAL: An encoding problem occured, secondary status code in resp->common.err |
1071 | * |
1072 | * On exit, the following fields are updated: |
1073 | * - resp->xdr |
1074 | * - resp->common.err |
1075 | * - resp->cookie_offset |
1076 | */ |
1077 | int nfs3svc_encode_entry3(void *data, const char *name, int namlen, |
1078 | loff_t offset, u64 ino, unsigned int d_type) |
1079 | { |
1080 | struct readdir_cd *ccd = data; |
1081 | struct nfsd3_readdirres *resp = container_of(ccd, |
1082 | struct nfsd3_readdirres, |
1083 | common); |
1084 | unsigned int starting_length = resp->dirlist.len; |
1085 | |
1086 | /* The offset cookie for the previous entry */ |
1087 | nfs3svc_encode_cookie3(resp, offset); |
1088 | |
1089 | if (!svcxdr_encode_entry3_common(resp, name, namlen, offset, ino)) |
1090 | goto out_toosmall; |
1091 | |
1092 | xdr_commit_encode(xdr: &resp->xdr); |
1093 | resp->common.err = nfs_ok; |
1094 | return 0; |
1095 | |
1096 | out_toosmall: |
1097 | resp->cookie_offset = 0; |
1098 | resp->common.err = nfserr_toosmall; |
1099 | resp->dirlist.len = starting_length; |
1100 | return -EINVAL; |
1101 | } |
1102 | |
1103 | static bool |
1104 | svcxdr_encode_entry3_plus(struct nfsd3_readdirres *resp, const char *name, |
1105 | int namlen, u64 ino) |
1106 | { |
1107 | struct xdr_stream *xdr = &resp->xdr; |
1108 | struct svc_fh *fhp = &resp->scratch; |
1109 | bool result; |
1110 | |
1111 | result = false; |
1112 | fh_init(fhp, NFS3_FHSIZE); |
1113 | if (compose_entry_fh(cd: resp, fhp, name, namlen, ino) != nfs_ok) |
1114 | goto out_noattrs; |
1115 | |
1116 | if (!svcxdr_encode_post_op_attr(rqstp: resp->rqstp, xdr, fhp)) |
1117 | goto out; |
1118 | if (!svcxdr_encode_post_op_fh3(xdr, fhp)) |
1119 | goto out; |
1120 | result = true; |
1121 | |
1122 | out: |
1123 | fh_put(fhp); |
1124 | return result; |
1125 | |
1126 | out_noattrs: |
1127 | if (xdr_stream_encode_item_absent(xdr) < 0) |
1128 | return false; |
1129 | if (xdr_stream_encode_item_absent(xdr) < 0) |
1130 | return false; |
1131 | return true; |
1132 | } |
1133 | |
1134 | /** |
1135 | * nfs3svc_encode_entryplus3 - encode one NFSv3 READDIRPLUS entry |
1136 | * @data: directory context |
1137 | * @name: name of the object to be encoded |
1138 | * @namlen: length of that name, in bytes |
1139 | * @offset: the offset of the previous entry |
1140 | * @ino: the fileid of this entry |
1141 | * @d_type: unused |
1142 | * |
1143 | * Return values: |
1144 | * %0: Entry was successfully encoded. |
1145 | * %-EINVAL: An encoding problem occured, secondary status code in resp->common.err |
1146 | * |
1147 | * On exit, the following fields are updated: |
1148 | * - resp->xdr |
1149 | * - resp->common.err |
1150 | * - resp->cookie_offset |
1151 | */ |
1152 | int nfs3svc_encode_entryplus3(void *data, const char *name, int namlen, |
1153 | loff_t offset, u64 ino, unsigned int d_type) |
1154 | { |
1155 | struct readdir_cd *ccd = data; |
1156 | struct nfsd3_readdirres *resp = container_of(ccd, |
1157 | struct nfsd3_readdirres, |
1158 | common); |
1159 | unsigned int starting_length = resp->dirlist.len; |
1160 | |
1161 | /* The offset cookie for the previous entry */ |
1162 | nfs3svc_encode_cookie3(resp, offset); |
1163 | |
1164 | if (!svcxdr_encode_entry3_common(resp, name, namlen, offset, ino)) |
1165 | goto out_toosmall; |
1166 | if (!svcxdr_encode_entry3_plus(resp, name, namlen, ino)) |
1167 | goto out_toosmall; |
1168 | |
1169 | xdr_commit_encode(xdr: &resp->xdr); |
1170 | resp->common.err = nfs_ok; |
1171 | return 0; |
1172 | |
1173 | out_toosmall: |
1174 | resp->cookie_offset = 0; |
1175 | resp->common.err = nfserr_toosmall; |
1176 | resp->dirlist.len = starting_length; |
1177 | return -EINVAL; |
1178 | } |
1179 | |
1180 | static bool |
1181 | svcxdr_encode_fsstat3resok(struct xdr_stream *xdr, |
1182 | const struct nfsd3_fsstatres *resp) |
1183 | { |
1184 | const struct kstatfs *s = &resp->stats; |
1185 | u64 bs = s->f_bsize; |
1186 | __be32 *p; |
1187 | |
1188 | p = xdr_reserve_space(xdr, XDR_UNIT * 13); |
1189 | if (!p) |
1190 | return false; |
1191 | p = xdr_encode_hyper(p, val: bs * s->f_blocks); /* total bytes */ |
1192 | p = xdr_encode_hyper(p, val: bs * s->f_bfree); /* free bytes */ |
1193 | p = xdr_encode_hyper(p, val: bs * s->f_bavail); /* user available bytes */ |
1194 | p = xdr_encode_hyper(p, val: s->f_files); /* total inodes */ |
1195 | p = xdr_encode_hyper(p, val: s->f_ffree); /* free inodes */ |
1196 | p = xdr_encode_hyper(p, val: s->f_ffree); /* user available inodes */ |
1197 | *p = cpu_to_be32(resp->invarsec); /* mean unchanged time */ |
1198 | |
1199 | return true; |
1200 | } |
1201 | |
1202 | /* FSSTAT */ |
1203 | bool |
1204 | nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
1205 | { |
1206 | struct nfsd3_fsstatres *resp = rqstp->rq_resp; |
1207 | |
1208 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
1209 | return false; |
1210 | switch (resp->status) { |
1211 | case nfs_ok: |
1212 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &nfs3svc_null_fh)) |
1213 | return false; |
1214 | if (!svcxdr_encode_fsstat3resok(xdr, resp)) |
1215 | return false; |
1216 | break; |
1217 | default: |
1218 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &nfs3svc_null_fh)) |
1219 | return false; |
1220 | } |
1221 | |
1222 | return true; |
1223 | } |
1224 | |
1225 | static bool |
1226 | svcxdr_encode_fsinfo3resok(struct xdr_stream *xdr, |
1227 | const struct nfsd3_fsinfores *resp) |
1228 | { |
1229 | __be32 *p; |
1230 | |
1231 | p = xdr_reserve_space(xdr, XDR_UNIT * 12); |
1232 | if (!p) |
1233 | return false; |
1234 | *p++ = cpu_to_be32(resp->f_rtmax); |
1235 | *p++ = cpu_to_be32(resp->f_rtpref); |
1236 | *p++ = cpu_to_be32(resp->f_rtmult); |
1237 | *p++ = cpu_to_be32(resp->f_wtmax); |
1238 | *p++ = cpu_to_be32(resp->f_wtpref); |
1239 | *p++ = cpu_to_be32(resp->f_wtmult); |
1240 | *p++ = cpu_to_be32(resp->f_dtpref); |
1241 | p = xdr_encode_hyper(p, val: resp->f_maxfilesize); |
1242 | p = encode_nfstime3(p, time: &nfs3svc_time_delta); |
1243 | *p = cpu_to_be32(resp->f_properties); |
1244 | |
1245 | return true; |
1246 | } |
1247 | |
1248 | /* FSINFO */ |
1249 | bool |
1250 | nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
1251 | { |
1252 | struct nfsd3_fsinfores *resp = rqstp->rq_resp; |
1253 | |
1254 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
1255 | return false; |
1256 | switch (resp->status) { |
1257 | case nfs_ok: |
1258 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &nfs3svc_null_fh)) |
1259 | return false; |
1260 | if (!svcxdr_encode_fsinfo3resok(xdr, resp)) |
1261 | return false; |
1262 | break; |
1263 | default: |
1264 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &nfs3svc_null_fh)) |
1265 | return false; |
1266 | } |
1267 | |
1268 | return true; |
1269 | } |
1270 | |
1271 | static bool |
1272 | svcxdr_encode_pathconf3resok(struct xdr_stream *xdr, |
1273 | const struct nfsd3_pathconfres *resp) |
1274 | { |
1275 | __be32 *p; |
1276 | |
1277 | p = xdr_reserve_space(xdr, XDR_UNIT * 6); |
1278 | if (!p) |
1279 | return false; |
1280 | *p++ = cpu_to_be32(resp->p_link_max); |
1281 | *p++ = cpu_to_be32(resp->p_name_max); |
1282 | p = xdr_encode_bool(p, n: resp->p_no_trunc); |
1283 | p = xdr_encode_bool(p, n: resp->p_chown_restricted); |
1284 | p = xdr_encode_bool(p, n: resp->p_case_insensitive); |
1285 | xdr_encode_bool(p, n: resp->p_case_preserving); |
1286 | |
1287 | return true; |
1288 | } |
1289 | |
1290 | /* PATHCONF */ |
1291 | bool |
1292 | nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
1293 | { |
1294 | struct nfsd3_pathconfres *resp = rqstp->rq_resp; |
1295 | |
1296 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
1297 | return false; |
1298 | switch (resp->status) { |
1299 | case nfs_ok: |
1300 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &nfs3svc_null_fh)) |
1301 | return false; |
1302 | if (!svcxdr_encode_pathconf3resok(xdr, resp)) |
1303 | return false; |
1304 | break; |
1305 | default: |
1306 | if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp: &nfs3svc_null_fh)) |
1307 | return false; |
1308 | } |
1309 | |
1310 | return true; |
1311 | } |
1312 | |
1313 | /* COMMIT */ |
1314 | bool |
1315 | nfs3svc_encode_commitres(struct svc_rqst *rqstp, struct xdr_stream *xdr) |
1316 | { |
1317 | struct nfsd3_commitres *resp = rqstp->rq_resp; |
1318 | |
1319 | if (!svcxdr_encode_nfsstat3(xdr, status: resp->status)) |
1320 | return false; |
1321 | switch (resp->status) { |
1322 | case nfs_ok: |
1323 | if (!svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->fh)) |
1324 | return false; |
1325 | if (!svcxdr_encode_writeverf3(xdr, verf: resp->verf)) |
1326 | return false; |
1327 | break; |
1328 | default: |
1329 | if (!svcxdr_encode_wcc_data(rqstp, xdr, fhp: &resp->fh)) |
1330 | return false; |
1331 | } |
1332 | |
1333 | return true; |
1334 | } |
1335 | |
1336 | /* |
1337 | * XDR release functions |
1338 | */ |
1339 | void |
1340 | nfs3svc_release_fhandle(struct svc_rqst *rqstp) |
1341 | { |
1342 | struct nfsd3_attrstat *resp = rqstp->rq_resp; |
1343 | |
1344 | fh_put(&resp->fh); |
1345 | } |
1346 | |
1347 | void |
1348 | nfs3svc_release_fhandle2(struct svc_rqst *rqstp) |
1349 | { |
1350 | struct nfsd3_fhandle_pair *resp = rqstp->rq_resp; |
1351 | |
1352 | fh_put(&resp->fh1); |
1353 | fh_put(&resp->fh2); |
1354 | } |
1355 | |