Merge with Linux 2.5.74.
[linux-2.6/linux-mips.git] / fs / nfsd / nfs3xdr.c
blob2e53ddd8393587bdfa176e08780a45f4287092c7
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>
17 #include <linux/vfs.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 unsigned 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 inode *inode = fhp->fh_dentry->d_inode;
207 /* Attributes to follow */
208 *p++ = xdr_one;
210 *p++ = htonl(nfs3_ftypes[(fhp->fh_post_mode & S_IFMT) >> 12]);
211 *p++ = htonl((u32) fhp->fh_post_mode);
212 *p++ = htonl((u32) fhp->fh_post_nlink);
213 *p++ = htonl((u32) nfsd_ruid(rqstp, fhp->fh_post_uid));
214 *p++ = htonl((u32) nfsd_rgid(rqstp, fhp->fh_post_gid));
215 if (S_ISLNK(fhp->fh_post_mode) && fhp->fh_post_size > NFS3_MAXPATHLEN) {
216 p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
217 } else {
218 p = xdr_encode_hyper(p, (u64) fhp->fh_post_size);
220 p = xdr_encode_hyper(p, ((u64)fhp->fh_post_blocks) << 9);
221 *p++ = fhp->fh_post_rdev[0];
222 *p++ = fhp->fh_post_rdev[1];
223 if (rqstp->rq_reffh->fh_version == 1
224 && rqstp->rq_reffh->fh_fsid_type == 1
225 && (fhp->fh_export->ex_flags & NFSEXP_FSID))
226 p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
227 else
228 p = xdr_encode_hyper(p, (u64) inode->i_sb->s_dev);
229 p = xdr_encode_hyper(p, (u64) inode->i_ino);
230 p = encode_time3(p, &fhp->fh_post_atime);
231 p = encode_time3(p, &fhp->fh_post_mtime);
232 p = encode_time3(p, &fhp->fh_post_ctime);
234 return p;
238 * Encode post-operation attributes.
239 * The inode may be NULL if the call failed because of a stale file
240 * handle. In this case, no attributes are returned.
242 static u32 *
243 encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
245 struct dentry *dentry = fhp->fh_dentry;
246 if (dentry && dentry->d_inode != NULL) {
247 *p++ = xdr_one; /* attributes follow */
248 return encode_fattr3(rqstp, p, fhp);
250 *p++ = xdr_zero;
251 return p;
255 * Enocde weak cache consistency data
257 static u32 *
258 encode_wcc_data(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
260 struct dentry *dentry = fhp->fh_dentry;
262 if (dentry && dentry->d_inode && fhp->fh_post_saved) {
263 if (fhp->fh_pre_saved) {
264 *p++ = xdr_one;
265 p = xdr_encode_hyper(p, (u64) fhp->fh_pre_size);
266 p = encode_time3(p, &fhp->fh_pre_mtime);
267 p = encode_time3(p, &fhp->fh_pre_ctime);
268 } else {
269 *p++ = xdr_zero;
271 return encode_saved_post_attr(rqstp, p, fhp);
273 /* no pre- or post-attrs */
274 *p++ = xdr_zero;
275 return encode_post_op_attr(rqstp, p, fhp);
280 * XDR decode functions
283 nfs3svc_decode_fhandle(struct svc_rqst *rqstp, u32 *p, struct nfsd_fhandle *args)
285 if (!(p = decode_fh(p, &args->fh)))
286 return 0;
287 return xdr_argsize_check(rqstp, p);
291 nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, u32 *p,
292 struct nfsd3_sattrargs *args)
294 if (!(p = decode_fh(p, &args->fh))
295 || !(p = decode_sattr3(p, &args->attrs)))
296 return 0;
298 if ((args->check_guard = ntohl(*p++)) != 0) {
299 struct timespec time;
300 p = decode_time3(p, &time);
301 args->guardtime = time.tv_sec;
304 return xdr_argsize_check(rqstp, p);
308 nfs3svc_decode_diropargs(struct svc_rqst *rqstp, u32 *p,
309 struct nfsd3_diropargs *args)
311 if (!(p = decode_fh(p, &args->fh))
312 || !(p = decode_filename(p, &args->name, &args->len)))
313 return 0;
315 return xdr_argsize_check(rqstp, p);
319 nfs3svc_decode_accessargs(struct svc_rqst *rqstp, u32 *p,
320 struct nfsd3_accessargs *args)
322 if (!(p = decode_fh(p, &args->fh)))
323 return 0;
324 args->access = ntohl(*p++);
326 return xdr_argsize_check(rqstp, p);
330 nfs3svc_decode_readargs(struct svc_rqst *rqstp, u32 *p,
331 struct nfsd3_readargs *args)
333 int len;
334 int v,pn;
336 if (!(p = decode_fh(p, &args->fh))
337 || !(p = xdr_decode_hyper(p, &args->offset)))
338 return 0;
340 len = args->count = ntohl(*p++);
342 if (len > NFSSVC_MAXBLKSIZE)
343 len = NFSSVC_MAXBLKSIZE;
345 /* set up the iovec */
346 v=0;
347 while (len > 0) {
348 pn = rqstp->rq_resused;
349 svc_take_page(rqstp);
350 args->vec[v].iov_base = page_address(rqstp->rq_respages[pn]);
351 args->vec[v].iov_len = len < PAGE_SIZE? len : PAGE_SIZE;
352 v++;
353 len -= PAGE_SIZE;
355 args->vlen = v;
356 return xdr_argsize_check(rqstp, p);
360 nfs3svc_decode_writeargs(struct svc_rqst *rqstp, u32 *p,
361 struct nfsd3_writeargs *args)
363 int len, v;
365 if (!(p = decode_fh(p, &args->fh))
366 || !(p = xdr_decode_hyper(p, &args->offset)))
367 return 0;
369 args->count = ntohl(*p++);
370 args->stable = ntohl(*p++);
371 len = args->len = ntohl(*p++);
373 args->vec[0].iov_base = (void*)p;
374 args->vec[0].iov_len = rqstp->rq_arg.head[0].iov_len -
375 (((void*)p) - rqstp->rq_arg.head[0].iov_base);
377 if (len > NFSSVC_MAXBLKSIZE)
378 len = NFSSVC_MAXBLKSIZE;
379 v= 0;
380 while (len > args->vec[v].iov_len) {
381 len -= args->vec[v].iov_len;
382 v++;
383 args->vec[v].iov_base = page_address(rqstp->rq_argpages[v]);
384 args->vec[v].iov_len = PAGE_SIZE;
386 args->vec[v].iov_len = len;
387 args->vlen = v+1;
389 return args->count == args->len && args->vec[0].iov_len > 0;
393 nfs3svc_decode_createargs(struct svc_rqst *rqstp, u32 *p,
394 struct nfsd3_createargs *args)
396 if (!(p = decode_fh(p, &args->fh))
397 || !(p = decode_filename(p, &args->name, &args->len)))
398 return 0;
400 switch (args->createmode = ntohl(*p++)) {
401 case NFS3_CREATE_UNCHECKED:
402 case NFS3_CREATE_GUARDED:
403 if (!(p = decode_sattr3(p, &args->attrs)))
404 return 0;
405 break;
406 case NFS3_CREATE_EXCLUSIVE:
407 args->verf = p;
408 p += 2;
409 break;
410 default:
411 return 0;
414 return xdr_argsize_check(rqstp, p);
417 nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, u32 *p,
418 struct nfsd3_createargs *args)
420 if (!(p = decode_fh(p, &args->fh))
421 || !(p = decode_filename(p, &args->name, &args->len))
422 || !(p = decode_sattr3(p, &args->attrs)))
423 return 0;
425 return xdr_argsize_check(rqstp, p);
429 nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p,
430 struct nfsd3_symlinkargs *args)
432 int len;
433 int avail;
434 char *old, *new;
435 struct iovec *vec;
437 if (!(p = decode_fh(p, &args->ffh))
438 || !(p = decode_filename(p, &args->fname, &args->flen))
439 || !(p = decode_sattr3(p, &args->attrs))
441 return 0;
442 /* now decode the pathname, which might be larger than the first page.
443 * As we have to check for nul's anyway, we copy it into a new page
444 * This page appears in the rq_res.pages list, but as pages_len is always
445 * 0, it won't get in the way
447 svc_take_page(rqstp);
448 len = ntohl(*p++);
449 if (len <= 0 || len > NFS3_MAXPATHLEN)
450 return 0;
451 args->tname = new = page_address(rqstp->rq_respages[rqstp->rq_resused-1]);
452 args->tlen = len;
453 /* first copy and check from the first page */
454 old = (char*)p;
455 vec = &rqstp->rq_arg.head[0];
456 avail = vec->iov_len - (old - (char*)vec->iov_base);
457 while (len > 0 && *old && avail) {
458 *new++ = *old++;
459 len--;
460 avail--;
462 /* now copy next page if there is one */
463 if (len && !avail && rqstp->rq_arg.page_len) {
464 avail = rqstp->rq_arg.page_len;
465 if (avail > PAGE_SIZE) avail = PAGE_SIZE;
466 old = page_address(rqstp->rq_arg.pages[0]);
468 while (len > 0 && *old && avail) {
469 *new++ = *old++;
470 len--;
471 avail--;
473 *new = '\0';
474 if (len)
475 return 0;
477 return 1;
481 nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, u32 *p,
482 struct nfsd3_mknodargs *args)
484 if (!(p = decode_fh(p, &args->fh))
485 || !(p = decode_filename(p, &args->name, &args->len)))
486 return 0;
488 args->ftype = ntohl(*p++);
490 if (args->ftype == NF3BLK || args->ftype == NF3CHR
491 || args->ftype == NF3SOCK || args->ftype == NF3FIFO) {
492 if (!(p = decode_sattr3(p, &args->attrs)))
493 return 0;
496 if (args->ftype == NF3BLK || args->ftype == NF3CHR) {
497 args->major = ntohl(*p++);
498 args->minor = ntohl(*p++);
501 return xdr_argsize_check(rqstp, p);
505 nfs3svc_decode_renameargs(struct svc_rqst *rqstp, u32 *p,
506 struct nfsd3_renameargs *args)
508 if (!(p = decode_fh(p, &args->ffh))
509 || !(p = decode_filename(p, &args->fname, &args->flen))
510 || !(p = decode_fh(p, &args->tfh))
511 || !(p = decode_filename(p, &args->tname, &args->tlen)))
512 return 0;
514 return xdr_argsize_check(rqstp, p);
518 nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, u32 *p,
519 struct nfsd3_readlinkargs *args)
521 if (!(p = decode_fh(p, &args->fh)))
522 return 0;
523 svc_take_page(rqstp);
524 args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]);
526 return xdr_argsize_check(rqstp, p);
530 nfs3svc_decode_linkargs(struct svc_rqst *rqstp, u32 *p,
531 struct nfsd3_linkargs *args)
533 if (!(p = decode_fh(p, &args->ffh))
534 || !(p = decode_fh(p, &args->tfh))
535 || !(p = decode_filename(p, &args->tname, &args->tlen)))
536 return 0;
538 return xdr_argsize_check(rqstp, p);
542 nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p,
543 struct nfsd3_readdirargs *args)
545 if (!(p = decode_fh(p, &args->fh)))
546 return 0;
547 p = xdr_decode_hyper(p, &args->cookie);
548 args->verf = p; p += 2;
549 args->dircount = ~0;
550 args->count = ntohl(*p++);
552 if (args->count > PAGE_SIZE)
553 args->count = PAGE_SIZE;
555 svc_take_page(rqstp);
556 args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]);
558 return xdr_argsize_check(rqstp, p);
562 nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, u32 *p,
563 struct nfsd3_readdirargs *args)
565 if (!(p = decode_fh(p, &args->fh)))
566 return 0;
567 p = xdr_decode_hyper(p, &args->cookie);
568 args->verf = p; p += 2;
569 args->dircount = ntohl(*p++);
570 args->count = ntohl(*p++);
572 if (args->count > PAGE_SIZE)
573 args->count = PAGE_SIZE;
575 svc_take_page(rqstp);
576 args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]);
578 return xdr_argsize_check(rqstp, p);
582 nfs3svc_decode_commitargs(struct svc_rqst *rqstp, u32 *p,
583 struct nfsd3_commitargs *args)
585 if (!(p = decode_fh(p, &args->fh)))
586 return 0;
587 p = xdr_decode_hyper(p, &args->offset);
588 args->count = ntohl(*p++);
590 return xdr_argsize_check(rqstp, p);
594 * XDR encode functions
597 * There must be an encoding function for void results so svc_process
598 * will work properly.
601 nfs3svc_encode_voidres(struct svc_rqst *rqstp, u32 *p, void *dummy)
603 return xdr_ressize_check(rqstp, p);
606 /* GETATTR */
608 nfs3svc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
609 struct nfsd3_attrstat *resp)
611 if (resp->status == 0)
612 p = encode_fattr3(rqstp, p, &resp->fh);
613 return xdr_ressize_check(rqstp, p);
616 /* SETATTR, REMOVE, RMDIR */
618 nfs3svc_encode_wccstat(struct svc_rqst *rqstp, u32 *p,
619 struct nfsd3_attrstat *resp)
621 p = encode_wcc_data(rqstp, p, &resp->fh);
622 return xdr_ressize_check(rqstp, p);
625 /* LOOKUP */
627 nfs3svc_encode_diropres(struct svc_rqst *rqstp, u32 *p,
628 struct nfsd3_diropres *resp)
630 if (resp->status == 0) {
631 p = encode_fh(p, &resp->fh);
632 p = encode_post_op_attr(rqstp, p, &resp->fh);
634 p = encode_post_op_attr(rqstp, p, &resp->dirfh);
635 return xdr_ressize_check(rqstp, p);
638 /* ACCESS */
640 nfs3svc_encode_accessres(struct svc_rqst *rqstp, u32 *p,
641 struct nfsd3_accessres *resp)
643 p = encode_post_op_attr(rqstp, p, &resp->fh);
644 if (resp->status == 0)
645 *p++ = htonl(resp->access);
646 return xdr_ressize_check(rqstp, p);
649 /* READLINK */
651 nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p,
652 struct nfsd3_readlinkres *resp)
654 p = encode_post_op_attr(rqstp, p, &resp->fh);
655 if (resp->status == 0) {
656 *p++ = htonl(resp->len);
657 xdr_ressize_check(rqstp, p);
658 rqstp->rq_res.page_len = resp->len;
659 if (resp->len & 3) {
660 /* need to pad the tail */
661 rqstp->rq_restailpage = 0;
662 rqstp->rq_res.tail[0].iov_base = p;
663 *p = 0;
664 rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3);
666 return 1;
667 } else
668 return xdr_ressize_check(rqstp, p);
671 /* READ */
673 nfs3svc_encode_readres(struct svc_rqst *rqstp, u32 *p,
674 struct nfsd3_readres *resp)
676 p = encode_post_op_attr(rqstp, p, &resp->fh);
677 if (resp->status == 0) {
678 *p++ = htonl(resp->count);
679 *p++ = htonl(resp->eof);
680 *p++ = htonl(resp->count); /* xdr opaque count */
681 xdr_ressize_check(rqstp, p);
682 /* now update rqstp->rq_res to reflect data aswell */
683 rqstp->rq_res.page_len = resp->count;
684 if (resp->count & 3) {
685 /* need to pad the tail */
686 rqstp->rq_restailpage = 0;
687 rqstp->rq_res.tail[0].iov_base = p;
688 *p = 0;
689 rqstp->rq_res.tail[0].iov_len = 4 - (resp->count & 3);
691 return 1;
692 } else
693 return xdr_ressize_check(rqstp, p);
696 /* WRITE */
698 nfs3svc_encode_writeres(struct svc_rqst *rqstp, u32 *p,
699 struct nfsd3_writeres *resp)
701 p = encode_wcc_data(rqstp, p, &resp->fh);
702 if (resp->status == 0) {
703 *p++ = htonl(resp->count);
704 *p++ = htonl(resp->committed);
705 *p++ = htonl(nfssvc_boot.tv_sec);
706 *p++ = htonl(nfssvc_boot.tv_usec);
708 return xdr_ressize_check(rqstp, p);
711 /* CREATE, MKDIR, SYMLINK, MKNOD */
713 nfs3svc_encode_createres(struct svc_rqst *rqstp, u32 *p,
714 struct nfsd3_diropres *resp)
716 if (resp->status == 0) {
717 *p++ = xdr_one;
718 p = encode_fh(p, &resp->fh);
719 p = encode_post_op_attr(rqstp, p, &resp->fh);
721 p = encode_wcc_data(rqstp, p, &resp->dirfh);
722 return xdr_ressize_check(rqstp, p);
725 /* RENAME */
727 nfs3svc_encode_renameres(struct svc_rqst *rqstp, u32 *p,
728 struct nfsd3_renameres *resp)
730 p = encode_wcc_data(rqstp, p, &resp->ffh);
731 p = encode_wcc_data(rqstp, p, &resp->tfh);
732 return xdr_ressize_check(rqstp, p);
735 /* LINK */
737 nfs3svc_encode_linkres(struct svc_rqst *rqstp, u32 *p,
738 struct nfsd3_linkres *resp)
740 p = encode_post_op_attr(rqstp, p, &resp->fh);
741 p = encode_wcc_data(rqstp, p, &resp->tfh);
742 return xdr_ressize_check(rqstp, p);
745 /* READDIR */
747 nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p,
748 struct nfsd3_readdirres *resp)
750 p = encode_post_op_attr(rqstp, p, &resp->fh);
752 if (resp->status == 0) {
753 /* stupid readdir cookie */
754 memcpy(p, resp->verf, 8); p += 2;
755 xdr_ressize_check(rqstp, p);
756 p = resp->buffer;
757 *p++ = 0; /* no more entries */
758 *p++ = htonl(resp->common.err == nfserr_eof);
759 rqstp->rq_res.page_len = (((unsigned long)p-1) & ~PAGE_MASK)+1;
760 return 1;
761 } else
762 return xdr_ressize_check(rqstp, p);
766 * Encode a directory entry. This one works for both normal readdir
767 * and readdirplus.
768 * The normal readdir reply requires 2 (fileid) + 1 (stringlen)
769 * + string + 2 (cookie) + 1 (next) words, i.e. 6 + strlen.
771 * The readdirplus baggage is 1+21 words for post_op_attr, plus the
772 * file handle.
775 #define NFS3_ENTRY_BAGGAGE (2 + 1 + 2 + 1)
776 #define NFS3_ENTRYPLUS_BAGGAGE (1 + 21 + 1 + (NFS3_FHSIZE >> 2))
777 static int
778 encode_entry(struct readdir_cd *ccd, const char *name,
779 int namlen, off_t offset, ino_t ino, unsigned int d_type, int plus)
781 struct nfsd3_readdirres *cd = container_of(ccd, struct nfsd3_readdirres, common);
782 u32 *p = cd->buffer;
783 int slen, elen;
785 if (cd->offset)
786 xdr_encode_hyper(cd->offset, (u64) offset);
789 dprintk("encode_entry(%.*s @%ld%s)\n",
790 namlen, name, (long) offset, plus? " plus" : "");
793 /* truncate filename if too long */
794 if (namlen > NFS3_MAXNAMLEN)
795 namlen = NFS3_MAXNAMLEN;
797 slen = XDR_QUADLEN(namlen);
798 elen = slen + NFS3_ENTRY_BAGGAGE
799 + (plus? NFS3_ENTRYPLUS_BAGGAGE : 0);
800 if (cd->buflen < elen) {
801 cd->common.err = nfserr_readdir_nospc;
802 return -EINVAL;
804 *p++ = xdr_one; /* mark entry present */
805 p = xdr_encode_hyper(p, ino); /* file id */
806 p = xdr_encode_array(p, name, namlen);/* name length & name */
808 cd->offset = p; /* remember pointer */
809 p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */
811 /* throw in readdirplus baggage */
812 if (plus) {
813 struct svc_fh fh;
814 struct svc_export *exp;
815 struct dentry *dparent, *dchild;
817 dparent = cd->fh.fh_dentry;
818 exp = cd->fh.fh_export;
820 fh_init(&fh, NFS3_FHSIZE);
821 if (isdotent(name, namlen)) {
822 if (namlen == 2) {
823 dchild = dget_parent(dparent);
824 } else
825 dchild = dget(dparent);
826 } else
827 dchild = lookup_one_len(name, dparent,namlen);
828 if (IS_ERR(dchild))
829 goto noexec;
830 if (fh_compose(&fh, exp, dchild, &cd->fh) != 0 || !dchild->d_inode)
831 goto noexec;
832 p = encode_post_op_attr(cd->rqstp, p, &fh);
833 *p++ = xdr_one; /* yes, a file handle follows */
834 p = encode_fh(p, &fh);
835 fh_put(&fh);
838 out:
839 cd->buflen -= p - cd->buffer;
840 cd->buffer = p;
841 cd->common.err = nfs_ok;
842 return 0;
844 noexec:
845 *p++ = 0;
846 *p++ = 0;
847 goto out;
851 nfs3svc_encode_entry(struct readdir_cd *cd, const char *name,
852 int namlen, loff_t offset, ino_t ino, unsigned int d_type)
854 return encode_entry(cd, name, namlen, offset, ino, d_type, 0);
858 nfs3svc_encode_entry_plus(struct readdir_cd *cd, const char *name,
859 int namlen, loff_t offset, ino_t ino, unsigned int d_type)
861 return encode_entry(cd, name, namlen, offset, ino, d_type, 1);
864 /* FSSTAT */
866 nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, u32 *p,
867 struct nfsd3_fsstatres *resp)
869 struct kstatfs *s = &resp->stats;
870 u64 bs = s->f_bsize;
872 *p++ = xdr_zero; /* no post_op_attr */
874 if (resp->status == 0) {
875 p = xdr_encode_hyper(p, bs * s->f_blocks); /* total bytes */
876 p = xdr_encode_hyper(p, bs * s->f_bfree); /* free bytes */
877 p = xdr_encode_hyper(p, bs * s->f_bavail); /* user available bytes */
878 p = xdr_encode_hyper(p, s->f_files); /* total inodes */
879 p = xdr_encode_hyper(p, s->f_ffree); /* free inodes */
880 p = xdr_encode_hyper(p, s->f_ffree); /* user available inodes */
881 *p++ = htonl(resp->invarsec); /* mean unchanged time */
883 return xdr_ressize_check(rqstp, p);
886 /* FSINFO */
888 nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, u32 *p,
889 struct nfsd3_fsinfores *resp)
891 *p++ = xdr_zero; /* no post_op_attr */
893 if (resp->status == 0) {
894 *p++ = htonl(resp->f_rtmax);
895 *p++ = htonl(resp->f_rtpref);
896 *p++ = htonl(resp->f_rtmult);
897 *p++ = htonl(resp->f_wtmax);
898 *p++ = htonl(resp->f_wtpref);
899 *p++ = htonl(resp->f_wtmult);
900 *p++ = htonl(resp->f_dtpref);
901 p = xdr_encode_hyper(p, resp->f_maxfilesize);
902 *p++ = xdr_one;
903 *p++ = xdr_zero;
904 *p++ = htonl(resp->f_properties);
907 return xdr_ressize_check(rqstp, p);
910 /* PATHCONF */
912 nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, u32 *p,
913 struct nfsd3_pathconfres *resp)
915 *p++ = xdr_zero; /* no post_op_attr */
917 if (resp->status == 0) {
918 *p++ = htonl(resp->p_link_max);
919 *p++ = htonl(resp->p_name_max);
920 *p++ = htonl(resp->p_no_trunc);
921 *p++ = htonl(resp->p_chown_restricted);
922 *p++ = htonl(resp->p_case_insensitive);
923 *p++ = htonl(resp->p_case_preserving);
926 return xdr_ressize_check(rqstp, p);
929 /* COMMIT */
931 nfs3svc_encode_commitres(struct svc_rqst *rqstp, u32 *p,
932 struct nfsd3_commitres *resp)
934 p = encode_wcc_data(rqstp, p, &resp->fh);
935 /* Write verifier */
936 if (resp->status == 0) {
937 *p++ = htonl(nfssvc_boot.tv_sec);
938 *p++ = htonl(nfssvc_boot.tv_usec);
940 return xdr_ressize_check(rqstp, p);
944 * XDR release functions
947 nfs3svc_release_fhandle(struct svc_rqst *rqstp, u32 *p,
948 struct nfsd3_attrstat *resp)
950 fh_put(&resp->fh);
951 return 1;
955 nfs3svc_release_fhandle2(struct svc_rqst *rqstp, u32 *p,
956 struct nfsd3_fhandle_pair *resp)
958 fh_put(&resp->fh1);
959 fh_put(&resp->fh2);
960 return 1;