Merge with Linux 2.5.48.
[linux-2.6/linux-mips.git] / fs / nfsd / nfs3xdr.c
blob27f8c188bf1b1f1e36bd2121635f0f162ccee18e
1 /*
2 * linux/fs/nfsd/nfs3xdr.c
4 * XDR support for nfsd/protocol version 3.
6 * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
7 */
9 #include <linux/types.h>
10 #include <linux/time.h>
11 #include <linux/nfs3.h>
12 #include <linux/list.h>
13 #include <linux/spinlock.h>
14 #include <linux/dcache.h>
15 #include <linux/namei.h>
16 #include <linux/mm.h>
18 #include <linux/sunrpc/xdr.h>
19 #include <linux/sunrpc/svc.h>
20 #include <linux/nfsd/nfsd.h>
21 #include <linux/nfsd/xdr3.h>
23 #define NFSDDBG_FACILITY NFSDDBG_XDR
25 #ifdef NFSD_OPTIMIZE_SPACE
26 # define inline
27 #endif
31 * Mapping of S_IF* types to NFS file types
33 static u32 nfs3_ftypes[] = {
34 NF3NON, NF3FIFO, NF3CHR, NF3BAD,
35 NF3DIR, NF3BAD, NF3BLK, NF3BAD,
36 NF3REG, NF3BAD, NF3LNK, NF3BAD,
37 NF3SOCK, NF3BAD, NF3LNK, NF3BAD,
41 * XDR functions for basic NFS types
43 static inline u32 *
44 encode_time3(u32 *p, struct timespec *time)
46 *p++ = htonl((u32) time->tv_sec); *p++ = htons(time->tv_nsec);
47 return p;
50 static inline u32 *
51 decode_time3(u32 *p, struct timespec *time)
53 time->tv_sec = ntohl(*p++);
54 time->tv_nsec = ntohl(*p++);
55 return p;
58 static inline u32 *
59 decode_fh(u32 *p, struct svc_fh *fhp)
61 int size;
62 fh_init(fhp, NFS3_FHSIZE);
63 size = ntohl(*p++);
64 if (size > NFS3_FHSIZE)
65 return NULL;
67 memcpy(&fhp->fh_handle.fh_base, p, size);
68 fhp->fh_handle.fh_size = size;
69 return p + XDR_QUADLEN(size);
72 static inline u32 *
73 encode_fh(u32 *p, struct svc_fh *fhp)
75 int size = fhp->fh_handle.fh_size;
76 *p++ = htonl(size);
77 if (size) p[XDR_QUADLEN(size)-1]=0;
78 memcpy(p, &fhp->fh_handle.fh_base, size);
79 return p + XDR_QUADLEN(size);
83 * Decode a file name and make sure that the path contains
84 * no slashes or null bytes.
86 static inline u32 *
87 decode_filename(u32 *p, char **namp, int *lenp)
89 char *name;
90 int i;
92 if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS3_MAXNAMLEN)) != NULL) {
93 for (i = 0, name = *namp; i < *lenp; i++, name++) {
94 if (*name == '\0' || *name == '/')
95 return NULL;
99 return p;
102 static inline u32 *
103 decode_pathname(u32 *p, char **namp, int *lenp)
105 char *name;
106 int i;
108 if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS3_MAXPATHLEN)) != NULL) {
109 for (i = 0, name = *namp; i < *lenp; i++, name++) {
110 if (*name == '\0')
111 return NULL;
115 return p;
118 static inline u32 *
119 decode_sattr3(u32 *p, struct iattr *iap)
121 u32 tmp;
123 iap->ia_valid = 0;
125 if (*p++) {
126 iap->ia_valid |= ATTR_MODE;
127 iap->ia_mode = ntohl(*p++);
129 if (*p++) {
130 iap->ia_valid |= ATTR_UID;
131 iap->ia_uid = ntohl(*p++);
133 if (*p++) {
134 iap->ia_valid |= ATTR_GID;
135 iap->ia_gid = ntohl(*p++);
137 if (*p++) {
138 u64 newsize;
140 iap->ia_valid |= ATTR_SIZE;
141 p = xdr_decode_hyper(p, &newsize);
142 if (newsize <= NFS_OFFSET_MAX)
143 iap->ia_size = newsize;
144 else
145 iap->ia_size = NFS_OFFSET_MAX;
147 if ((tmp = ntohl(*p++)) == 1) { /* set to server time */
148 iap->ia_valid |= ATTR_ATIME;
149 } else if (tmp == 2) { /* set to client time */
150 iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
151 iap->ia_atime.tv_sec = ntohl(*p++);
152 iap->ia_atime.tv_nsec = ntohl(*p++);
154 if ((tmp = ntohl(*p++)) == 1) { /* set to server time */
155 iap->ia_valid |= ATTR_MTIME;
156 } else if (tmp == 2) { /* set to client time */
157 iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
158 iap->ia_mtime.tv_sec = ntohl(*p++);
159 iap->ia_mtime.tv_nsec = ntohl(*p++);
161 return p;
164 static inline u32 *
165 encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
167 struct vfsmount *mnt = fhp->fh_export->ex_mnt;
168 struct dentry *dentry = fhp->fh_dentry;
169 struct kstat stat;
170 struct timespec time;
172 vfs_getattr(mnt, dentry, &stat);
174 *p++ = htonl(nfs3_ftypes[(stat.mode & S_IFMT) >> 12]);
175 *p++ = htonl((u32) stat.mode);
176 *p++ = htonl((u32) stat.nlink);
177 *p++ = htonl((u32) nfsd_ruid(rqstp, stat.uid));
178 *p++ = htonl((u32) nfsd_rgid(rqstp, stat.gid));
179 if (S_ISLNK(stat.mode) && stat.size > NFS3_MAXPATHLEN) {
180 p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
181 } else {
182 p = xdr_encode_hyper(p, (u64) stat.size);
184 p = xdr_encode_hyper(p, ((u64)stat.blocks) << 9);
185 *p++ = htonl((u32) MAJOR(stat.rdev));
186 *p++ = htonl((u32) MINOR(stat.rdev));
187 if (rqstp->rq_reffh->fh_version == 1
188 && rqstp->rq_reffh->fh_fsid_type == 1
189 && (fhp->fh_export->ex_flags & NFSEXP_FSID))
190 p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
191 else
192 p = xdr_encode_hyper(p, (u64) stat.dev);
193 p = xdr_encode_hyper(p, (u64) stat.ino);
194 p = encode_time3(p, &stat.atime);
195 lease_get_mtime(dentry->d_inode, &time);
196 p = encode_time3(p, &time);
197 p = encode_time3(p, &stat.ctime);
199 return p;
202 static inline u32 *
203 encode_saved_post_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
205 struct timespec time;
206 struct inode *inode = fhp->fh_dentry->d_inode;
208 /* Attributes to follow */
209 *p++ = xdr_one;
211 *p++ = htonl(nfs3_ftypes[(fhp->fh_post_mode & S_IFMT) >> 12]);
212 *p++ = htonl((u32) fhp->fh_post_mode);
213 *p++ = htonl((u32) fhp->fh_post_nlink);
214 *p++ = htonl((u32) nfsd_ruid(rqstp, fhp->fh_post_uid));
215 *p++ = htonl((u32) nfsd_rgid(rqstp, fhp->fh_post_gid));
216 if (S_ISLNK(fhp->fh_post_mode) && fhp->fh_post_size > NFS3_MAXPATHLEN) {
217 p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
218 } else {
219 p = xdr_encode_hyper(p, (u64) fhp->fh_post_size);
221 p = xdr_encode_hyper(p, ((u64)fhp->fh_post_blocks) << 9);
222 *p++ = fhp->fh_post_rdev[0];
223 *p++ = fhp->fh_post_rdev[1];
224 if (rqstp->rq_reffh->fh_version == 1
225 && rqstp->rq_reffh->fh_fsid_type == 1
226 && (fhp->fh_export->ex_flags & NFSEXP_FSID))
227 p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
228 else
229 p = xdr_encode_hyper(p, (u64) inode->i_dev);
230 p = xdr_encode_hyper(p, (u64) inode->i_ino);
231 time.tv_sec = fhp->fh_post_atime;
232 time.tv_nsec = 0;
233 p = encode_time3(p, &time);
234 time.tv_sec = fhp->fh_post_mtime;
235 p = encode_time3(p, &time);
236 time.tv_sec = fhp->fh_post_ctime;
237 p = encode_time3(p, &time);
239 return p;
243 * Encode post-operation attributes.
244 * The inode may be NULL if the call failed because of a stale file
245 * handle. In this case, no attributes are returned.
247 static u32 *
248 encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
250 struct dentry *dentry = fhp->fh_dentry;
251 if (dentry && dentry->d_inode != NULL) {
252 *p++ = xdr_one; /* attributes follow */
253 return encode_fattr3(rqstp, p, fhp);
255 *p++ = xdr_zero;
256 return p;
260 * Enocde weak cache consistency data
262 static u32 *
263 encode_wcc_data(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
265 struct dentry *dentry = fhp->fh_dentry;
267 if (dentry && dentry->d_inode && fhp->fh_post_saved) {
268 if (fhp->fh_pre_saved) {
269 struct timespec time;
270 *p++ = xdr_one;
271 p = xdr_encode_hyper(p, (u64) fhp->fh_pre_size);
272 time.tv_nsec = 0;
273 time.tv_sec = fhp->fh_pre_mtime;
274 p = encode_time3(p, &time);
275 time.tv_sec = fhp->fh_pre_ctime;
276 p = encode_time3(p, &time);
277 } else {
278 *p++ = xdr_zero;
280 return encode_saved_post_attr(rqstp, p, fhp);
282 /* no pre- or post-attrs */
283 *p++ = xdr_zero;
284 return encode_post_op_attr(rqstp, p, fhp);
289 * XDR decode functions
292 nfs3svc_decode_fhandle(struct svc_rqst *rqstp, u32 *p, struct nfsd_fhandle *args)
294 if (!(p = decode_fh(p, &args->fh)))
295 return 0;
296 return xdr_argsize_check(rqstp, p);
300 nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, u32 *p,
301 struct nfsd3_sattrargs *args)
303 if (!(p = decode_fh(p, &args->fh))
304 || !(p = decode_sattr3(p, &args->attrs)))
305 return 0;
307 if ((args->check_guard = ntohl(*p++)) != 0) {
308 struct timespec time;
309 p = decode_time3(p, &time);
310 args->guardtime = time.tv_sec;
313 return xdr_argsize_check(rqstp, p);
317 nfs3svc_decode_diropargs(struct svc_rqst *rqstp, u32 *p,
318 struct nfsd3_diropargs *args)
320 if (!(p = decode_fh(p, &args->fh))
321 || !(p = decode_filename(p, &args->name, &args->len)))
322 return 0;
324 return xdr_argsize_check(rqstp, p);
328 nfs3svc_decode_accessargs(struct svc_rqst *rqstp, u32 *p,
329 struct nfsd3_accessargs *args)
331 if (!(p = decode_fh(p, &args->fh)))
332 return 0;
333 args->access = ntohl(*p++);
335 return xdr_argsize_check(rqstp, p);
339 nfs3svc_decode_readargs(struct svc_rqst *rqstp, u32 *p,
340 struct nfsd3_readargs *args)
342 int len;
343 int v,pn;
345 if (!(p = decode_fh(p, &args->fh))
346 || !(p = xdr_decode_hyper(p, &args->offset)))
347 return 0;
349 len = args->count = ntohl(*p++);
351 if (len > NFSSVC_MAXBLKSIZE)
352 len = NFSSVC_MAXBLKSIZE;
354 /* set up the iovec */
355 v=0;
356 while (len > 0) {
357 pn = rqstp->rq_resused;
358 svc_take_page(rqstp);
359 args->vec[v].iov_base = page_address(rqstp->rq_respages[pn]);
360 args->vec[v].iov_len = len < PAGE_SIZE? len : PAGE_SIZE;
361 v++;
362 len -= PAGE_SIZE;
364 args->vlen = v;
365 return xdr_argsize_check(rqstp, p);
369 nfs3svc_decode_writeargs(struct svc_rqst *rqstp, u32 *p,
370 struct nfsd3_writeargs *args)
372 int len, v;
374 if (!(p = decode_fh(p, &args->fh))
375 || !(p = xdr_decode_hyper(p, &args->offset)))
376 return 0;
378 args->count = ntohl(*p++);
379 args->stable = ntohl(*p++);
380 len = args->len = ntohl(*p++);
382 args->vec[0].iov_base = (void*)p;
383 args->vec[0].iov_len = rqstp->rq_arg.head[0].iov_len -
384 (((void*)p) - rqstp->rq_arg.head[0].iov_base);
386 if (len > NFSSVC_MAXBLKSIZE)
387 len = NFSSVC_MAXBLKSIZE;
388 v= 0;
389 while (len > args->vec[v].iov_len) {
390 len -= args->vec[v].iov_len;
391 v++;
392 args->vec[v].iov_base = page_address(rqstp->rq_argpages[v]);
393 args->vec[v].iov_len = PAGE_SIZE;
395 args->vec[v].iov_len = len;
396 args->vlen = v+1;
398 return args->count == args->len && args->vec[0].iov_len > 0;
402 nfs3svc_decode_createargs(struct svc_rqst *rqstp, u32 *p,
403 struct nfsd3_createargs *args)
405 if (!(p = decode_fh(p, &args->fh))
406 || !(p = decode_filename(p, &args->name, &args->len)))
407 return 0;
409 switch (args->createmode = ntohl(*p++)) {
410 case NFS3_CREATE_UNCHECKED:
411 case NFS3_CREATE_GUARDED:
412 if (!(p = decode_sattr3(p, &args->attrs)))
413 return 0;
414 break;
415 case NFS3_CREATE_EXCLUSIVE:
416 args->verf = p;
417 p += 2;
418 break;
419 default:
420 return 0;
423 return xdr_argsize_check(rqstp, p);
426 nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, u32 *p,
427 struct nfsd3_createargs *args)
429 if (!(p = decode_fh(p, &args->fh))
430 || !(p = decode_filename(p, &args->name, &args->len))
431 || !(p = decode_sattr3(p, &args->attrs)))
432 return 0;
434 return xdr_argsize_check(rqstp, p);
438 nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p,
439 struct nfsd3_symlinkargs *args)
441 if (!(p = decode_fh(p, &args->ffh))
442 || !(p = decode_filename(p, &args->fname, &args->flen))
443 || !(p = decode_sattr3(p, &args->attrs))
444 || !(p = decode_pathname(p, &args->tname, &args->tlen)))
445 return 0;
447 return xdr_argsize_check(rqstp, p);
451 nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, u32 *p,
452 struct nfsd3_mknodargs *args)
454 if (!(p = decode_fh(p, &args->fh))
455 || !(p = decode_filename(p, &args->name, &args->len)))
456 return 0;
458 args->ftype = ntohl(*p++);
460 if (args->ftype == NF3BLK || args->ftype == NF3CHR
461 || args->ftype == NF3SOCK || args->ftype == NF3FIFO) {
462 if (!(p = decode_sattr3(p, &args->attrs)))
463 return 0;
466 if (args->ftype == NF3BLK || args->ftype == NF3CHR) {
467 args->major = ntohl(*p++);
468 args->minor = ntohl(*p++);
471 return xdr_argsize_check(rqstp, p);
475 nfs3svc_decode_renameargs(struct svc_rqst *rqstp, u32 *p,
476 struct nfsd3_renameargs *args)
478 if (!(p = decode_fh(p, &args->ffh))
479 || !(p = decode_filename(p, &args->fname, &args->flen))
480 || !(p = decode_fh(p, &args->tfh))
481 || !(p = decode_filename(p, &args->tname, &args->tlen)))
482 return 0;
484 return xdr_argsize_check(rqstp, p);
488 nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, u32 *p,
489 struct nfsd3_readlinkargs *args)
491 if (!(p = decode_fh(p, &args->fh)))
492 return 0;
493 svc_take_page(rqstp);
494 args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]);
496 return xdr_argsize_check(rqstp, p);
500 nfs3svc_decode_linkargs(struct svc_rqst *rqstp, u32 *p,
501 struct nfsd3_linkargs *args)
503 if (!(p = decode_fh(p, &args->ffh))
504 || !(p = decode_fh(p, &args->tfh))
505 || !(p = decode_filename(p, &args->tname, &args->tlen)))
506 return 0;
508 return xdr_argsize_check(rqstp, p);
512 nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p,
513 struct nfsd3_readdirargs *args)
515 if (!(p = decode_fh(p, &args->fh)))
516 return 0;
517 p = xdr_decode_hyper(p, &args->cookie);
518 args->verf = p; p += 2;
519 args->dircount = ~0;
520 args->count = ntohl(*p++);
522 if (args->count > PAGE_SIZE)
523 args->count = PAGE_SIZE;
525 svc_take_page(rqstp);
526 args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]);
528 return xdr_argsize_check(rqstp, p);
532 nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, u32 *p,
533 struct nfsd3_readdirargs *args)
535 if (!(p = decode_fh(p, &args->fh)))
536 return 0;
537 p = xdr_decode_hyper(p, &args->cookie);
538 args->verf = p; p += 2;
539 args->dircount = ntohl(*p++);
540 args->count = ntohl(*p++);
542 svc_take_page(rqstp);
543 args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]);
545 return xdr_argsize_check(rqstp, p);
549 nfs3svc_decode_commitargs(struct svc_rqst *rqstp, u32 *p,
550 struct nfsd3_commitargs *args)
552 if (!(p = decode_fh(p, &args->fh)))
553 return 0;
554 p = xdr_decode_hyper(p, &args->offset);
555 args->count = ntohl(*p++);
557 return xdr_argsize_check(rqstp, p);
561 * XDR encode functions
564 * There must be an encoding function for void results so svc_process
565 * will work properly.
568 nfs3svc_encode_voidres(struct svc_rqst *rqstp, u32 *p, void *dummy)
570 return xdr_ressize_check(rqstp, p);
573 /* GETATTR */
575 nfs3svc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
576 struct nfsd3_attrstat *resp)
578 if (resp->status == 0)
579 p = encode_fattr3(rqstp, p, &resp->fh);
580 return xdr_ressize_check(rqstp, p);
583 /* SETATTR, REMOVE, RMDIR */
585 nfs3svc_encode_wccstat(struct svc_rqst *rqstp, u32 *p,
586 struct nfsd3_attrstat *resp)
588 p = encode_wcc_data(rqstp, p, &resp->fh);
589 return xdr_ressize_check(rqstp, p);
592 /* LOOKUP */
594 nfs3svc_encode_diropres(struct svc_rqst *rqstp, u32 *p,
595 struct nfsd3_diropres *resp)
597 if (resp->status == 0) {
598 p = encode_fh(p, &resp->fh);
599 p = encode_post_op_attr(rqstp, p, &resp->fh);
601 p = encode_post_op_attr(rqstp, p, &resp->dirfh);
602 return xdr_ressize_check(rqstp, p);
605 /* ACCESS */
607 nfs3svc_encode_accessres(struct svc_rqst *rqstp, u32 *p,
608 struct nfsd3_accessres *resp)
610 p = encode_post_op_attr(rqstp, p, &resp->fh);
611 if (resp->status == 0)
612 *p++ = htonl(resp->access);
613 return xdr_ressize_check(rqstp, p);
616 /* READLINK */
618 nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p,
619 struct nfsd3_readlinkres *resp)
621 p = encode_post_op_attr(rqstp, p, &resp->fh);
622 if (resp->status == 0) {
623 *p++ = htonl(resp->len);
624 xdr_ressize_check(rqstp, p);
625 rqstp->rq_res.page_len = resp->len;
626 if (resp->len & 3) {
627 /* need to pad the tail */
628 rqstp->rq_res.tail[0].iov_base = p;
629 *p = 0;
630 rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3);
632 return 1;
633 } else
634 return xdr_ressize_check(rqstp, p);
637 /* READ */
639 nfs3svc_encode_readres(struct svc_rqst *rqstp, u32 *p,
640 struct nfsd3_readres *resp)
642 p = encode_post_op_attr(rqstp, p, &resp->fh);
643 if (resp->status == 0) {
644 *p++ = htonl(resp->count);
645 *p++ = htonl(resp->eof);
646 *p++ = htonl(resp->count); /* xdr opaque count */
647 xdr_ressize_check(rqstp, p);
648 /* now update rqstp->rq_res to reflect data aswell */
649 rqstp->rq_res.page_len = resp->count;
650 if (resp->count & 3) {
651 /* need to pad the tail */
652 rqstp->rq_res.tail[0].iov_base = p;
653 *p = 0;
654 rqstp->rq_res.tail[0].iov_len = 4 - (resp->count & 3);
656 return 1;
657 } else
658 return xdr_ressize_check(rqstp, p);
661 /* WRITE */
663 nfs3svc_encode_writeres(struct svc_rqst *rqstp, u32 *p,
664 struct nfsd3_writeres *resp)
666 p = encode_wcc_data(rqstp, p, &resp->fh);
667 if (resp->status == 0) {
668 *p++ = htonl(resp->count);
669 *p++ = htonl(resp->committed);
670 *p++ = htonl(nfssvc_boot.tv_sec);
671 *p++ = htonl(nfssvc_boot.tv_usec);
673 return xdr_ressize_check(rqstp, p);
676 /* CREATE, MKDIR, SYMLINK, MKNOD */
678 nfs3svc_encode_createres(struct svc_rqst *rqstp, u32 *p,
679 struct nfsd3_diropres *resp)
681 if (resp->status == 0) {
682 *p++ = xdr_one;
683 p = encode_fh(p, &resp->fh);
684 p = encode_post_op_attr(rqstp, p, &resp->fh);
686 p = encode_wcc_data(rqstp, p, &resp->dirfh);
687 return xdr_ressize_check(rqstp, p);
690 /* RENAME */
692 nfs3svc_encode_renameres(struct svc_rqst *rqstp, u32 *p,
693 struct nfsd3_renameres *resp)
695 p = encode_wcc_data(rqstp, p, &resp->ffh);
696 p = encode_wcc_data(rqstp, p, &resp->tfh);
697 return xdr_ressize_check(rqstp, p);
700 /* LINK */
702 nfs3svc_encode_linkres(struct svc_rqst *rqstp, u32 *p,
703 struct nfsd3_linkres *resp)
705 p = encode_post_op_attr(rqstp, p, &resp->fh);
706 p = encode_wcc_data(rqstp, p, &resp->tfh);
707 return xdr_ressize_check(rqstp, p);
710 /* READDIR */
712 nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p,
713 struct nfsd3_readdirres *resp)
715 p = encode_post_op_attr(rqstp, p, &resp->fh);
717 if (resp->status == 0) {
718 /* stupid readdir cookie */
719 memcpy(p, resp->verf, 8); p += 2;
720 xdr_ressize_check(rqstp, p);
721 p = resp->buffer;
722 *p++ = 0; /* no more entries */
723 *p++ = htonl(resp->common.err == nfserr_eof);
724 rqstp->rq_res.page_len = ((unsigned long)p & ~PAGE_MASK);
725 return 1;
726 } else
727 return xdr_ressize_check(rqstp, p);
731 * Encode a directory entry. This one works for both normal readdir
732 * and readdirplus.
733 * The normal readdir reply requires 2 (fileid) + 1 (stringlen)
734 * + string + 2 (cookie) + 1 (next) words, i.e. 6 + strlen.
736 * The readdirplus baggage is 1+21 words for post_op_attr, plus the
737 * file handle.
740 #define NFS3_ENTRY_BAGGAGE (2 + 1 + 2 + 1)
741 #define NFS3_ENTRYPLUS_BAGGAGE (1 + 21 + 1 + (NFS3_FHSIZE >> 2))
742 static int
743 encode_entry(struct readdir_cd *ccd, const char *name,
744 int namlen, off_t offset, ino_t ino, unsigned int d_type, int plus)
746 struct nfsd3_readdirres *cd = container_of(ccd, struct nfsd3_readdirres, common);
747 u32 *p = cd->buffer;
748 int buflen, slen, elen;
750 if (cd->offset)
751 xdr_encode_hyper(cd->offset, (u64) offset);
754 dprintk("encode_entry(%.*s @%ld%s)\n",
755 namlen, name, (long) offset, plus? " plus" : "");
758 /* truncate filename if too long */
759 if (namlen > NFS3_MAXNAMLEN)
760 namlen = NFS3_MAXNAMLEN;
762 slen = XDR_QUADLEN(namlen);
763 elen = slen + NFS3_ENTRY_BAGGAGE
764 + (plus? NFS3_ENTRYPLUS_BAGGAGE : 0);
765 if ((buflen = cd->buflen - elen) < 0) {
766 cd->common.err = nfserr_readdir_nospc;
767 return -EINVAL;
769 *p++ = xdr_one; /* mark entry present */
770 p = xdr_encode_hyper(p, ino); /* file id */
771 p = xdr_encode_array(p, name, namlen);/* name length & name */
773 cd->offset = p; /* remember pointer */
774 p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */
776 /* throw in readdirplus baggage */
777 if (plus) {
778 struct svc_fh fh;
779 struct svc_export *exp;
780 struct dentry *dparent, *dchild;
782 dparent = cd->fh.fh_dentry;
783 exp = cd->fh.fh_export;
785 fh_init(&fh, NFS3_FHSIZE);
786 if (isdotent(name, namlen)) {
787 if (namlen == 2) {
788 read_lock(&dparent_lock);
789 dchild = dget(dparent->d_parent);
790 read_unlock(&dparent_lock);
791 } else
792 dchild = dget(dparent);
793 } else
794 dchild = lookup_one_len(name, dparent,namlen);
795 if (IS_ERR(dchild))
796 goto noexec;
797 if (fh_compose(&fh, exp, dchild, &cd->fh) != 0 || !dchild->d_inode)
798 goto noexec;
799 p = encode_post_op_attr(cd->rqstp, p, &fh);
800 *p++ = xdr_one; /* yes, a file handle follows */
801 p = encode_fh(p, &fh);
802 fh_put(&fh);
805 out:
806 cd->buflen = buflen;
807 cd->buffer = p;
808 cd->common.err = nfs_ok;
809 return 0;
811 noexec:
812 *p++ = 0;
813 *p++ = 0;
814 goto out;
818 nfs3svc_encode_entry(struct readdir_cd *cd, const char *name,
819 int namlen, loff_t offset, ino_t ino, unsigned int d_type)
821 return encode_entry(cd, name, namlen, offset, ino, d_type, 0);
825 nfs3svc_encode_entry_plus(struct readdir_cd *cd, const char *name,
826 int namlen, loff_t offset, ino_t ino, unsigned int d_type)
828 return encode_entry(cd, name, namlen, offset, ino, d_type, 1);
831 /* FSSTAT */
833 nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, u32 *p,
834 struct nfsd3_fsstatres *resp)
836 struct statfs *s = &resp->stats;
837 u64 bs = s->f_bsize;
839 *p++ = xdr_zero; /* no post_op_attr */
841 if (resp->status == 0) {
842 p = xdr_encode_hyper(p, bs * s->f_blocks); /* total bytes */
843 p = xdr_encode_hyper(p, bs * s->f_bfree); /* free bytes */
844 p = xdr_encode_hyper(p, bs * s->f_bavail); /* user available bytes */
845 p = xdr_encode_hyper(p, s->f_files); /* total inodes */
846 p = xdr_encode_hyper(p, s->f_ffree); /* free inodes */
847 p = xdr_encode_hyper(p, s->f_ffree); /* user available inodes */
848 *p++ = htonl(resp->invarsec); /* mean unchanged time */
850 return xdr_ressize_check(rqstp, p);
853 /* FSINFO */
855 nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, u32 *p,
856 struct nfsd3_fsinfores *resp)
858 *p++ = xdr_zero; /* no post_op_attr */
860 if (resp->status == 0) {
861 *p++ = htonl(resp->f_rtmax);
862 *p++ = htonl(resp->f_rtpref);
863 *p++ = htonl(resp->f_rtmult);
864 *p++ = htonl(resp->f_wtmax);
865 *p++ = htonl(resp->f_wtpref);
866 *p++ = htonl(resp->f_wtmult);
867 *p++ = htonl(resp->f_dtpref);
868 p = xdr_encode_hyper(p, resp->f_maxfilesize);
869 *p++ = xdr_one;
870 *p++ = xdr_zero;
871 *p++ = htonl(resp->f_properties);
874 return xdr_ressize_check(rqstp, p);
877 /* PATHCONF */
879 nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, u32 *p,
880 struct nfsd3_pathconfres *resp)
882 *p++ = xdr_zero; /* no post_op_attr */
884 if (resp->status == 0) {
885 *p++ = htonl(resp->p_link_max);
886 *p++ = htonl(resp->p_name_max);
887 *p++ = htonl(resp->p_no_trunc);
888 *p++ = htonl(resp->p_chown_restricted);
889 *p++ = htonl(resp->p_case_insensitive);
890 *p++ = htonl(resp->p_case_preserving);
893 return xdr_ressize_check(rqstp, p);
896 /* COMMIT */
898 nfs3svc_encode_commitres(struct svc_rqst *rqstp, u32 *p,
899 struct nfsd3_commitres *resp)
901 p = encode_wcc_data(rqstp, p, &resp->fh);
902 /* Write verifier */
903 if (resp->status == 0) {
904 *p++ = htonl(nfssvc_boot.tv_sec);
905 *p++ = htonl(nfssvc_boot.tv_usec);
907 return xdr_ressize_check(rqstp, p);
911 * XDR release functions
914 nfs3svc_release_fhandle(struct svc_rqst *rqstp, u32 *p,
915 struct nfsd3_attrstat *resp)
917 fh_put(&resp->fh);
918 return 1;
922 nfs3svc_release_fhandle2(struct svc_rqst *rqstp, u32 *p,
923 struct nfsd3_fhandle_pair *resp)
925 fh_put(&resp->fh1);
926 fh_put(&resp->fh2);
927 return 1;