xref: /openbmc/linux/fs/nfsd/nfs3xdr.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * XDR support for nfsd/protocol version 3.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * 2003-08-09 Jamie Lokier: Use htonl() for nanoseconds, not htons()!
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds #include <linux/namei.h>
11b9c0ef85SStanislav Kinsbursky #include <linux/sunrpc/svc_xprt.h>
129a74af21SBoaz Harrosh #include "xdr3.h"
132e8138a2SJ. Bruce Fields #include "auth.h"
14b9c0ef85SStanislav Kinsbursky #include "netns.h"
153dadecceSAl Viro #include "vfs.h"
161da177e4SLinus Torvalds 
171da177e4SLinus Torvalds /*
188b704498SChuck Lever  * Force construction of an empty post-op attr
198b704498SChuck Lever  */
208b704498SChuck Lever static const struct svc_fh nfs3svc_null_fh = {
218b704498SChuck Lever 	.fh_no_wcc	= true,
228b704498SChuck Lever };
238b704498SChuck Lever 
248b704498SChuck Lever /*
250a139d1bSChuck Lever  * time_delta. {1, 0} means the server is accurate only
260a139d1bSChuck Lever  * to the nearest second.
270a139d1bSChuck Lever  */
280a139d1bSChuck Lever static const struct timespec64 nfs3svc_time_delta = {
290a139d1bSChuck Lever 	.tv_sec		= 1,
300a139d1bSChuck Lever 	.tv_nsec	= 0,
310a139d1bSChuck Lever };
320a139d1bSChuck Lever 
330a139d1bSChuck Lever /*
341da177e4SLinus Torvalds  * Mapping of S_IF* types to NFS file types
351da177e4SLinus Torvalds  */
362c42f804SChuck Lever static const u32 nfs3_ftypes[] = {
371da177e4SLinus Torvalds 	NF3NON,  NF3FIFO, NF3CHR, NF3BAD,
381da177e4SLinus Torvalds 	NF3DIR,  NF3BAD,  NF3BLK, NF3BAD,
391da177e4SLinus Torvalds 	NF3REG,  NF3BAD,  NF3LNK, NF3BAD,
401da177e4SLinus Torvalds 	NF3SOCK, NF3BAD,  NF3LNK, NF3BAD,
411da177e4SLinus Torvalds };
421da177e4SLinus Torvalds 
4327c438f5STrond Myklebust 
441da177e4SLinus Torvalds /*
459575363aSChuck Lever  * Basic NFSv3 data types (RFC 1813 Sections 2.5 and 2.6)
461da177e4SLinus Torvalds  */
479575363aSChuck Lever 
483ee6f61cSAdrian Bunk static __be32 *
encode_nfstime3(__be32 * p,const struct timespec64 * time)492c42f804SChuck Lever encode_nfstime3(__be32 *p, const struct timespec64 *time)
502c42f804SChuck Lever {
512c42f804SChuck Lever 	*p++ = cpu_to_be32((u32)time->tv_sec);
522c42f804SChuck Lever 	*p++ = cpu_to_be32(time->tv_nsec);
532c42f804SChuck Lever 
542c42f804SChuck Lever 	return p;
552c42f804SChuck Lever }
562c42f804SChuck Lever 
579cde9360SChuck Lever static bool
svcxdr_decode_nfstime3(struct xdr_stream * xdr,struct timespec64 * timep)589cde9360SChuck Lever svcxdr_decode_nfstime3(struct xdr_stream *xdr, struct timespec64 *timep)
591da177e4SLinus Torvalds {
609cde9360SChuck Lever 	__be32 *p;
619cde9360SChuck Lever 
629cde9360SChuck Lever 	p = xdr_inline_decode(xdr, XDR_UNIT * 2);
639cde9360SChuck Lever 	if (!p)
649cde9360SChuck Lever 		return false;
659cde9360SChuck Lever 	timep->tv_sec = be32_to_cpup(p++);
669cde9360SChuck Lever 	timep->tv_nsec = be32_to_cpup(p);
679cde9360SChuck Lever 
689cde9360SChuck Lever 	return true;
691da177e4SLinus Torvalds }
701da177e4SLinus Torvalds 
7105027eafSChuck Lever /**
7205027eafSChuck Lever  * svcxdr_decode_nfs_fh3 - Decode an NFSv3 file handle
7305027eafSChuck Lever  * @xdr: XDR stream positioned at an undecoded NFSv3 FH
7405027eafSChuck Lever  * @fhp: OUT: filled-in server file handle
7505027eafSChuck Lever  *
7605027eafSChuck Lever  * Return values:
7705027eafSChuck Lever  *  %false: The encoded file handle was not valid
7805027eafSChuck Lever  *  %true: @fhp has been initialized
7905027eafSChuck Lever  */
8005027eafSChuck Lever bool
svcxdr_decode_nfs_fh3(struct xdr_stream * xdr,struct svc_fh * fhp)819575363aSChuck Lever svcxdr_decode_nfs_fh3(struct xdr_stream *xdr, struct svc_fh *fhp)
829575363aSChuck Lever {
839575363aSChuck Lever 	__be32 *p;
849575363aSChuck Lever 	u32 size;
859575363aSChuck Lever 
869575363aSChuck Lever 	if (xdr_stream_decode_u32(xdr, &size) < 0)
879575363aSChuck Lever 		return false;
889575363aSChuck Lever 	if (size == 0 || size > NFS3_FHSIZE)
899575363aSChuck Lever 		return false;
909575363aSChuck Lever 	p = xdr_inline_decode(xdr, size);
919575363aSChuck Lever 	if (!p)
929575363aSChuck Lever 		return false;
939575363aSChuck Lever 	fh_init(fhp, NFS3_FHSIZE);
949575363aSChuck Lever 	fhp->fh_handle.fh_size = size;
95d8b26071SNeilBrown 	memcpy(&fhp->fh_handle.fh_raw, p, size);
969575363aSChuck Lever 
979575363aSChuck Lever 	return true;
989575363aSChuck Lever }
999575363aSChuck Lever 
10020798dfeSChuck Lever /**
10120798dfeSChuck Lever  * svcxdr_encode_nfsstat3 - Encode an NFSv3 status code
10220798dfeSChuck Lever  * @xdr: XDR stream
10320798dfeSChuck Lever  * @status: status value to encode
10420798dfeSChuck Lever  *
10520798dfeSChuck Lever  * Return values:
10620798dfeSChuck Lever  *   %false: Send buffer space was exhausted
10720798dfeSChuck Lever  *   %true: Success
10820798dfeSChuck Lever  */
10920798dfeSChuck Lever bool
svcxdr_encode_nfsstat3(struct xdr_stream * xdr,__be32 status)1102c42f804SChuck Lever svcxdr_encode_nfsstat3(struct xdr_stream *xdr, __be32 status)
1112c42f804SChuck Lever {
1122c42f804SChuck Lever 	__be32 *p;
1132c42f804SChuck Lever 
1142c42f804SChuck Lever 	p = xdr_reserve_space(xdr, sizeof(status));
1152c42f804SChuck Lever 	if (!p)
1162c42f804SChuck Lever 		return false;
1172c42f804SChuck Lever 	*p = status;
1182c42f804SChuck Lever 
1192c42f804SChuck Lever 	return true;
1202c42f804SChuck Lever }
1212c42f804SChuck Lever 
1225cf35335SChuck Lever static bool
svcxdr_encode_nfs_fh3(struct xdr_stream * xdr,const struct svc_fh * fhp)1235cf35335SChuck Lever svcxdr_encode_nfs_fh3(struct xdr_stream *xdr, const struct svc_fh *fhp)
1245cf35335SChuck Lever {
1255cf35335SChuck Lever 	u32 size = fhp->fh_handle.fh_size;
1265cf35335SChuck Lever 	__be32 *p;
1275cf35335SChuck Lever 
1285cf35335SChuck Lever 	p = xdr_reserve_space(xdr, XDR_UNIT + size);
1295cf35335SChuck Lever 	if (!p)
1305cf35335SChuck Lever 		return false;
1315cf35335SChuck Lever 	*p++ = cpu_to_be32(size);
1325cf35335SChuck Lever 	if (size)
1335cf35335SChuck Lever 		p[XDR_QUADLEN(size) - 1] = 0;
134d8b26071SNeilBrown 	memcpy(p, &fhp->fh_handle.fh_raw, size);
1355cf35335SChuck Lever 
1365cf35335SChuck Lever 	return true;
1375cf35335SChuck Lever }
1385cf35335SChuck Lever 
13978315b36SChuck Lever static bool
svcxdr_encode_post_op_fh3(struct xdr_stream * xdr,const struct svc_fh * fhp)14078315b36SChuck Lever svcxdr_encode_post_op_fh3(struct xdr_stream *xdr, const struct svc_fh *fhp)
14178315b36SChuck Lever {
14278315b36SChuck Lever 	if (xdr_stream_encode_item_present(xdr) < 0)
14378315b36SChuck Lever 		return false;
14478315b36SChuck Lever 	if (!svcxdr_encode_nfs_fh3(xdr, fhp))
14578315b36SChuck Lever 		return false;
14678315b36SChuck Lever 
14778315b36SChuck Lever 	return true;
14878315b36SChuck Lever }
14978315b36SChuck Lever 
15054d1d43dSChuck Lever static bool
svcxdr_encode_cookieverf3(struct xdr_stream * xdr,const __be32 * verf)151e4ccfe30SChuck Lever svcxdr_encode_cookieverf3(struct xdr_stream *xdr, const __be32 *verf)
152e4ccfe30SChuck Lever {
153e4ccfe30SChuck Lever 	__be32 *p;
154e4ccfe30SChuck Lever 
155e4ccfe30SChuck Lever 	p = xdr_reserve_space(xdr, NFS3_COOKIEVERFSIZE);
156e4ccfe30SChuck Lever 	if (!p)
157e4ccfe30SChuck Lever 		return false;
158e4ccfe30SChuck Lever 	memcpy(p, verf, NFS3_COOKIEVERFSIZE);
159e4ccfe30SChuck Lever 
160e4ccfe30SChuck Lever 	return true;
161e4ccfe30SChuck Lever }
162e4ccfe30SChuck Lever 
163e4ccfe30SChuck Lever static bool
svcxdr_encode_writeverf3(struct xdr_stream * xdr,const __be32 * verf)164ecb7a085SChuck Lever svcxdr_encode_writeverf3(struct xdr_stream *xdr, const __be32 *verf)
165ecb7a085SChuck Lever {
166ecb7a085SChuck Lever 	__be32 *p;
167ecb7a085SChuck Lever 
168ecb7a085SChuck Lever 	p = xdr_reserve_space(xdr, NFS3_WRITEVERFSIZE);
169ecb7a085SChuck Lever 	if (!p)
170ecb7a085SChuck Lever 		return false;
171ecb7a085SChuck Lever 	memcpy(p, verf, NFS3_WRITEVERFSIZE);
172ecb7a085SChuck Lever 
173ecb7a085SChuck Lever 	return true;
174ecb7a085SChuck Lever }
175ecb7a085SChuck Lever 
176ecb7a085SChuck Lever static bool
svcxdr_decode_filename3(struct xdr_stream * xdr,char ** name,unsigned int * len)17754d1d43dSChuck Lever svcxdr_decode_filename3(struct xdr_stream *xdr, char **name, unsigned int *len)
17854d1d43dSChuck Lever {
17954d1d43dSChuck Lever 	u32 size, i;
18054d1d43dSChuck Lever 	__be32 *p;
18154d1d43dSChuck Lever 	char *c;
18254d1d43dSChuck Lever 
18354d1d43dSChuck Lever 	if (xdr_stream_decode_u32(xdr, &size) < 0)
18454d1d43dSChuck Lever 		return false;
18554d1d43dSChuck Lever 	if (size == 0 || size > NFS3_MAXNAMLEN)
18654d1d43dSChuck Lever 		return false;
18754d1d43dSChuck Lever 	p = xdr_inline_decode(xdr, size);
18854d1d43dSChuck Lever 	if (!p)
18954d1d43dSChuck Lever 		return false;
19054d1d43dSChuck Lever 
19154d1d43dSChuck Lever 	*len = size;
19254d1d43dSChuck Lever 	*name = (char *)p;
19354d1d43dSChuck Lever 	for (i = 0, c = *name; i < size; i++, c++) {
19454d1d43dSChuck Lever 		if (*c == '\0' || *c == '/')
19554d1d43dSChuck Lever 			return false;
19654d1d43dSChuck Lever 	}
19754d1d43dSChuck Lever 
19854d1d43dSChuck Lever 	return true;
19954d1d43dSChuck Lever }
20054d1d43dSChuck Lever 
20154d1d43dSChuck Lever static bool
svcxdr_decode_diropargs3(struct xdr_stream * xdr,struct svc_fh * fhp,char ** name,unsigned int * len)20254d1d43dSChuck Lever svcxdr_decode_diropargs3(struct xdr_stream *xdr, struct svc_fh *fhp,
20354d1d43dSChuck Lever 			 char **name, unsigned int *len)
20454d1d43dSChuck Lever {
20554d1d43dSChuck Lever 	return svcxdr_decode_nfs_fh3(xdr, fhp) &&
20654d1d43dSChuck Lever 		svcxdr_decode_filename3(xdr, name, len);
20754d1d43dSChuck Lever }
20854d1d43dSChuck Lever 
2099cde9360SChuck Lever static bool
svcxdr_decode_sattr3(struct svc_rqst * rqstp,struct xdr_stream * xdr,struct iattr * iap)2109cde9360SChuck Lever svcxdr_decode_sattr3(struct svc_rqst *rqstp, struct xdr_stream *xdr,
2119cde9360SChuck Lever 		     struct iattr *iap)
2129cde9360SChuck Lever {
2139cde9360SChuck Lever 	u32 set_it;
2149cde9360SChuck Lever 
2159cde9360SChuck Lever 	iap->ia_valid = 0;
2169cde9360SChuck Lever 
2179cde9360SChuck Lever 	if (xdr_stream_decode_bool(xdr, &set_it) < 0)
2189cde9360SChuck Lever 		return false;
2199cde9360SChuck Lever 	if (set_it) {
2209cde9360SChuck Lever 		u32 mode;
2219cde9360SChuck Lever 
2229cde9360SChuck Lever 		if (xdr_stream_decode_u32(xdr, &mode) < 0)
2239cde9360SChuck Lever 			return false;
2249cde9360SChuck Lever 		iap->ia_valid |= ATTR_MODE;
2259cde9360SChuck Lever 		iap->ia_mode = mode;
2269cde9360SChuck Lever 	}
2279cde9360SChuck Lever 	if (xdr_stream_decode_bool(xdr, &set_it) < 0)
2289cde9360SChuck Lever 		return false;
2299cde9360SChuck Lever 	if (set_it) {
2309cde9360SChuck Lever 		u32 uid;
2319cde9360SChuck Lever 
2329cde9360SChuck Lever 		if (xdr_stream_decode_u32(xdr, &uid) < 0)
2339cde9360SChuck Lever 			return false;
2349cde9360SChuck Lever 		iap->ia_uid = make_kuid(nfsd_user_namespace(rqstp), uid);
2359cde9360SChuck Lever 		if (uid_valid(iap->ia_uid))
2369cde9360SChuck Lever 			iap->ia_valid |= ATTR_UID;
2379cde9360SChuck Lever 	}
2389cde9360SChuck Lever 	if (xdr_stream_decode_bool(xdr, &set_it) < 0)
2399cde9360SChuck Lever 		return false;
2409cde9360SChuck Lever 	if (set_it) {
2419cde9360SChuck Lever 		u32 gid;
2429cde9360SChuck Lever 
2439cde9360SChuck Lever 		if (xdr_stream_decode_u32(xdr, &gid) < 0)
2449cde9360SChuck Lever 			return false;
2459cde9360SChuck Lever 		iap->ia_gid = make_kgid(nfsd_user_namespace(rqstp), gid);
2469cde9360SChuck Lever 		if (gid_valid(iap->ia_gid))
2479cde9360SChuck Lever 			iap->ia_valid |= ATTR_GID;
2489cde9360SChuck Lever 	}
2499cde9360SChuck Lever 	if (xdr_stream_decode_bool(xdr, &set_it) < 0)
2509cde9360SChuck Lever 		return false;
2519cde9360SChuck Lever 	if (set_it) {
2529cde9360SChuck Lever 		u64 newsize;
2539cde9360SChuck Lever 
2549cde9360SChuck Lever 		if (xdr_stream_decode_u64(xdr, &newsize) < 0)
2559cde9360SChuck Lever 			return false;
2569cde9360SChuck Lever 		iap->ia_valid |= ATTR_SIZE;
257a648fdebSChuck Lever 		iap->ia_size = newsize;
2589cde9360SChuck Lever 	}
2599cde9360SChuck Lever 	if (xdr_stream_decode_u32(xdr, &set_it) < 0)
2609cde9360SChuck Lever 		return false;
2619cde9360SChuck Lever 	switch (set_it) {
2629cde9360SChuck Lever 	case DONT_CHANGE:
2639cde9360SChuck Lever 		break;
2649cde9360SChuck Lever 	case SET_TO_SERVER_TIME:
2659cde9360SChuck Lever 		iap->ia_valid |= ATTR_ATIME;
2669cde9360SChuck Lever 		break;
2679cde9360SChuck Lever 	case SET_TO_CLIENT_TIME:
2689cde9360SChuck Lever 		if (!svcxdr_decode_nfstime3(xdr, &iap->ia_atime))
2699cde9360SChuck Lever 			return false;
2709cde9360SChuck Lever 		iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
2719cde9360SChuck Lever 		break;
2729cde9360SChuck Lever 	default:
2739cde9360SChuck Lever 		return false;
2749cde9360SChuck Lever 	}
2759cde9360SChuck Lever 	if (xdr_stream_decode_u32(xdr, &set_it) < 0)
2769cde9360SChuck Lever 		return false;
2779cde9360SChuck Lever 	switch (set_it) {
2789cde9360SChuck Lever 	case DONT_CHANGE:
2799cde9360SChuck Lever 		break;
2809cde9360SChuck Lever 	case SET_TO_SERVER_TIME:
2819cde9360SChuck Lever 		iap->ia_valid |= ATTR_MTIME;
2829cde9360SChuck Lever 		break;
2839cde9360SChuck Lever 	case SET_TO_CLIENT_TIME:
2849cde9360SChuck Lever 		if (!svcxdr_decode_nfstime3(xdr, &iap->ia_mtime))
2859cde9360SChuck Lever 			return false;
2869cde9360SChuck Lever 		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
2879cde9360SChuck Lever 		break;
2889cde9360SChuck Lever 	default:
2899cde9360SChuck Lever 		return false;
2909cde9360SChuck Lever 	}
2919cde9360SChuck Lever 
2929cde9360SChuck Lever 	return true;
2939cde9360SChuck Lever }
2949cde9360SChuck Lever 
2959cde9360SChuck Lever static bool
svcxdr_decode_sattrguard3(struct xdr_stream * xdr,struct nfsd3_sattrargs * args)2969cde9360SChuck Lever svcxdr_decode_sattrguard3(struct xdr_stream *xdr, struct nfsd3_sattrargs *args)
2979cde9360SChuck Lever {
2989cde9360SChuck Lever 	__be32 *p;
2999cde9360SChuck Lever 	u32 check;
3009cde9360SChuck Lever 
3019cde9360SChuck Lever 	if (xdr_stream_decode_bool(xdr, &check) < 0)
3029cde9360SChuck Lever 		return false;
3039cde9360SChuck Lever 	if (check) {
3049cde9360SChuck Lever 		p = xdr_inline_decode(xdr, XDR_UNIT * 2);
3059cde9360SChuck Lever 		if (!p)
3069cde9360SChuck Lever 			return false;
3079cde9360SChuck Lever 		args->check_guard = 1;
3089cde9360SChuck Lever 		args->guardtime = be32_to_cpup(p);
3099cde9360SChuck Lever 	} else
3109cde9360SChuck Lever 		args->check_guard = 0;
3119cde9360SChuck Lever 
3129cde9360SChuck Lever 	return true;
3139cde9360SChuck Lever }
3149cde9360SChuck Lever 
315f8a38e2dSChuck Lever static bool
svcxdr_decode_specdata3(struct xdr_stream * xdr,struct nfsd3_mknodargs * args)316f8a38e2dSChuck Lever svcxdr_decode_specdata3(struct xdr_stream *xdr, struct nfsd3_mknodargs *args)
3171da177e4SLinus Torvalds {
318f8a38e2dSChuck Lever 	__be32 *p;
3191da177e4SLinus Torvalds 
320f8a38e2dSChuck Lever 	p = xdr_inline_decode(xdr, XDR_UNIT * 2);
321f8a38e2dSChuck Lever 	if (!p)
322f8a38e2dSChuck Lever 		return false;
323f8a38e2dSChuck Lever 	args->major = be32_to_cpup(p++);
324f8a38e2dSChuck Lever 	args->minor = be32_to_cpup(p);
3251da177e4SLinus Torvalds 
326f8a38e2dSChuck Lever 	return true;
3271da177e4SLinus Torvalds }
3281da177e4SLinus Torvalds 
329f8a38e2dSChuck Lever static bool
svcxdr_decode_devicedata3(struct svc_rqst * rqstp,struct xdr_stream * xdr,struct nfsd3_mknodargs * args)330f8a38e2dSChuck Lever svcxdr_decode_devicedata3(struct svc_rqst *rqstp, struct xdr_stream *xdr,
331f8a38e2dSChuck Lever 			  struct nfsd3_mknodargs *args)
332f8a38e2dSChuck Lever {
333f8a38e2dSChuck Lever 	return svcxdr_decode_sattr3(rqstp, xdr, &args->attrs) &&
334f8a38e2dSChuck Lever 		svcxdr_decode_specdata3(xdr, args);
3351da177e4SLinus Torvalds }
3361da177e4SLinus Torvalds 
3372c42f804SChuck Lever static bool
svcxdr_encode_fattr3(struct svc_rqst * rqstp,struct xdr_stream * xdr,const struct svc_fh * fhp,const struct kstat * stat)3382c42f804SChuck Lever svcxdr_encode_fattr3(struct svc_rqst *rqstp, struct xdr_stream *xdr,
3392c42f804SChuck Lever 		     const struct svc_fh *fhp, const struct kstat *stat)
3402c42f804SChuck Lever {
3412c42f804SChuck Lever 	struct user_namespace *userns = nfsd_user_namespace(rqstp);
3422c42f804SChuck Lever 	__be32 *p;
3432c42f804SChuck Lever 	u64 fsid;
3442c42f804SChuck Lever 
3452c42f804SChuck Lever 	p = xdr_reserve_space(xdr, XDR_UNIT * 21);
3462c42f804SChuck Lever 	if (!p)
3472c42f804SChuck Lever 		return false;
3482c42f804SChuck Lever 
3492c42f804SChuck Lever 	*p++ = cpu_to_be32(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]);
3502c42f804SChuck Lever 	*p++ = cpu_to_be32((u32)(stat->mode & S_IALLUGO));
3512c42f804SChuck Lever 	*p++ = cpu_to_be32((u32)stat->nlink);
3522c42f804SChuck Lever 	*p++ = cpu_to_be32((u32)from_kuid_munged(userns, stat->uid));
3532c42f804SChuck Lever 	*p++ = cpu_to_be32((u32)from_kgid_munged(userns, stat->gid));
3542c42f804SChuck Lever 	if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN)
3552c42f804SChuck Lever 		p = xdr_encode_hyper(p, (u64)NFS3_MAXPATHLEN);
3562c42f804SChuck Lever 	else
3572c42f804SChuck Lever 		p = xdr_encode_hyper(p, (u64)stat->size);
3582c42f804SChuck Lever 
3592c42f804SChuck Lever 	/* used */
3602c42f804SChuck Lever 	p = xdr_encode_hyper(p, ((u64)stat->blocks) << 9);
3612c42f804SChuck Lever 
3622c42f804SChuck Lever 	/* rdev */
3632c42f804SChuck Lever 	*p++ = cpu_to_be32((u32)MAJOR(stat->rdev));
3642c42f804SChuck Lever 	*p++ = cpu_to_be32((u32)MINOR(stat->rdev));
3652c42f804SChuck Lever 
3662c42f804SChuck Lever 	switch(fsid_source(fhp)) {
3672c42f804SChuck Lever 	case FSIDSOURCE_FSID:
3682c42f804SChuck Lever 		fsid = (u64)fhp->fh_export->ex_fsid;
3692c42f804SChuck Lever 		break;
3702c42f804SChuck Lever 	case FSIDSOURCE_UUID:
3712c42f804SChuck Lever 		fsid = ((u64 *)fhp->fh_export->ex_uuid)[0];
3722c42f804SChuck Lever 		fsid ^= ((u64 *)fhp->fh_export->ex_uuid)[1];
3732c42f804SChuck Lever 		break;
3742c42f804SChuck Lever 	default:
3752c42f804SChuck Lever 		fsid = (u64)huge_encode_dev(fhp->fh_dentry->d_sb->s_dev);
3762c42f804SChuck Lever 	}
3772c42f804SChuck Lever 	p = xdr_encode_hyper(p, fsid);
3782c42f804SChuck Lever 
3792c42f804SChuck Lever 	/* fileid */
3802c42f804SChuck Lever 	p = xdr_encode_hyper(p, stat->ino);
3812c42f804SChuck Lever 
3822c42f804SChuck Lever 	p = encode_nfstime3(p, &stat->atime);
3832c42f804SChuck Lever 	p = encode_nfstime3(p, &stat->mtime);
3842c42f804SChuck Lever 	encode_nfstime3(p, &stat->ctime);
3852c42f804SChuck Lever 
3862c42f804SChuck Lever 	return true;
3872c42f804SChuck Lever }
3882c42f804SChuck Lever 
389907c3822SChuck Lever static bool
svcxdr_encode_wcc_attr(struct xdr_stream * xdr,const struct svc_fh * fhp)39070f8e839SChuck Lever svcxdr_encode_wcc_attr(struct xdr_stream *xdr, const struct svc_fh *fhp)
39170f8e839SChuck Lever {
39270f8e839SChuck Lever 	__be32 *p;
39370f8e839SChuck Lever 
39470f8e839SChuck Lever 	p = xdr_reserve_space(xdr, XDR_UNIT * 6);
39570f8e839SChuck Lever 	if (!p)
39670f8e839SChuck Lever 		return false;
39770f8e839SChuck Lever 	p = xdr_encode_hyper(p, (u64)fhp->fh_pre_size);
39870f8e839SChuck Lever 	p = encode_nfstime3(p, &fhp->fh_pre_mtime);
39970f8e839SChuck Lever 	encode_nfstime3(p, &fhp->fh_pre_ctime);
40070f8e839SChuck Lever 
40170f8e839SChuck Lever 	return true;
40270f8e839SChuck Lever }
40370f8e839SChuck Lever 
40470f8e839SChuck Lever static bool
svcxdr_encode_pre_op_attr(struct xdr_stream * xdr,const struct svc_fh * fhp)40570f8e839SChuck Lever svcxdr_encode_pre_op_attr(struct xdr_stream *xdr, const struct svc_fh *fhp)
40670f8e839SChuck Lever {
40770f8e839SChuck Lever 	if (!fhp->fh_pre_saved) {
40870f8e839SChuck Lever 		if (xdr_stream_encode_item_absent(xdr) < 0)
40970f8e839SChuck Lever 			return false;
41070f8e839SChuck Lever 		return true;
41170f8e839SChuck Lever 	}
41270f8e839SChuck Lever 
41370f8e839SChuck Lever 	if (xdr_stream_encode_item_present(xdr) < 0)
41470f8e839SChuck Lever 		return false;
41570f8e839SChuck Lever 	return svcxdr_encode_wcc_attr(xdr, fhp);
41670f8e839SChuck Lever }
41770f8e839SChuck Lever 
41820798dfeSChuck Lever /**
41920798dfeSChuck Lever  * svcxdr_encode_post_op_attr - Encode NFSv3 post-op attributes
42020798dfeSChuck Lever  * @rqstp: Context of a completed RPC transaction
42120798dfeSChuck Lever  * @xdr: XDR stream
42220798dfeSChuck Lever  * @fhp: File handle to encode
42320798dfeSChuck Lever  *
42420798dfeSChuck Lever  * Return values:
42520798dfeSChuck Lever  *   %false: Send buffer space was exhausted
42620798dfeSChuck Lever  *   %true: Success
42720798dfeSChuck Lever  */
42820798dfeSChuck Lever bool
svcxdr_encode_post_op_attr(struct svc_rqst * rqstp,struct xdr_stream * xdr,const struct svc_fh * fhp)429907c3822SChuck Lever svcxdr_encode_post_op_attr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
430907c3822SChuck Lever 			   const struct svc_fh *fhp)
431907c3822SChuck Lever {
432907c3822SChuck Lever 	struct dentry *dentry = fhp->fh_dentry;
433907c3822SChuck Lever 	struct kstat stat;
434907c3822SChuck Lever 
435907c3822SChuck Lever 	/*
436907c3822SChuck Lever 	 * The inode may be NULL if the call failed because of a
437907c3822SChuck Lever 	 * stale file handle. In this case, no attributes are
438907c3822SChuck Lever 	 * returned.
439907c3822SChuck Lever 	 */
440907c3822SChuck Lever 	if (fhp->fh_no_wcc || !dentry || !d_really_is_positive(dentry))
441907c3822SChuck Lever 		goto no_post_op_attrs;
442907c3822SChuck Lever 	if (fh_getattr(fhp, &stat) != nfs_ok)
443907c3822SChuck Lever 		goto no_post_op_attrs;
444907c3822SChuck Lever 
445907c3822SChuck Lever 	if (xdr_stream_encode_item_present(xdr) < 0)
446907c3822SChuck Lever 		return false;
447907c3822SChuck Lever 	lease_get_mtime(d_inode(dentry), &stat.mtime);
448907c3822SChuck Lever 	if (!svcxdr_encode_fattr3(rqstp, xdr, fhp, &stat))
449907c3822SChuck Lever 		return false;
450907c3822SChuck Lever 
451907c3822SChuck Lever 	return true;
452907c3822SChuck Lever 
453907c3822SChuck Lever no_post_op_attrs:
454907c3822SChuck Lever 	return xdr_stream_encode_item_absent(xdr) > 0;
455907c3822SChuck Lever }
456907c3822SChuck Lever 
4571da177e4SLinus Torvalds /*
45870f8e839SChuck Lever  * Encode weak cache consistency data
45970f8e839SChuck Lever  */
46070f8e839SChuck Lever static bool
svcxdr_encode_wcc_data(struct svc_rqst * rqstp,struct xdr_stream * xdr,const struct svc_fh * fhp)46170f8e839SChuck Lever svcxdr_encode_wcc_data(struct svc_rqst *rqstp, struct xdr_stream *xdr,
46270f8e839SChuck Lever 		       const struct svc_fh *fhp)
46370f8e839SChuck Lever {
46470f8e839SChuck Lever 	struct dentry *dentry = fhp->fh_dentry;
46570f8e839SChuck Lever 
46670f8e839SChuck Lever 	if (!dentry || !d_really_is_positive(dentry) || !fhp->fh_post_saved)
46770f8e839SChuck Lever 		goto neither;
46870f8e839SChuck Lever 
46970f8e839SChuck Lever 	/* before */
47070f8e839SChuck Lever 	if (!svcxdr_encode_pre_op_attr(xdr, fhp))
47170f8e839SChuck Lever 		return false;
47270f8e839SChuck Lever 
47370f8e839SChuck Lever 	/* after */
47470f8e839SChuck Lever 	if (xdr_stream_encode_item_present(xdr) < 0)
47570f8e839SChuck Lever 		return false;
47670f8e839SChuck Lever 	if (!svcxdr_encode_fattr3(rqstp, xdr, fhp, &fhp->fh_post_attr))
47770f8e839SChuck Lever 		return false;
47870f8e839SChuck Lever 
47970f8e839SChuck Lever 	return true;
48070f8e839SChuck Lever 
48170f8e839SChuck Lever neither:
48270f8e839SChuck Lever 	if (xdr_stream_encode_item_absent(xdr) < 0)
48370f8e839SChuck Lever 		return false;
48470f8e839SChuck Lever 	if (!svcxdr_encode_post_op_attr(rqstp, xdr, fhp))
48570f8e839SChuck Lever 		return false;
48670f8e839SChuck Lever 
48770f8e839SChuck Lever 	return true;
48870f8e839SChuck Lever }
48970f8e839SChuck Lever 
49040ee5dc6SPeter Staubach /*
4911da177e4SLinus Torvalds  * XDR decode functions
4921da177e4SLinus Torvalds  */
493dcc46991SChuck Lever 
494c44b31c2SChuck Lever bool
nfs3svc_decode_fhandleargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)49516c66364SChuck Lever nfs3svc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
4961da177e4SLinus Torvalds {
497026fec7eSChristoph Hellwig 	struct nfsd_fhandle *args = rqstp->rq_argp;
498026fec7eSChristoph Hellwig 
4999575363aSChuck Lever 	return svcxdr_decode_nfs_fh3(xdr, &args->fh);
5001da177e4SLinus Torvalds }
5011da177e4SLinus Torvalds 
502c44b31c2SChuck Lever bool
nfs3svc_decode_sattrargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)50316c66364SChuck Lever nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5041da177e4SLinus Torvalds {
505026fec7eSChristoph Hellwig 	struct nfsd3_sattrargs *args = rqstp->rq_argp;
506026fec7eSChristoph Hellwig 
5079cde9360SChuck Lever 	return svcxdr_decode_nfs_fh3(xdr, &args->fh) &&
5089cde9360SChuck Lever 		svcxdr_decode_sattr3(rqstp, xdr, &args->attrs) &&
5099cde9360SChuck Lever 		svcxdr_decode_sattrguard3(xdr, args);
5101da177e4SLinus Torvalds }
5111da177e4SLinus Torvalds 
512c44b31c2SChuck Lever bool
nfs3svc_decode_diropargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)51316c66364SChuck Lever nfs3svc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5141da177e4SLinus Torvalds {
515026fec7eSChristoph Hellwig 	struct nfsd3_diropargs *args = rqstp->rq_argp;
516026fec7eSChristoph Hellwig 
51754d1d43dSChuck Lever 	return svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len);
5181da177e4SLinus Torvalds }
5191da177e4SLinus Torvalds 
520c44b31c2SChuck Lever bool
nfs3svc_decode_accessargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)52116c66364SChuck Lever nfs3svc_decode_accessargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5221da177e4SLinus Torvalds {
523026fec7eSChristoph Hellwig 	struct nfsd3_accessargs *args = rqstp->rq_argp;
524026fec7eSChristoph Hellwig 
5253b921a2bSChuck Lever 	if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
526c44b31c2SChuck Lever 		return false;
5273b921a2bSChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->access) < 0)
528c44b31c2SChuck Lever 		return false;
5291da177e4SLinus Torvalds 
530c44b31c2SChuck Lever 	return true;
5311da177e4SLinus Torvalds }
5321da177e4SLinus Torvalds 
533c44b31c2SChuck Lever bool
nfs3svc_decode_readargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)53416c66364SChuck Lever nfs3svc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5351da177e4SLinus Torvalds {
536026fec7eSChristoph Hellwig 	struct nfsd3_readargs *args = rqstp->rq_argp;
5371da177e4SLinus Torvalds 
538be63bd2aSChuck Lever 	if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
539c44b31c2SChuck Lever 		return false;
540be63bd2aSChuck Lever 	if (xdr_stream_decode_u64(xdr, &args->offset) < 0)
541c44b31c2SChuck Lever 		return false;
542be63bd2aSChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->count) < 0)
543c44b31c2SChuck Lever 		return false;
5449512a16bSJ. Bruce Fields 
545c44b31c2SChuck Lever 	return true;
5461da177e4SLinus Torvalds }
5471da177e4SLinus Torvalds 
548c44b31c2SChuck Lever bool
nfs3svc_decode_writeargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)54916c66364SChuck Lever nfs3svc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5501da177e4SLinus Torvalds {
551026fec7eSChristoph Hellwig 	struct nfsd3_writeargs *args = rqstp->rq_argp;
5527adae489SGreg Banks 	u32 max_blocksize = svc_max_payload(rqstp);
5531da177e4SLinus Torvalds 
554c43b2f22SChuck Lever 	if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
555c44b31c2SChuck Lever 		return false;
556c43b2f22SChuck Lever 	if (xdr_stream_decode_u64(xdr, &args->offset) < 0)
557c44b31c2SChuck Lever 		return false;
558c43b2f22SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->count) < 0)
559c44b31c2SChuck Lever 		return false;
560c43b2f22SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->stable) < 0)
561c44b31c2SChuck Lever 		return false;
5621da177e4SLinus Torvalds 
563c43b2f22SChuck Lever 	/* opaque data */
564c43b2f22SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->len) < 0)
565c44b31c2SChuck Lever 		return false;
566c43b2f22SChuck Lever 
567c43b2f22SChuck Lever 	/* request sanity */
568f34b9568SPeter Staubach 	if (args->count != args->len)
569c44b31c2SChuck Lever 		return false;
570f34b9568SPeter Staubach 	if (args->count > max_blocksize) {
571f34b9568SPeter Staubach 		args->count = max_blocksize;
572c43b2f22SChuck Lever 		args->len = max_blocksize;
573f34b9568SPeter Staubach 	}
574c43b2f22SChuck Lever 
575d4da5baaSChuck Lever 	return xdr_stream_subsegment(xdr, &args->payload, args->count);
5761da177e4SLinus Torvalds }
5771da177e4SLinus Torvalds 
578c44b31c2SChuck Lever bool
nfs3svc_decode_createargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)57916c66364SChuck Lever nfs3svc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5801da177e4SLinus Torvalds {
581026fec7eSChristoph Hellwig 	struct nfsd3_createargs *args = rqstp->rq_argp;
582026fec7eSChristoph Hellwig 
5836b3a1196SChuck Lever 	if (!svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len))
584c44b31c2SChuck Lever 		return false;
5856b3a1196SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->createmode) < 0)
586c44b31c2SChuck Lever 		return false;
5876b3a1196SChuck Lever 	switch (args->createmode) {
5881da177e4SLinus Torvalds 	case NFS3_CREATE_UNCHECKED:
5891da177e4SLinus Torvalds 	case NFS3_CREATE_GUARDED:
5906b3a1196SChuck Lever 		return svcxdr_decode_sattr3(rqstp, xdr, &args->attrs);
5911da177e4SLinus Torvalds 	case NFS3_CREATE_EXCLUSIVE:
5926b3a1196SChuck Lever 		args->verf = xdr_inline_decode(xdr, NFS3_CREATEVERFSIZE);
5936b3a1196SChuck Lever 		if (!args->verf)
594c44b31c2SChuck Lever 			return false;
5951da177e4SLinus Torvalds 		break;
5961da177e4SLinus Torvalds 	default:
597c44b31c2SChuck Lever 		return false;
5981da177e4SLinus Torvalds 	}
599c44b31c2SChuck Lever 	return true;
6001da177e4SLinus Torvalds }
601026fec7eSChristoph Hellwig 
602c44b31c2SChuck Lever bool
nfs3svc_decode_mkdirargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)60316c66364SChuck Lever nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
6041da177e4SLinus Torvalds {
605026fec7eSChristoph Hellwig 	struct nfsd3_createargs *args = rqstp->rq_argp;
606026fec7eSChristoph Hellwig 
60783374c27SChuck Lever 	return svcxdr_decode_diropargs3(xdr, &args->fh,
60883374c27SChuck Lever 					&args->name, &args->len) &&
60983374c27SChuck Lever 		svcxdr_decode_sattr3(rqstp, xdr, &args->attrs);
6101da177e4SLinus Torvalds }
6111da177e4SLinus Torvalds 
612c44b31c2SChuck Lever bool
nfs3svc_decode_symlinkargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)61316c66364SChuck Lever nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
6141da177e4SLinus Torvalds {
615026fec7eSChristoph Hellwig 	struct nfsd3_symlinkargs *args = rqstp->rq_argp;
616da392016SChuck Lever 	struct kvec *head = rqstp->rq_arg.head;
6171da177e4SLinus Torvalds 
618da392016SChuck Lever 	if (!svcxdr_decode_diropargs3(xdr, &args->ffh, &args->fname, &args->flen))
619c44b31c2SChuck Lever 		return false;
620da392016SChuck Lever 	if (!svcxdr_decode_sattr3(rqstp, xdr, &args->attrs))
621c44b31c2SChuck Lever 		return false;
622da392016SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->tlen) < 0)
623c44b31c2SChuck Lever 		return false;
624da392016SChuck Lever 
625c3d2a04fSChuck Lever 	/* symlink_data */
626da392016SChuck Lever 	args->first.iov_len = head->iov_len - xdr_stream_pos(xdr);
627c3d2a04fSChuck Lever 	args->first.iov_base = xdr_inline_decode(xdr, args->tlen);
628c3d2a04fSChuck Lever 	return args->first.iov_base != NULL;
6291da177e4SLinus Torvalds }
6301da177e4SLinus Torvalds 
631c44b31c2SChuck Lever bool
nfs3svc_decode_mknodargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)63216c66364SChuck Lever nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
6331da177e4SLinus Torvalds {
634026fec7eSChristoph Hellwig 	struct nfsd3_mknodargs *args = rqstp->rq_argp;
635026fec7eSChristoph Hellwig 
636f8a38e2dSChuck Lever 	if (!svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len))
637c44b31c2SChuck Lever 		return false;
638f8a38e2dSChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->ftype) < 0)
639c44b31c2SChuck Lever 		return false;
640f8a38e2dSChuck Lever 	switch (args->ftype) {
641f8a38e2dSChuck Lever 	case NF3CHR:
642f8a38e2dSChuck Lever 	case NF3BLK:
643f8a38e2dSChuck Lever 		return svcxdr_decode_devicedata3(rqstp, xdr, args);
644f8a38e2dSChuck Lever 	case NF3SOCK:
645f8a38e2dSChuck Lever 	case NF3FIFO:
646f8a38e2dSChuck Lever 		return svcxdr_decode_sattr3(rqstp, xdr, &args->attrs);
647f8a38e2dSChuck Lever 	case NF3REG:
648f8a38e2dSChuck Lever 	case NF3DIR:
649f8a38e2dSChuck Lever 	case NF3LNK:
650f8a38e2dSChuck Lever 		/* Valid XDR but illegal file types */
651f8a38e2dSChuck Lever 		break;
652f8a38e2dSChuck Lever 	default:
653c44b31c2SChuck Lever 		return false;
6541da177e4SLinus Torvalds 	}
6551da177e4SLinus Torvalds 
656c44b31c2SChuck Lever 	return true;
6571da177e4SLinus Torvalds }
6581da177e4SLinus Torvalds 
659c44b31c2SChuck Lever bool
nfs3svc_decode_renameargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)66016c66364SChuck Lever nfs3svc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
6611da177e4SLinus Torvalds {
662026fec7eSChristoph Hellwig 	struct nfsd3_renameargs *args = rqstp->rq_argp;
663026fec7eSChristoph Hellwig 
664d181e0a4SChuck Lever 	return svcxdr_decode_diropargs3(xdr, &args->ffh,
665d181e0a4SChuck Lever 					&args->fname, &args->flen) &&
666d181e0a4SChuck Lever 		svcxdr_decode_diropargs3(xdr, &args->tfh,
667d181e0a4SChuck Lever 					 &args->tname, &args->tlen);
6681da177e4SLinus Torvalds }
6691da177e4SLinus Torvalds 
670c44b31c2SChuck Lever bool
nfs3svc_decode_linkargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)67116c66364SChuck Lever nfs3svc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
6721da177e4SLinus Torvalds {
673026fec7eSChristoph Hellwig 	struct nfsd3_linkargs *args = rqstp->rq_argp;
674026fec7eSChristoph Hellwig 
675efaa1e7cSChuck Lever 	return svcxdr_decode_nfs_fh3(xdr, &args->ffh) &&
676efaa1e7cSChuck Lever 		svcxdr_decode_diropargs3(xdr, &args->tfh,
677efaa1e7cSChuck Lever 					 &args->tname, &args->tlen);
6781da177e4SLinus Torvalds }
6791da177e4SLinus Torvalds 
680c44b31c2SChuck Lever bool
nfs3svc_decode_readdirargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)68116c66364SChuck Lever nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
6821da177e4SLinus Torvalds {
683026fec7eSChristoph Hellwig 	struct nfsd3_readdirargs *args = rqstp->rq_argp;
684f875a792SNeilBrown 
6859cedc2e6SChuck Lever 	if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
686c44b31c2SChuck Lever 		return false;
6879cedc2e6SChuck Lever 	if (xdr_stream_decode_u64(xdr, &args->cookie) < 0)
688c44b31c2SChuck Lever 		return false;
6899cedc2e6SChuck Lever 	args->verf = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE);
6909cedc2e6SChuck Lever 	if (!args->verf)
691c44b31c2SChuck Lever 		return false;
6929cedc2e6SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->count) < 0)
693c44b31c2SChuck Lever 		return false;
6941da177e4SLinus Torvalds 
695c44b31c2SChuck Lever 	return true;
6961da177e4SLinus Torvalds }
6971da177e4SLinus Torvalds 
698c44b31c2SChuck Lever bool
nfs3svc_decode_readdirplusargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)69916c66364SChuck Lever nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
7001da177e4SLinus Torvalds {
701026fec7eSChristoph Hellwig 	struct nfsd3_readdirargs *args = rqstp->rq_argp;
7029cedc2e6SChuck Lever 	u32 dircount;
7031da177e4SLinus Torvalds 
7049cedc2e6SChuck Lever 	if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
705c44b31c2SChuck Lever 		return false;
7069cedc2e6SChuck Lever 	if (xdr_stream_decode_u64(xdr, &args->cookie) < 0)
707c44b31c2SChuck Lever 		return false;
7089cedc2e6SChuck Lever 	args->verf = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE);
7099cedc2e6SChuck Lever 	if (!args->verf)
710c44b31c2SChuck Lever 		return false;
7119cedc2e6SChuck Lever 	/* dircount is ignored */
7129cedc2e6SChuck Lever 	if (xdr_stream_decode_u32(xdr, &dircount) < 0)
713c44b31c2SChuck Lever 		return false;
7149cedc2e6SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->count) < 0)
715c44b31c2SChuck Lever 		return false;
7161da177e4SLinus Torvalds 
717c44b31c2SChuck Lever 	return true;
7181da177e4SLinus Torvalds }
7191da177e4SLinus Torvalds 
720c44b31c2SChuck Lever bool
nfs3svc_decode_commitargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)72116c66364SChuck Lever nfs3svc_decode_commitargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
7221da177e4SLinus Torvalds {
723026fec7eSChristoph Hellwig 	struct nfsd3_commitargs *args = rqstp->rq_argp;
7241da177e4SLinus Torvalds 
725c8d26a0aSChuck Lever 	if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
726c44b31c2SChuck Lever 		return false;
727c8d26a0aSChuck Lever 	if (xdr_stream_decode_u64(xdr, &args->offset) < 0)
728c44b31c2SChuck Lever 		return false;
729c8d26a0aSChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->count) < 0)
730c44b31c2SChuck Lever 		return false;
731c8d26a0aSChuck Lever 
732c44b31c2SChuck Lever 	return true;
7331da177e4SLinus Torvalds }
7341da177e4SLinus Torvalds 
7351da177e4SLinus Torvalds /*
7361da177e4SLinus Torvalds  * XDR encode functions
7371da177e4SLinus Torvalds  */
738cc028a10SChuck Lever 
7391da177e4SLinus Torvalds /* GETATTR */
740130e2054SChuck Lever bool
nfs3svc_encode_getattrres(struct svc_rqst * rqstp,struct xdr_stream * xdr)741fda49441SChuck Lever nfs3svc_encode_getattrres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
7421da177e4SLinus Torvalds {
74363f8de37SChristoph Hellwig 	struct nfsd3_attrstat *resp = rqstp->rq_resp;
74463f8de37SChristoph Hellwig 
7452c42f804SChuck Lever 	if (!svcxdr_encode_nfsstat3(xdr, resp->status))
746130e2054SChuck Lever 		return false;
7472c42f804SChuck Lever 	switch (resp->status) {
7482c42f804SChuck Lever 	case nfs_ok:
7492c42f804SChuck Lever 		lease_get_mtime(d_inode(resp->fh.fh_dentry), &resp->stat.mtime);
7502c42f804SChuck Lever 		if (!svcxdr_encode_fattr3(rqstp, xdr, &resp->fh, &resp->stat))
751130e2054SChuck Lever 			return false;
7522c42f804SChuck Lever 		break;
75340ee5dc6SPeter Staubach 	}
7542c42f804SChuck Lever 
755130e2054SChuck Lever 	return true;
7561da177e4SLinus Torvalds }
7571da177e4SLinus Torvalds 
7581da177e4SLinus Torvalds /* SETATTR, REMOVE, RMDIR */
759130e2054SChuck Lever bool
nfs3svc_encode_wccstat(struct svc_rqst * rqstp,struct xdr_stream * xdr)760fda49441SChuck Lever nfs3svc_encode_wccstat(struct svc_rqst *rqstp, struct xdr_stream *xdr)
7611da177e4SLinus Torvalds {
76263f8de37SChristoph Hellwig 	struct nfsd3_attrstat *resp = rqstp->rq_resp;
76363f8de37SChristoph Hellwig 
76470f8e839SChuck Lever 	return svcxdr_encode_nfsstat3(xdr, resp->status) &&
76570f8e839SChuck Lever 		svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh);
7661da177e4SLinus Torvalds }
7671da177e4SLinus Torvalds 
7681da177e4SLinus Torvalds /* LOOKUP */
769130e2054SChuck Lever bool
nfs3svc_encode_lookupres(struct svc_rqst * rqstp,struct xdr_stream * xdr)770fda49441SChuck Lever nfs3svc_encode_lookupres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
7711da177e4SLinus Torvalds {
77263f8de37SChristoph Hellwig 	struct nfsd3_diropres *resp = rqstp->rq_resp;
77363f8de37SChristoph Hellwig 
7745cf35335SChuck Lever 	if (!svcxdr_encode_nfsstat3(xdr, resp->status))
775130e2054SChuck Lever 		return false;
7765cf35335SChuck Lever 	switch (resp->status) {
7775cf35335SChuck Lever 	case nfs_ok:
7785cf35335SChuck Lever 		if (!svcxdr_encode_nfs_fh3(xdr, &resp->fh))
779130e2054SChuck Lever 			return false;
7805cf35335SChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
781130e2054SChuck Lever 			return false;
7825cf35335SChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->dirfh))
783130e2054SChuck Lever 			return false;
7845cf35335SChuck Lever 		break;
7855cf35335SChuck Lever 	default:
7865cf35335SChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->dirfh))
787130e2054SChuck Lever 			return false;
7881da177e4SLinus Torvalds 	}
7895cf35335SChuck Lever 
790130e2054SChuck Lever 	return true;
7911da177e4SLinus Torvalds }
7921da177e4SLinus Torvalds 
7931da177e4SLinus Torvalds /* ACCESS */
794130e2054SChuck Lever bool
nfs3svc_encode_accessres(struct svc_rqst * rqstp,struct xdr_stream * xdr)795fda49441SChuck Lever nfs3svc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
7961da177e4SLinus Torvalds {
79763f8de37SChristoph Hellwig 	struct nfsd3_accessres *resp = rqstp->rq_resp;
79863f8de37SChristoph Hellwig 
799907c3822SChuck Lever 	if (!svcxdr_encode_nfsstat3(xdr, resp->status))
800130e2054SChuck Lever 		return false;
801907c3822SChuck Lever 	switch (resp->status) {
802907c3822SChuck Lever 	case nfs_ok:
803907c3822SChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
804130e2054SChuck Lever 			return false;
805907c3822SChuck Lever 		if (xdr_stream_encode_u32(xdr, resp->access) < 0)
806130e2054SChuck Lever 			return false;
807907c3822SChuck Lever 		break;
808907c3822SChuck Lever 	default:
809907c3822SChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
810130e2054SChuck Lever 			return false;
811907c3822SChuck Lever 	}
812907c3822SChuck Lever 
813130e2054SChuck Lever 	return true;
8141da177e4SLinus Torvalds }
8151da177e4SLinus Torvalds 
8161da177e4SLinus Torvalds /* READLINK */
817130e2054SChuck Lever bool
nfs3svc_encode_readlinkres(struct svc_rqst * rqstp,struct xdr_stream * xdr)818fda49441SChuck Lever nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
8191da177e4SLinus Torvalds {
82063f8de37SChristoph Hellwig 	struct nfsd3_readlinkres *resp = rqstp->rq_resp;
82176e5492bSChuck Lever 	struct kvec *head = rqstp->rq_res.head;
82263f8de37SChristoph Hellwig 
8239a9c8923SChuck Lever 	if (!svcxdr_encode_nfsstat3(xdr, resp->status))
824130e2054SChuck Lever 		return false;
8259a9c8923SChuck Lever 	switch (resp->status) {
8269a9c8923SChuck Lever 	case nfs_ok:
8279a9c8923SChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
828130e2054SChuck Lever 			return false;
8299a9c8923SChuck Lever 		if (xdr_stream_encode_u32(xdr, resp->len) < 0)
830130e2054SChuck Lever 			return false;
831*82078b98SChuck Lever 		svcxdr_encode_opaque_pages(rqstp, xdr, resp->pages, 0,
832*82078b98SChuck Lever 					   resp->len);
8339a9c8923SChuck Lever 		if (svc_encode_result_payload(rqstp, head->iov_len, resp->len) < 0)
834130e2054SChuck Lever 			return false;
8359a9c8923SChuck Lever 		break;
8369a9c8923SChuck Lever 	default:
8379a9c8923SChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
838130e2054SChuck Lever 			return false;
8399a9c8923SChuck Lever 	}
8409a9c8923SChuck Lever 
841130e2054SChuck Lever 	return true;
8421da177e4SLinus Torvalds }
8431da177e4SLinus Torvalds 
8441da177e4SLinus Torvalds /* READ */
845130e2054SChuck Lever bool
nfs3svc_encode_readres(struct svc_rqst * rqstp,struct xdr_stream * xdr)846fda49441SChuck Lever nfs3svc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
8471da177e4SLinus Torvalds {
84863f8de37SChristoph Hellwig 	struct nfsd3_readres *resp = rqstp->rq_resp;
84976e5492bSChuck Lever 	struct kvec *head = rqstp->rq_res.head;
85063f8de37SChristoph Hellwig 
851cc9bcdadSChuck Lever 	if (!svcxdr_encode_nfsstat3(xdr, resp->status))
852130e2054SChuck Lever 		return false;
853cc9bcdadSChuck Lever 	switch (resp->status) {
854cc9bcdadSChuck Lever 	case nfs_ok:
855cc9bcdadSChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
856130e2054SChuck Lever 			return false;
857cc9bcdadSChuck Lever 		if (xdr_stream_encode_u32(xdr, resp->count) < 0)
858130e2054SChuck Lever 			return false;
859cc9bcdadSChuck Lever 		if (xdr_stream_encode_bool(xdr, resp->eof) < 0)
860130e2054SChuck Lever 			return false;
861cc9bcdadSChuck Lever 		if (xdr_stream_encode_u32(xdr, resp->count) < 0)
862130e2054SChuck Lever 			return false;
863*82078b98SChuck Lever 		svcxdr_encode_opaque_pages(rqstp, xdr, resp->pages,
864*82078b98SChuck Lever 					   rqstp->rq_res.page_base,
865cc9bcdadSChuck Lever 					   resp->count);
866cc9bcdadSChuck Lever 		if (svc_encode_result_payload(rqstp, head->iov_len, resp->count) < 0)
867130e2054SChuck Lever 			return false;
868cc9bcdadSChuck Lever 		break;
869cc9bcdadSChuck Lever 	default:
870cc9bcdadSChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
871130e2054SChuck Lever 			return false;
872cc9bcdadSChuck Lever 	}
873cc9bcdadSChuck Lever 
874130e2054SChuck Lever 	return true;
8751da177e4SLinus Torvalds }
8761da177e4SLinus Torvalds 
8771da177e4SLinus Torvalds /* WRITE */
878130e2054SChuck Lever bool
nfs3svc_encode_writeres(struct svc_rqst * rqstp,struct xdr_stream * xdr)879fda49441SChuck Lever nfs3svc_encode_writeres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
8801da177e4SLinus Torvalds {
88163f8de37SChristoph Hellwig 	struct nfsd3_writeres *resp = rqstp->rq_resp;
882b9c0ef85SStanislav Kinsbursky 
883ecb7a085SChuck Lever 	if (!svcxdr_encode_nfsstat3(xdr, resp->status))
884130e2054SChuck Lever 		return false;
885ecb7a085SChuck Lever 	switch (resp->status) {
886ecb7a085SChuck Lever 	case nfs_ok:
887ecb7a085SChuck Lever 		if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh))
888130e2054SChuck Lever 			return false;
889ecb7a085SChuck Lever 		if (xdr_stream_encode_u32(xdr, resp->count) < 0)
890130e2054SChuck Lever 			return false;
891ecb7a085SChuck Lever 		if (xdr_stream_encode_u32(xdr, resp->committed) < 0)
892130e2054SChuck Lever 			return false;
893ecb7a085SChuck Lever 		if (!svcxdr_encode_writeverf3(xdr, resp->verf))
894130e2054SChuck Lever 			return false;
895ecb7a085SChuck Lever 		break;
896ecb7a085SChuck Lever 	default:
897ecb7a085SChuck Lever 		if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh))
898130e2054SChuck Lever 			return false;
8991da177e4SLinus Torvalds 	}
900ecb7a085SChuck Lever 
901130e2054SChuck Lever 	return true;
9021da177e4SLinus Torvalds }
9031da177e4SLinus Torvalds 
9041da177e4SLinus Torvalds /* CREATE, MKDIR, SYMLINK, MKNOD */
905130e2054SChuck Lever bool
nfs3svc_encode_createres(struct svc_rqst * rqstp,struct xdr_stream * xdr)906fda49441SChuck Lever nfs3svc_encode_createres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
9071da177e4SLinus Torvalds {
90863f8de37SChristoph Hellwig 	struct nfsd3_diropres *resp = rqstp->rq_resp;
90963f8de37SChristoph Hellwig 
91078315b36SChuck Lever 	if (!svcxdr_encode_nfsstat3(xdr, resp->status))
911130e2054SChuck Lever 		return false;
91278315b36SChuck Lever 	switch (resp->status) {
91378315b36SChuck Lever 	case nfs_ok:
91478315b36SChuck Lever 		if (!svcxdr_encode_post_op_fh3(xdr, &resp->fh))
915130e2054SChuck Lever 			return false;
91678315b36SChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
917130e2054SChuck Lever 			return false;
91878315b36SChuck Lever 		if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->dirfh))
919130e2054SChuck Lever 			return false;
92078315b36SChuck Lever 		break;
92178315b36SChuck Lever 	default:
92278315b36SChuck Lever 		if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->dirfh))
923130e2054SChuck Lever 			return false;
9241da177e4SLinus Torvalds 	}
92578315b36SChuck Lever 
926130e2054SChuck Lever 	return true;
9271da177e4SLinus Torvalds }
9281da177e4SLinus Torvalds 
9291da177e4SLinus Torvalds /* RENAME */
930130e2054SChuck Lever bool
nfs3svc_encode_renameres(struct svc_rqst * rqstp,struct xdr_stream * xdr)931fda49441SChuck Lever nfs3svc_encode_renameres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
9321da177e4SLinus Torvalds {
93363f8de37SChristoph Hellwig 	struct nfsd3_renameres *resp = rqstp->rq_resp;
93463f8de37SChristoph Hellwig 
93589d79e96SChuck Lever 	return svcxdr_encode_nfsstat3(xdr, resp->status) &&
93689d79e96SChuck Lever 		svcxdr_encode_wcc_data(rqstp, xdr, &resp->ffh) &&
93789d79e96SChuck Lever 		svcxdr_encode_wcc_data(rqstp, xdr, &resp->tfh);
9381da177e4SLinus Torvalds }
9391da177e4SLinus Torvalds 
9401da177e4SLinus Torvalds /* LINK */
941130e2054SChuck Lever bool
nfs3svc_encode_linkres(struct svc_rqst * rqstp,struct xdr_stream * xdr)942fda49441SChuck Lever nfs3svc_encode_linkres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
9431da177e4SLinus Torvalds {
94463f8de37SChristoph Hellwig 	struct nfsd3_linkres *resp = rqstp->rq_resp;
94563f8de37SChristoph Hellwig 
9464d74380aSChuck Lever 	return svcxdr_encode_nfsstat3(xdr, resp->status) &&
9474d74380aSChuck Lever 		svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh) &&
9484d74380aSChuck Lever 		svcxdr_encode_wcc_data(rqstp, xdr, &resp->tfh);
9491da177e4SLinus Torvalds }
9501da177e4SLinus Torvalds 
9511da177e4SLinus Torvalds /* READDIR */
952130e2054SChuck Lever bool
nfs3svc_encode_readdirres(struct svc_rqst * rqstp,struct xdr_stream * xdr)953fda49441SChuck Lever nfs3svc_encode_readdirres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
9541da177e4SLinus Torvalds {
95563f8de37SChristoph Hellwig 	struct nfsd3_readdirres *resp = rqstp->rq_resp;
9567f87fc2dSChuck Lever 	struct xdr_buf *dirlist = &resp->dirlist;
95763f8de37SChristoph Hellwig 
958e4ccfe30SChuck Lever 	if (!svcxdr_encode_nfsstat3(xdr, resp->status))
959130e2054SChuck Lever 		return false;
960e4ccfe30SChuck Lever 	switch (resp->status) {
961e4ccfe30SChuck Lever 	case nfs_ok:
962e4ccfe30SChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
963130e2054SChuck Lever 			return false;
964e4ccfe30SChuck Lever 		if (!svcxdr_encode_cookieverf3(xdr, resp->verf))
965130e2054SChuck Lever 			return false;
966*82078b98SChuck Lever 		svcxdr_encode_opaque_pages(rqstp, xdr, dirlist->pages, 0,
967*82078b98SChuck Lever 					   dirlist->len);
968e4ccfe30SChuck Lever 		/* no more entries */
969e4ccfe30SChuck Lever 		if (xdr_stream_encode_item_absent(xdr) < 0)
970130e2054SChuck Lever 			return false;
971e4ccfe30SChuck Lever 		if (xdr_stream_encode_bool(xdr, resp->common.err == nfserr_eof) < 0)
972130e2054SChuck Lever 			return false;
973e4ccfe30SChuck Lever 		break;
974e4ccfe30SChuck Lever 	default:
975e4ccfe30SChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &resp->fh))
976130e2054SChuck Lever 			return false;
977e4ccfe30SChuck Lever 	}
9781da177e4SLinus Torvalds 
979130e2054SChuck Lever 	return true;
9801da177e4SLinus Torvalds }
9811da177e4SLinus Torvalds 
982efe39651SAl Viro static __be32
compose_entry_fh(struct nfsd3_readdirres * cd,struct svc_fh * fhp,const char * name,int namlen,u64 ino)9831da177e4SLinus Torvalds compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
98443b0e7eaSNeilBrown 		 const char *name, int namlen, u64 ino)
9851da177e4SLinus Torvalds {
9861da177e4SLinus Torvalds 	struct svc_export	*exp;
9871da177e4SLinus Torvalds 	struct dentry		*dparent, *dchild;
988efe39651SAl Viro 	__be32 rv = nfserr_noent;
9891da177e4SLinus Torvalds 
9901da177e4SLinus Torvalds 	dparent = cd->fh.fh_dentry;
9911da177e4SLinus Torvalds 	exp  = cd->fh.fh_export;
9921da177e4SLinus Torvalds 
9931da177e4SLinus Torvalds 	if (isdotent(name, namlen)) {
9941da177e4SLinus Torvalds 		if (namlen == 2) {
9951da177e4SLinus Torvalds 			dchild = dget_parent(dparent);
99651b2ee7dSJ. Bruce Fields 			/*
99751b2ee7dSJ. Bruce Fields 			 * Don't return filehandle for ".." if we're at
99851b2ee7dSJ. Bruce Fields 			 * the filesystem or export root:
99951b2ee7dSJ. Bruce Fields 			 */
1000efe39651SAl Viro 			if (dchild == dparent)
1001efe39651SAl Viro 				goto out;
100251b2ee7dSJ. Bruce Fields 			if (dparent == exp->ex_path.dentry)
100351b2ee7dSJ. Bruce Fields 				goto out;
10041da177e4SLinus Torvalds 		} else
10051da177e4SLinus Torvalds 			dchild = dget(dparent);
10061da177e4SLinus Torvalds 	} else
10076c2d4798SAl Viro 		dchild = lookup_positive_unlocked(name, dparent, namlen);
10081da177e4SLinus Torvalds 	if (IS_ERR(dchild))
1009efe39651SAl Viro 		return rv;
10108177e6d6SJ. Bruce Fields 	if (d_mountpoint(dchild))
10118177e6d6SJ. Bruce Fields 		goto out;
101243b0e7eaSNeilBrown 	if (dchild->d_inode->i_ino != ino)
101343b0e7eaSNeilBrown 		goto out;
1014efe39651SAl Viro 	rv = fh_compose(fhp, exp, dchild, &cd->fh);
10158177e6d6SJ. Bruce Fields out:
10161da177e4SLinus Torvalds 	dput(dchild);
10171da177e4SLinus Torvalds 	return rv;
10181da177e4SLinus Torvalds }
10191da177e4SLinus Torvalds 
1020a161e6c7SChuck Lever /**
1021a161e6c7SChuck Lever  * nfs3svc_encode_cookie3 - Encode a directory offset cookie
1022a161e6c7SChuck Lever  * @resp: readdir result context
1023a161e6c7SChuck Lever  * @offset: offset cookie to encode
1024a161e6c7SChuck Lever  *
10257f87fc2dSChuck Lever  * The buffer space for the offset cookie has already been reserved
10267f87fc2dSChuck Lever  * by svcxdr_encode_entry3_common().
1027a161e6c7SChuck Lever  */
nfs3svc_encode_cookie3(struct nfsd3_readdirres * resp,u64 offset)1028a161e6c7SChuck Lever void nfs3svc_encode_cookie3(struct nfsd3_readdirres *resp, u64 offset)
1029a161e6c7SChuck Lever {
10307f87fc2dSChuck Lever 	__be64 cookie = cpu_to_be64(offset);
1031a161e6c7SChuck Lever 
10327f87fc2dSChuck Lever 	if (!resp->cookie_offset)
10337f87fc2dSChuck Lever 		return;
10347f87fc2dSChuck Lever 	write_bytes_to_xdr_buf(&resp->dirlist, resp->cookie_offset, &cookie,
10357f87fc2dSChuck Lever 			       sizeof(cookie));
10367f87fc2dSChuck Lever 	resp->cookie_offset = 0;
1037a161e6c7SChuck Lever }
1038a161e6c7SChuck Lever 
10398b704498SChuck Lever static bool
svcxdr_encode_entry3_common(struct nfsd3_readdirres * resp,const char * name,int namlen,loff_t offset,u64 ino)10407f87fc2dSChuck Lever svcxdr_encode_entry3_common(struct nfsd3_readdirres *resp, const char *name,
10417f87fc2dSChuck Lever 			    int namlen, loff_t offset, u64 ino)
10427f87fc2dSChuck Lever {
10437f87fc2dSChuck Lever 	struct xdr_buf *dirlist = &resp->dirlist;
10447f87fc2dSChuck Lever 	struct xdr_stream *xdr = &resp->xdr;
10457f87fc2dSChuck Lever 
10467f87fc2dSChuck Lever 	if (xdr_stream_encode_item_present(xdr) < 0)
10477f87fc2dSChuck Lever 		return false;
10487f87fc2dSChuck Lever 	/* fileid */
10497f87fc2dSChuck Lever 	if (xdr_stream_encode_u64(xdr, ino) < 0)
10507f87fc2dSChuck Lever 		return false;
10517f87fc2dSChuck Lever 	/* name */
10527f87fc2dSChuck Lever 	if (xdr_stream_encode_opaque(xdr, name, min(namlen, NFS3_MAXNAMLEN)) < 0)
10537f87fc2dSChuck Lever 		return false;
10547f87fc2dSChuck Lever 	/* cookie */
10557f87fc2dSChuck Lever 	resp->cookie_offset = dirlist->len;
1056c306d737SChuck Lever 	if (xdr_stream_encode_u64(xdr, OFFSET_MAX) < 0)
10577f87fc2dSChuck Lever 		return false;
10587f87fc2dSChuck Lever 
10597f87fc2dSChuck Lever 	return true;
10607f87fc2dSChuck Lever }
10617f87fc2dSChuck Lever 
10627f87fc2dSChuck Lever /**
10637f87fc2dSChuck Lever  * nfs3svc_encode_entry3 - encode one NFSv3 READDIR entry
10647f87fc2dSChuck Lever  * @data: directory context
10657f87fc2dSChuck Lever  * @name: name of the object to be encoded
10667f87fc2dSChuck Lever  * @namlen: length of that name, in bytes
10677f87fc2dSChuck Lever  * @offset: the offset of the previous entry
10687f87fc2dSChuck Lever  * @ino: the fileid of this entry
10697f87fc2dSChuck Lever  * @d_type: unused
10707f87fc2dSChuck Lever  *
10717f87fc2dSChuck Lever  * Return values:
10727f87fc2dSChuck Lever  *   %0: Entry was successfully encoded.
10737f87fc2dSChuck Lever  *   %-EINVAL: An encoding problem occured, secondary status code in resp->common.err
10747f87fc2dSChuck Lever  *
10757f87fc2dSChuck Lever  * On exit, the following fields are updated:
10767f87fc2dSChuck Lever  *   - resp->xdr
10777f87fc2dSChuck Lever  *   - resp->common.err
10787f87fc2dSChuck Lever  *   - resp->cookie_offset
10797f87fc2dSChuck Lever  */
nfs3svc_encode_entry3(void * data,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)10807f87fc2dSChuck Lever int nfs3svc_encode_entry3(void *data, const char *name, int namlen,
10817f87fc2dSChuck Lever 			  loff_t offset, u64 ino, unsigned int d_type)
10827f87fc2dSChuck Lever {
10837f87fc2dSChuck Lever 	struct readdir_cd *ccd = data;
10847f87fc2dSChuck Lever 	struct nfsd3_readdirres *resp = container_of(ccd,
10857f87fc2dSChuck Lever 						     struct nfsd3_readdirres,
10867f87fc2dSChuck Lever 						     common);
10877f87fc2dSChuck Lever 	unsigned int starting_length = resp->dirlist.len;
10887f87fc2dSChuck Lever 
10897f87fc2dSChuck Lever 	/* The offset cookie for the previous entry */
10907f87fc2dSChuck Lever 	nfs3svc_encode_cookie3(resp, offset);
10917f87fc2dSChuck Lever 
10927f87fc2dSChuck Lever 	if (!svcxdr_encode_entry3_common(resp, name, namlen, offset, ino))
10937f87fc2dSChuck Lever 		goto out_toosmall;
10947f87fc2dSChuck Lever 
10957f87fc2dSChuck Lever 	xdr_commit_encode(&resp->xdr);
10967f87fc2dSChuck Lever 	resp->common.err = nfs_ok;
10977f87fc2dSChuck Lever 	return 0;
10987f87fc2dSChuck Lever 
10997f87fc2dSChuck Lever out_toosmall:
11007f87fc2dSChuck Lever 	resp->cookie_offset = 0;
11017f87fc2dSChuck Lever 	resp->common.err = nfserr_toosmall;
11027f87fc2dSChuck Lever 	resp->dirlist.len = starting_length;
11037f87fc2dSChuck Lever 	return -EINVAL;
11047f87fc2dSChuck Lever }
11057f87fc2dSChuck Lever 
11067f87fc2dSChuck Lever static bool
svcxdr_encode_entry3_plus(struct nfsd3_readdirres * resp,const char * name,int namlen,u64 ino)11077f87fc2dSChuck Lever svcxdr_encode_entry3_plus(struct nfsd3_readdirres *resp, const char *name,
11087f87fc2dSChuck Lever 			  int namlen, u64 ino)
11097f87fc2dSChuck Lever {
11107f87fc2dSChuck Lever 	struct xdr_stream *xdr = &resp->xdr;
11117f87fc2dSChuck Lever 	struct svc_fh *fhp = &resp->scratch;
11127f87fc2dSChuck Lever 	bool result;
11137f87fc2dSChuck Lever 
11147f87fc2dSChuck Lever 	result = false;
11157f87fc2dSChuck Lever 	fh_init(fhp, NFS3_FHSIZE);
11167f87fc2dSChuck Lever 	if (compose_entry_fh(resp, fhp, name, namlen, ino) != nfs_ok)
11177f87fc2dSChuck Lever 		goto out_noattrs;
11187f87fc2dSChuck Lever 
11197f87fc2dSChuck Lever 	if (!svcxdr_encode_post_op_attr(resp->rqstp, xdr, fhp))
11207f87fc2dSChuck Lever 		goto out;
11217f87fc2dSChuck Lever 	if (!svcxdr_encode_post_op_fh3(xdr, fhp))
11227f87fc2dSChuck Lever 		goto out;
11237f87fc2dSChuck Lever 	result = true;
11247f87fc2dSChuck Lever 
11257f87fc2dSChuck Lever out:
11267f87fc2dSChuck Lever 	fh_put(fhp);
11277f87fc2dSChuck Lever 	return result;
11287f87fc2dSChuck Lever 
11297f87fc2dSChuck Lever out_noattrs:
11307f87fc2dSChuck Lever 	if (xdr_stream_encode_item_absent(xdr) < 0)
11317f87fc2dSChuck Lever 		return false;
11327f87fc2dSChuck Lever 	if (xdr_stream_encode_item_absent(xdr) < 0)
11337f87fc2dSChuck Lever 		return false;
11347f87fc2dSChuck Lever 	return true;
11357f87fc2dSChuck Lever }
11367f87fc2dSChuck Lever 
11377f87fc2dSChuck Lever /**
11387f87fc2dSChuck Lever  * nfs3svc_encode_entryplus3 - encode one NFSv3 READDIRPLUS entry
11397f87fc2dSChuck Lever  * @data: directory context
11407f87fc2dSChuck Lever  * @name: name of the object to be encoded
11417f87fc2dSChuck Lever  * @namlen: length of that name, in bytes
11427f87fc2dSChuck Lever  * @offset: the offset of the previous entry
11437f87fc2dSChuck Lever  * @ino: the fileid of this entry
11447f87fc2dSChuck Lever  * @d_type: unused
11457f87fc2dSChuck Lever  *
11467f87fc2dSChuck Lever  * Return values:
11477f87fc2dSChuck Lever  *   %0: Entry was successfully encoded.
11487f87fc2dSChuck Lever  *   %-EINVAL: An encoding problem occured, secondary status code in resp->common.err
11497f87fc2dSChuck Lever  *
11507f87fc2dSChuck Lever  * On exit, the following fields are updated:
11517f87fc2dSChuck Lever  *   - resp->xdr
11527f87fc2dSChuck Lever  *   - resp->common.err
11537f87fc2dSChuck Lever  *   - resp->cookie_offset
11547f87fc2dSChuck Lever  */
nfs3svc_encode_entryplus3(void * data,const char * name,int namlen,loff_t offset,u64 ino,unsigned int d_type)11557f87fc2dSChuck Lever int nfs3svc_encode_entryplus3(void *data, const char *name, int namlen,
11567f87fc2dSChuck Lever 			      loff_t offset, u64 ino, unsigned int d_type)
11577f87fc2dSChuck Lever {
11587f87fc2dSChuck Lever 	struct readdir_cd *ccd = data;
11597f87fc2dSChuck Lever 	struct nfsd3_readdirres *resp = container_of(ccd,
11607f87fc2dSChuck Lever 						     struct nfsd3_readdirres,
11617f87fc2dSChuck Lever 						     common);
11627f87fc2dSChuck Lever 	unsigned int starting_length = resp->dirlist.len;
11637f87fc2dSChuck Lever 
11647f87fc2dSChuck Lever 	/* The offset cookie for the previous entry */
11657f87fc2dSChuck Lever 	nfs3svc_encode_cookie3(resp, offset);
11667f87fc2dSChuck Lever 
11677f87fc2dSChuck Lever 	if (!svcxdr_encode_entry3_common(resp, name, namlen, offset, ino))
11687f87fc2dSChuck Lever 		goto out_toosmall;
11697f87fc2dSChuck Lever 	if (!svcxdr_encode_entry3_plus(resp, name, namlen, ino))
11707f87fc2dSChuck Lever 		goto out_toosmall;
11717f87fc2dSChuck Lever 
11727f87fc2dSChuck Lever 	xdr_commit_encode(&resp->xdr);
11737f87fc2dSChuck Lever 	resp->common.err = nfs_ok;
11747f87fc2dSChuck Lever 	return 0;
11757f87fc2dSChuck Lever 
11767f87fc2dSChuck Lever out_toosmall:
11777f87fc2dSChuck Lever 	resp->cookie_offset = 0;
11787f87fc2dSChuck Lever 	resp->common.err = nfserr_toosmall;
11797f87fc2dSChuck Lever 	resp->dirlist.len = starting_length;
11807f87fc2dSChuck Lever 	return -EINVAL;
11817f87fc2dSChuck Lever }
11827f87fc2dSChuck Lever 
11837f87fc2dSChuck Lever static bool
svcxdr_encode_fsstat3resok(struct xdr_stream * xdr,const struct nfsd3_fsstatres * resp)11848b704498SChuck Lever svcxdr_encode_fsstat3resok(struct xdr_stream *xdr,
11858b704498SChuck Lever 			   const struct nfsd3_fsstatres *resp)
11861da177e4SLinus Torvalds {
11878b704498SChuck Lever 	const struct kstatfs *s = &resp->stats;
11881da177e4SLinus Torvalds 	u64 bs = s->f_bsize;
11898b704498SChuck Lever 	__be32 *p;
11901da177e4SLinus Torvalds 
11918b704498SChuck Lever 	p = xdr_reserve_space(xdr, XDR_UNIT * 13);
11928b704498SChuck Lever 	if (!p)
11938b704498SChuck Lever 		return false;
11941da177e4SLinus Torvalds 	p = xdr_encode_hyper(p, bs * s->f_blocks);	/* total bytes */
11951da177e4SLinus Torvalds 	p = xdr_encode_hyper(p, bs * s->f_bfree);	/* free bytes */
11961da177e4SLinus Torvalds 	p = xdr_encode_hyper(p, bs * s->f_bavail);	/* user available bytes */
11971da177e4SLinus Torvalds 	p = xdr_encode_hyper(p, s->f_files);		/* total inodes */
11981da177e4SLinus Torvalds 	p = xdr_encode_hyper(p, s->f_ffree);		/* free inodes */
11991da177e4SLinus Torvalds 	p = xdr_encode_hyper(p, s->f_ffree);		/* user available inodes */
12008b704498SChuck Lever 	*p = cpu_to_be32(resp->invarsec);		/* mean unchanged time */
12018b704498SChuck Lever 
12028b704498SChuck Lever 	return true;
12031da177e4SLinus Torvalds }
12048b704498SChuck Lever 
12058b704498SChuck Lever /* FSSTAT */
1206130e2054SChuck Lever bool
nfs3svc_encode_fsstatres(struct svc_rqst * rqstp,struct xdr_stream * xdr)1207fda49441SChuck Lever nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
12088b704498SChuck Lever {
12098b704498SChuck Lever 	struct nfsd3_fsstatres *resp = rqstp->rq_resp;
12108b704498SChuck Lever 
12118b704498SChuck Lever 	if (!svcxdr_encode_nfsstat3(xdr, resp->status))
1212130e2054SChuck Lever 		return false;
12138b704498SChuck Lever 	switch (resp->status) {
12148b704498SChuck Lever 	case nfs_ok:
12158b704498SChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &nfs3svc_null_fh))
1216130e2054SChuck Lever 			return false;
12178b704498SChuck Lever 		if (!svcxdr_encode_fsstat3resok(xdr, resp))
1218130e2054SChuck Lever 			return false;
12198b704498SChuck Lever 		break;
12208b704498SChuck Lever 	default:
12218b704498SChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &nfs3svc_null_fh))
1222130e2054SChuck Lever 			return false;
12238b704498SChuck Lever 	}
12248b704498SChuck Lever 
1225130e2054SChuck Lever 	return true;
12261da177e4SLinus Torvalds }
12271da177e4SLinus Torvalds 
12280a139d1bSChuck Lever static bool
svcxdr_encode_fsinfo3resok(struct xdr_stream * xdr,const struct nfsd3_fsinfores * resp)12290a139d1bSChuck Lever svcxdr_encode_fsinfo3resok(struct xdr_stream *xdr,
12300a139d1bSChuck Lever 			   const struct nfsd3_fsinfores *resp)
12310a139d1bSChuck Lever {
12320a139d1bSChuck Lever 	__be32 *p;
12330a139d1bSChuck Lever 
12340a139d1bSChuck Lever 	p = xdr_reserve_space(xdr, XDR_UNIT * 12);
12350a139d1bSChuck Lever 	if (!p)
12360a139d1bSChuck Lever 		return false;
12370a139d1bSChuck Lever 	*p++ = cpu_to_be32(resp->f_rtmax);
12380a139d1bSChuck Lever 	*p++ = cpu_to_be32(resp->f_rtpref);
12390a139d1bSChuck Lever 	*p++ = cpu_to_be32(resp->f_rtmult);
12400a139d1bSChuck Lever 	*p++ = cpu_to_be32(resp->f_wtmax);
12410a139d1bSChuck Lever 	*p++ = cpu_to_be32(resp->f_wtpref);
12420a139d1bSChuck Lever 	*p++ = cpu_to_be32(resp->f_wtmult);
12430a139d1bSChuck Lever 	*p++ = cpu_to_be32(resp->f_dtpref);
12440a139d1bSChuck Lever 	p = xdr_encode_hyper(p, resp->f_maxfilesize);
12450a139d1bSChuck Lever 	p = encode_nfstime3(p, &nfs3svc_time_delta);
12460a139d1bSChuck Lever 	*p = cpu_to_be32(resp->f_properties);
12470a139d1bSChuck Lever 
12480a139d1bSChuck Lever 	return true;
12490a139d1bSChuck Lever }
12500a139d1bSChuck Lever 
12511da177e4SLinus Torvalds /* FSINFO */
1252130e2054SChuck Lever bool
nfs3svc_encode_fsinfores(struct svc_rqst * rqstp,struct xdr_stream * xdr)1253fda49441SChuck Lever nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, struct xdr_stream *xdr)
12541da177e4SLinus Torvalds {
125563f8de37SChristoph Hellwig 	struct nfsd3_fsinfores *resp = rqstp->rq_resp;
125663f8de37SChristoph Hellwig 
12570a139d1bSChuck Lever 	if (!svcxdr_encode_nfsstat3(xdr, resp->status))
1258130e2054SChuck Lever 		return false;
12590a139d1bSChuck Lever 	switch (resp->status) {
12600a139d1bSChuck Lever 	case nfs_ok:
12610a139d1bSChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &nfs3svc_null_fh))
1262130e2054SChuck Lever 			return false;
12630a139d1bSChuck Lever 		if (!svcxdr_encode_fsinfo3resok(xdr, resp))
1264130e2054SChuck Lever 			return false;
12650a139d1bSChuck Lever 		break;
12660a139d1bSChuck Lever 	default:
12670a139d1bSChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &nfs3svc_null_fh))
1268130e2054SChuck Lever 			return false;
12691da177e4SLinus Torvalds 	}
12701da177e4SLinus Torvalds 
1271130e2054SChuck Lever 	return true;
12721da177e4SLinus Torvalds }
12731da177e4SLinus Torvalds 
1274ded04a58SChuck Lever static bool
svcxdr_encode_pathconf3resok(struct xdr_stream * xdr,const struct nfsd3_pathconfres * resp)1275ded04a58SChuck Lever svcxdr_encode_pathconf3resok(struct xdr_stream *xdr,
1276ded04a58SChuck Lever 			     const struct nfsd3_pathconfres *resp)
1277ded04a58SChuck Lever {
1278ded04a58SChuck Lever 	__be32 *p;
1279ded04a58SChuck Lever 
1280ded04a58SChuck Lever 	p = xdr_reserve_space(xdr, XDR_UNIT * 6);
1281ded04a58SChuck Lever 	if (!p)
1282ded04a58SChuck Lever 		return false;
1283ded04a58SChuck Lever 	*p++ = cpu_to_be32(resp->p_link_max);
1284ded04a58SChuck Lever 	*p++ = cpu_to_be32(resp->p_name_max);
1285ded04a58SChuck Lever 	p = xdr_encode_bool(p, resp->p_no_trunc);
1286ded04a58SChuck Lever 	p = xdr_encode_bool(p, resp->p_chown_restricted);
1287ded04a58SChuck Lever 	p = xdr_encode_bool(p, resp->p_case_insensitive);
1288ded04a58SChuck Lever 	xdr_encode_bool(p, resp->p_case_preserving);
1289ded04a58SChuck Lever 
1290ded04a58SChuck Lever 	return true;
1291ded04a58SChuck Lever }
1292ded04a58SChuck Lever 
12931da177e4SLinus Torvalds /* PATHCONF */
1294130e2054SChuck Lever bool
nfs3svc_encode_pathconfres(struct svc_rqst * rqstp,struct xdr_stream * xdr)1295fda49441SChuck Lever nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
12961da177e4SLinus Torvalds {
129763f8de37SChristoph Hellwig 	struct nfsd3_pathconfres *resp = rqstp->rq_resp;
129863f8de37SChristoph Hellwig 
1299ded04a58SChuck Lever 	if (!svcxdr_encode_nfsstat3(xdr, resp->status))
1300130e2054SChuck Lever 		return false;
1301ded04a58SChuck Lever 	switch (resp->status) {
1302ded04a58SChuck Lever 	case nfs_ok:
1303ded04a58SChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &nfs3svc_null_fh))
1304130e2054SChuck Lever 			return false;
1305ded04a58SChuck Lever 		if (!svcxdr_encode_pathconf3resok(xdr, resp))
1306130e2054SChuck Lever 			return false;
1307ded04a58SChuck Lever 		break;
1308ded04a58SChuck Lever 	default:
1309ded04a58SChuck Lever 		if (!svcxdr_encode_post_op_attr(rqstp, xdr, &nfs3svc_null_fh))
1310130e2054SChuck Lever 			return false;
13111da177e4SLinus Torvalds 	}
13121da177e4SLinus Torvalds 
1313130e2054SChuck Lever 	return true;
13141da177e4SLinus Torvalds }
13151da177e4SLinus Torvalds 
13161da177e4SLinus Torvalds /* COMMIT */
1317130e2054SChuck Lever bool
nfs3svc_encode_commitres(struct svc_rqst * rqstp,struct xdr_stream * xdr)1318fda49441SChuck Lever nfs3svc_encode_commitres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
13191da177e4SLinus Torvalds {
132063f8de37SChristoph Hellwig 	struct nfsd3_commitres *resp = rqstp->rq_resp;
1321b9c0ef85SStanislav Kinsbursky 
13225ef2826cSChuck Lever 	if (!svcxdr_encode_nfsstat3(xdr, resp->status))
1323130e2054SChuck Lever 		return false;
13245ef2826cSChuck Lever 	switch (resp->status) {
13255ef2826cSChuck Lever 	case nfs_ok:
13265ef2826cSChuck Lever 		if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh))
1327130e2054SChuck Lever 			return false;
13285ef2826cSChuck Lever 		if (!svcxdr_encode_writeverf3(xdr, resp->verf))
1329130e2054SChuck Lever 			return false;
13305ef2826cSChuck Lever 		break;
13315ef2826cSChuck Lever 	default:
13325ef2826cSChuck Lever 		if (!svcxdr_encode_wcc_data(rqstp, xdr, &resp->fh))
1333130e2054SChuck Lever 			return false;
13341da177e4SLinus Torvalds 	}
13355ef2826cSChuck Lever 
1336130e2054SChuck Lever 	return true;
13371da177e4SLinus Torvalds }
13381da177e4SLinus Torvalds 
13391da177e4SLinus Torvalds /*
13401da177e4SLinus Torvalds  * XDR release functions
13411da177e4SLinus Torvalds  */
13428537488bSChristoph Hellwig void
nfs3svc_release_fhandle(struct svc_rqst * rqstp)13438537488bSChristoph Hellwig nfs3svc_release_fhandle(struct svc_rqst *rqstp)
13441da177e4SLinus Torvalds {
13458537488bSChristoph Hellwig 	struct nfsd3_attrstat *resp = rqstp->rq_resp;
13468537488bSChristoph Hellwig 
13471da177e4SLinus Torvalds 	fh_put(&resp->fh);
13481da177e4SLinus Torvalds }
13491da177e4SLinus Torvalds 
13508537488bSChristoph Hellwig void
nfs3svc_release_fhandle2(struct svc_rqst * rqstp)13518537488bSChristoph Hellwig nfs3svc_release_fhandle2(struct svc_rqst *rqstp)
13521da177e4SLinus Torvalds {
13538537488bSChristoph Hellwig 	struct nfsd3_fhandle_pair *resp = rqstp->rq_resp;
13548537488bSChristoph Hellwig 
13551da177e4SLinus Torvalds 	fh_put(&resp->fh1);
13561da177e4SLinus Torvalds 	fh_put(&resp->fh2);
13571da177e4SLinus Torvalds }
1358