xref: /openbmc/linux/fs/nfsd/nfsxdr.c (revision 09f75a5375ac61f4adb94da0accc1cfc60eb4f2b)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * XDR support for nfsd
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
83dadecceSAl Viro #include "vfs.h"
99a74af21SBoaz Harrosh #include "xdr.h"
102e8138a2SJ. Bruce Fields #include "auth.h"
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #define NFSDDBG_FACILITY		NFSDDBG_XDR
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds /*
151da177e4SLinus Torvalds  * Mapping of S_IF* types to NFS file types
161da177e4SLinus Torvalds  */
171da177e4SLinus Torvalds static u32	nfs_ftypes[] = {
181da177e4SLinus Torvalds 	NFNON,  NFCHR,  NFCHR, NFBAD,
191da177e4SLinus Torvalds 	NFDIR,  NFBAD,  NFBLK, NFBAD,
201da177e4SLinus Torvalds 	NFREG,  NFBAD,  NFLNK, NFBAD,
211da177e4SLinus Torvalds 	NFSOCK, NFBAD,  NFLNK, NFBAD,
221da177e4SLinus Torvalds };
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds /*
26ebcd8e8bSChuck Lever  * Basic NFSv2 data types (RFC 1094 Section 2.3)
271da177e4SLinus Torvalds  */
28ebcd8e8bSChuck Lever 
29131a21c2SAl Viro static __be32 *
30131a21c2SAl Viro decode_fh(__be32 *p, struct svc_fh *fhp)
311da177e4SLinus Torvalds {
321da177e4SLinus Torvalds 	fh_init(fhp, NFS_FHSIZE);
331da177e4SLinus Torvalds 	memcpy(&fhp->fh_handle.fh_base, p, NFS_FHSIZE);
341da177e4SLinus Torvalds 	fhp->fh_handle.fh_size = NFS_FHSIZE;
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds 	/* FIXME: Look up export pointer here and verify
371da177e4SLinus Torvalds 	 * Sun Secure RPC if requested */
381da177e4SLinus Torvalds 	return p + (NFS_FHSIZE >> 2);
391da177e4SLinus Torvalds }
401da177e4SLinus Torvalds 
41ebcd8e8bSChuck Lever static bool
42ebcd8e8bSChuck Lever svcxdr_decode_fhandle(struct xdr_stream *xdr, struct svc_fh *fhp)
43ebcd8e8bSChuck Lever {
44ebcd8e8bSChuck Lever 	__be32 *p;
45ebcd8e8bSChuck Lever 
46ebcd8e8bSChuck Lever 	p = xdr_inline_decode(xdr, NFS_FHSIZE);
47ebcd8e8bSChuck Lever 	if (!p)
48ebcd8e8bSChuck Lever 		return false;
49ebcd8e8bSChuck Lever 	fh_init(fhp, NFS_FHSIZE);
50ebcd8e8bSChuck Lever 	memcpy(&fhp->fh_handle.fh_base, p, NFS_FHSIZE);
51ebcd8e8bSChuck Lever 	fhp->fh_handle.fh_size = NFS_FHSIZE;
52ebcd8e8bSChuck Lever 
53ebcd8e8bSChuck Lever 	return true;
54ebcd8e8bSChuck Lever }
55ebcd8e8bSChuck Lever 
56a257cdd0SAndreas Gruenbacher /* Helper function for NFSv2 ACL code */
57131a21c2SAl Viro __be32 *nfs2svc_decode_fh(__be32 *p, struct svc_fh *fhp)
58a257cdd0SAndreas Gruenbacher {
59a257cdd0SAndreas Gruenbacher 	return decode_fh(p, fhp);
60a257cdd0SAndreas Gruenbacher }
61a257cdd0SAndreas Gruenbacher 
623ee6f61cSAdrian Bunk static __be32 *
63131a21c2SAl Viro encode_fh(__be32 *p, struct svc_fh *fhp)
641da177e4SLinus Torvalds {
651da177e4SLinus Torvalds 	memcpy(p, &fhp->fh_handle.fh_base, NFS_FHSIZE);
661da177e4SLinus Torvalds 	return p + (NFS_FHSIZE>> 2);
671da177e4SLinus Torvalds }
681da177e4SLinus Torvalds 
696d742c18SChuck Lever static bool
706d742c18SChuck Lever svcxdr_decode_filename(struct xdr_stream *xdr, char **name, unsigned int *len)
716d742c18SChuck Lever {
726d742c18SChuck Lever 	u32 size, i;
736d742c18SChuck Lever 	__be32 *p;
746d742c18SChuck Lever 	char *c;
756d742c18SChuck Lever 
766d742c18SChuck Lever 	if (xdr_stream_decode_u32(xdr, &size) < 0)
776d742c18SChuck Lever 		return false;
786d742c18SChuck Lever 	if (size == 0 || size > NFS_MAXNAMLEN)
796d742c18SChuck Lever 		return false;
806d742c18SChuck Lever 	p = xdr_inline_decode(xdr, size);
816d742c18SChuck Lever 	if (!p)
826d742c18SChuck Lever 		return false;
836d742c18SChuck Lever 
846d742c18SChuck Lever 	*len = size;
856d742c18SChuck Lever 	*name = (char *)p;
866d742c18SChuck Lever 	for (i = 0, c = *name; i < size; i++, c++)
876d742c18SChuck Lever 		if (*c == '\0' || *c == '/')
886d742c18SChuck Lever 			return false;
896d742c18SChuck Lever 
906d742c18SChuck Lever 	return true;
916d742c18SChuck Lever }
926d742c18SChuck Lever 
936d742c18SChuck Lever static bool
946d742c18SChuck Lever svcxdr_decode_diropargs(struct xdr_stream *xdr, struct svc_fh *fhp,
956d742c18SChuck Lever 			char **name, unsigned int *len)
966d742c18SChuck Lever {
976d742c18SChuck Lever 	return svcxdr_decode_fhandle(xdr, fhp) &&
986d742c18SChuck Lever 		svcxdr_decode_filename(xdr, name, len);
996d742c18SChuck Lever }
1006d742c18SChuck Lever 
1012fdd6bd2SChuck Lever static bool
1022fdd6bd2SChuck Lever svcxdr_decode_sattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
1032fdd6bd2SChuck Lever 		    struct iattr *iap)
1042fdd6bd2SChuck Lever {
1052fdd6bd2SChuck Lever 	u32 tmp1, tmp2;
1062fdd6bd2SChuck Lever 	__be32 *p;
1072fdd6bd2SChuck Lever 
1082fdd6bd2SChuck Lever 	p = xdr_inline_decode(xdr, XDR_UNIT * 8);
1092fdd6bd2SChuck Lever 	if (!p)
1102fdd6bd2SChuck Lever 		return false;
1112fdd6bd2SChuck Lever 
1122fdd6bd2SChuck Lever 	iap->ia_valid = 0;
1132fdd6bd2SChuck Lever 
1142fdd6bd2SChuck Lever 	/*
1152fdd6bd2SChuck Lever 	 * Some Sun clients put 0xffff in the mode field when they
1162fdd6bd2SChuck Lever 	 * mean 0xffffffff.
1172fdd6bd2SChuck Lever 	 */
1182fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1192fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1 && tmp1 != 0xffff) {
1202fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_MODE;
1212fdd6bd2SChuck Lever 		iap->ia_mode = tmp1;
1222fdd6bd2SChuck Lever 	}
1232fdd6bd2SChuck Lever 
1242fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1252fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1) {
1262fdd6bd2SChuck Lever 		iap->ia_uid = make_kuid(nfsd_user_namespace(rqstp), tmp1);
1272fdd6bd2SChuck Lever 		if (uid_valid(iap->ia_uid))
1282fdd6bd2SChuck Lever 			iap->ia_valid |= ATTR_UID;
1292fdd6bd2SChuck Lever 	}
1302fdd6bd2SChuck Lever 
1312fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1322fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1) {
1332fdd6bd2SChuck Lever 		iap->ia_gid = make_kgid(nfsd_user_namespace(rqstp), tmp1);
1342fdd6bd2SChuck Lever 		if (gid_valid(iap->ia_gid))
1352fdd6bd2SChuck Lever 			iap->ia_valid |= ATTR_GID;
1362fdd6bd2SChuck Lever 	}
1372fdd6bd2SChuck Lever 
1382fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1392fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1) {
1402fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_SIZE;
1412fdd6bd2SChuck Lever 		iap->ia_size = tmp1;
1422fdd6bd2SChuck Lever 	}
1432fdd6bd2SChuck Lever 
1442fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1452fdd6bd2SChuck Lever 	tmp2 = be32_to_cpup(p++);
1462fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
1472fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
1482fdd6bd2SChuck Lever 		iap->ia_atime.tv_sec = tmp1;
1492fdd6bd2SChuck Lever 		iap->ia_atime.tv_nsec = tmp2 * NSEC_PER_USEC;
1502fdd6bd2SChuck Lever 	}
1512fdd6bd2SChuck Lever 
1522fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1532fdd6bd2SChuck Lever 	tmp2 = be32_to_cpup(p++);
1542fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
1552fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
1562fdd6bd2SChuck Lever 		iap->ia_mtime.tv_sec = tmp1;
1572fdd6bd2SChuck Lever 		iap->ia_mtime.tv_nsec = tmp2 * NSEC_PER_USEC;
1582fdd6bd2SChuck Lever 		/*
1592fdd6bd2SChuck Lever 		 * Passing the invalid value useconds=1000000 for mtime
1602fdd6bd2SChuck Lever 		 * is a Sun convention for "set both mtime and atime to
1612fdd6bd2SChuck Lever 		 * current server time".  It's needed to make permissions
1622fdd6bd2SChuck Lever 		 * checks for the "touch" program across v2 mounts to
1632fdd6bd2SChuck Lever 		 * Solaris and Irix boxes work correctly. See description of
1642fdd6bd2SChuck Lever 		 * sattr in section 6.1 of "NFS Illustrated" by
1652fdd6bd2SChuck Lever 		 * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
1662fdd6bd2SChuck Lever 		 */
1672fdd6bd2SChuck Lever 		if (tmp2 == 1000000)
1682fdd6bd2SChuck Lever 			iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET);
1692fdd6bd2SChuck Lever 	}
1702fdd6bd2SChuck Lever 
1712fdd6bd2SChuck Lever 	return true;
1722fdd6bd2SChuck Lever }
1732fdd6bd2SChuck Lever 
174131a21c2SAl Viro static __be32 *
175131a21c2SAl Viro encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
176a334de28SDavid Shaw 	     struct kstat *stat)
1771da177e4SLinus Torvalds {
178e45d1a18STrond Myklebust 	struct user_namespace *userns = nfsd_user_namespace(rqstp);
1791da177e4SLinus Torvalds 	struct dentry	*dentry = fhp->fh_dentry;
1801da177e4SLinus Torvalds 	int type;
18195582b00SDeepa Dinamani 	struct timespec64 time;
182af6a4e28SNeilBrown 	u32 f;
1831da177e4SLinus Torvalds 
184a334de28SDavid Shaw 	type = (stat->mode & S_IFMT);
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds 	*p++ = htonl(nfs_ftypes[type >> 12]);
187082f31a2SJ. Bruce Fields 	*p++ = htonl((u32) stat->mode);
188a334de28SDavid Shaw 	*p++ = htonl((u32) stat->nlink);
189e45d1a18STrond Myklebust 	*p++ = htonl((u32) from_kuid_munged(userns, stat->uid));
190e45d1a18STrond Myklebust 	*p++ = htonl((u32) from_kgid_munged(userns, stat->gid));
1911da177e4SLinus Torvalds 
192a334de28SDavid Shaw 	if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) {
1931da177e4SLinus Torvalds 		*p++ = htonl(NFS_MAXPATHLEN);
1941da177e4SLinus Torvalds 	} else {
195a334de28SDavid Shaw 		*p++ = htonl((u32) stat->size);
1961da177e4SLinus Torvalds 	}
197a334de28SDavid Shaw 	*p++ = htonl((u32) stat->blksize);
1981da177e4SLinus Torvalds 	if (S_ISCHR(type) || S_ISBLK(type))
199a334de28SDavid Shaw 		*p++ = htonl(new_encode_dev(stat->rdev));
2001da177e4SLinus Torvalds 	else
2011da177e4SLinus Torvalds 		*p++ = htonl(0xffffffff);
202a334de28SDavid Shaw 	*p++ = htonl((u32) stat->blocks);
203af6a4e28SNeilBrown 	switch (fsid_source(fhp)) {
204af6a4e28SNeilBrown 	default:
205af6a4e28SNeilBrown 	case FSIDSOURCE_DEV:
206a334de28SDavid Shaw 		*p++ = htonl(new_encode_dev(stat->dev));
207af6a4e28SNeilBrown 		break;
208af6a4e28SNeilBrown 	case FSIDSOURCE_FSID:
209af6a4e28SNeilBrown 		*p++ = htonl((u32) fhp->fh_export->ex_fsid);
210af6a4e28SNeilBrown 		break;
211af6a4e28SNeilBrown 	case FSIDSOURCE_UUID:
212af6a4e28SNeilBrown 		f = ((u32*)fhp->fh_export->ex_uuid)[0];
213af6a4e28SNeilBrown 		f ^= ((u32*)fhp->fh_export->ex_uuid)[1];
214af6a4e28SNeilBrown 		f ^= ((u32*)fhp->fh_export->ex_uuid)[2];
215af6a4e28SNeilBrown 		f ^= ((u32*)fhp->fh_export->ex_uuid)[3];
216af6a4e28SNeilBrown 		*p++ = htonl(f);
217af6a4e28SNeilBrown 		break;
218af6a4e28SNeilBrown 	}
219a334de28SDavid Shaw 	*p++ = htonl((u32) stat->ino);
220a334de28SDavid Shaw 	*p++ = htonl((u32) stat->atime.tv_sec);
221a334de28SDavid Shaw 	*p++ = htonl(stat->atime.tv_nsec ? stat->atime.tv_nsec / 1000 : 0);
22276c47948SAmir Goldstein 	time = stat->mtime;
2232b0143b5SDavid Howells 	lease_get_mtime(d_inode(dentry), &time);
2241da177e4SLinus Torvalds 	*p++ = htonl((u32) time.tv_sec);
2251da177e4SLinus Torvalds 	*p++ = htonl(time.tv_nsec ? time.tv_nsec / 1000 : 0);
226a334de28SDavid Shaw 	*p++ = htonl((u32) stat->ctime.tv_sec);
227a334de28SDavid Shaw 	*p++ = htonl(stat->ctime.tv_nsec ? stat->ctime.tv_nsec / 1000 : 0);
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds 	return p;
2301da177e4SLinus Torvalds }
2311da177e4SLinus Torvalds 
232a257cdd0SAndreas Gruenbacher /* Helper function for NFSv2 ACL code */
2334f4a4fadSJ. Bruce Fields __be32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, struct kstat *stat)
234a257cdd0SAndreas Gruenbacher {
2354f4a4fadSJ. Bruce Fields 	return encode_fattr(rqstp, p, fhp, stat);
236a257cdd0SAndreas Gruenbacher }
2371da177e4SLinus Torvalds 
2381da177e4SLinus Torvalds /*
2391da177e4SLinus Torvalds  * XDR decode functions
2401da177e4SLinus Torvalds  */
2411da177e4SLinus Torvalds 
2421da177e4SLinus Torvalds int
243ebcd8e8bSChuck Lever nfssvc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p)
2441da177e4SLinus Torvalds {
245ebcd8e8bSChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
246026fec7eSChristoph Hellwig 	struct nfsd_fhandle *args = rqstp->rq_argp;
247026fec7eSChristoph Hellwig 
248ebcd8e8bSChuck Lever 	return svcxdr_decode_fhandle(xdr, &args->fh);
2491da177e4SLinus Torvalds }
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds int
252026fec7eSChristoph Hellwig nfssvc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p)
2531da177e4SLinus Torvalds {
2542fdd6bd2SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
255026fec7eSChristoph Hellwig 	struct nfsd_sattrargs *args = rqstp->rq_argp;
256026fec7eSChristoph Hellwig 
2572fdd6bd2SChuck Lever 	return svcxdr_decode_fhandle(xdr, &args->fh) &&
2582fdd6bd2SChuck Lever 		svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
2591da177e4SLinus Torvalds }
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds int
262026fec7eSChristoph Hellwig nfssvc_decode_diropargs(struct svc_rqst *rqstp, __be32 *p)
2631da177e4SLinus Torvalds {
2646d742c18SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
265026fec7eSChristoph Hellwig 	struct nfsd_diropargs *args = rqstp->rq_argp;
266026fec7eSChristoph Hellwig 
2676d742c18SChuck Lever 	return svcxdr_decode_diropargs(xdr, &args->fh, &args->name, &args->len);
2681da177e4SLinus Torvalds }
2691da177e4SLinus Torvalds 
2701da177e4SLinus Torvalds int
271026fec7eSChristoph Hellwig nfssvc_decode_readargs(struct svc_rqst *rqstp, __be32 *p)
2721da177e4SLinus Torvalds {
2738c293ef9SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
274026fec7eSChristoph Hellwig 	struct nfsd_readargs *args = rqstp->rq_argp;
2758c293ef9SChuck Lever 	u32 totalcount;
2768c293ef9SChuck Lever 
2778c293ef9SChuck Lever 	if (!svcxdr_decode_fhandle(xdr, &args->fh))
2788c293ef9SChuck Lever 		return 0;
2798c293ef9SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->offset) < 0)
2808c293ef9SChuck Lever 		return 0;
2818c293ef9SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->count) < 0)
2828c293ef9SChuck Lever 		return 0;
2838c293ef9SChuck Lever 	/* totalcount is ignored */
2848c293ef9SChuck Lever 	if (xdr_stream_decode_u32(xdr, &totalcount) < 0)
2851da177e4SLinus Torvalds 		return 0;
2861da177e4SLinus Torvalds 
2878c293ef9SChuck Lever 	return 1;
2881da177e4SLinus Torvalds }
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds int
291026fec7eSChristoph Hellwig nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p)
2921da177e4SLinus Torvalds {
293a51b5b73SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
294026fec7eSChristoph Hellwig 	struct nfsd_writeargs *args = rqstp->rq_argp;
295db44bac4SJ. Bruce Fields 	struct kvec *head = rqstp->rq_arg.head;
296a51b5b73SChuck Lever 	struct kvec *tail = rqstp->rq_arg.tail;
297a51b5b73SChuck Lever 	u32 beginoffset, totalcount;
298a51b5b73SChuck Lever 	size_t remaining;
299f34b9568SPeter Staubach 
300a51b5b73SChuck Lever 	if (!svcxdr_decode_fhandle(xdr, &args->fh))
301a51b5b73SChuck Lever 		return 0;
302a51b5b73SChuck Lever 	/* beginoffset is ignored */
303a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &beginoffset) < 0)
304a51b5b73SChuck Lever 		return 0;
305a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->offset) < 0)
306a51b5b73SChuck Lever 		return 0;
307a51b5b73SChuck Lever 	/* totalcount is ignored */
308a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &totalcount) < 0)
3091da177e4SLinus Torvalds 		return 0;
3101da177e4SLinus Torvalds 
311a51b5b73SChuck Lever 	/* opaque data */
312a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->len) < 0)
313f34b9568SPeter Staubach 		return 0;
314a51b5b73SChuck Lever 	if (args->len > NFSSVC_MAXBLKSIZE_V2)
31513bf9fbfSJ. Bruce Fields 		return 0;
316a51b5b73SChuck Lever 	remaining = head->iov_len + rqstp->rq_arg.page_len + tail->iov_len;
317a51b5b73SChuck Lever 	remaining -= xdr_stream_pos(xdr);
318a51b5b73SChuck Lever 	if (remaining < xdr_align_size(args->len))
319f34b9568SPeter Staubach 		return 0;
320a51b5b73SChuck Lever 	args->first.iov_base = xdr->p;
321a51b5b73SChuck Lever 	args->first.iov_len = head->iov_len - xdr_stream_pos(xdr);
322f34b9568SPeter Staubach 
323f34b9568SPeter Staubach 	return 1;
3241da177e4SLinus Torvalds }
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds int
327026fec7eSChristoph Hellwig nfssvc_decode_createargs(struct svc_rqst *rqstp, __be32 *p)
3281da177e4SLinus Torvalds {
3297dcf65b9SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
330026fec7eSChristoph Hellwig 	struct nfsd_createargs *args = rqstp->rq_argp;
331026fec7eSChristoph Hellwig 
3327dcf65b9SChuck Lever 	return svcxdr_decode_diropargs(xdr, &args->fh,
3337dcf65b9SChuck Lever 				       &args->name, &args->len) &&
3347dcf65b9SChuck Lever 		svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
3351da177e4SLinus Torvalds }
3361da177e4SLinus Torvalds 
3371da177e4SLinus Torvalds int
338026fec7eSChristoph Hellwig nfssvc_decode_renameargs(struct svc_rqst *rqstp, __be32 *p)
3391da177e4SLinus Torvalds {
34062aa557eSChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
341026fec7eSChristoph Hellwig 	struct nfsd_renameargs *args = rqstp->rq_argp;
342026fec7eSChristoph Hellwig 
34362aa557eSChuck Lever 	return svcxdr_decode_diropargs(xdr, &args->ffh,
34462aa557eSChuck Lever 				       &args->fname, &args->flen) &&
34562aa557eSChuck Lever 		svcxdr_decode_diropargs(xdr, &args->tfh,
34662aa557eSChuck Lever 					&args->tname, &args->tlen);
3471da177e4SLinus Torvalds }
3481da177e4SLinus Torvalds 
3491da177e4SLinus Torvalds int
350026fec7eSChristoph Hellwig nfssvc_decode_linkargs(struct svc_rqst *rqstp, __be32 *p)
3511da177e4SLinus Torvalds {
35277edcdf9SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
353026fec7eSChristoph Hellwig 	struct nfsd_linkargs *args = rqstp->rq_argp;
354026fec7eSChristoph Hellwig 
35577edcdf9SChuck Lever 	return svcxdr_decode_fhandle(xdr, &args->ffh) &&
35677edcdf9SChuck Lever 		svcxdr_decode_diropargs(xdr, &args->tfh,
35777edcdf9SChuck Lever 					&args->tname, &args->tlen);
3581da177e4SLinus Torvalds }
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds int
361026fec7eSChristoph Hellwig nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
3621da177e4SLinus Torvalds {
363*09f75a53SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
364026fec7eSChristoph Hellwig 	struct nfsd_symlinkargs *args = rqstp->rq_argp;
365*09f75a53SChuck Lever 	struct kvec *head = rqstp->rq_arg.head;
366026fec7eSChristoph Hellwig 
367*09f75a53SChuck Lever 	if (!svcxdr_decode_diropargs(xdr, &args->ffh, &args->fname, &args->flen))
3681da177e4SLinus Torvalds 		return 0;
369*09f75a53SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->tlen) < 0)
370*09f75a53SChuck Lever 		return 0;
37138a70315SChuck Lever 	if (args->tlen == 0)
37238a70315SChuck Lever 		return 0;
37338a70315SChuck Lever 
374*09f75a53SChuck Lever 	args->first.iov_len = head->iov_len - xdr_stream_pos(xdr);
375*09f75a53SChuck Lever 	args->first.iov_base = xdr_inline_decode(xdr, args->tlen);
376*09f75a53SChuck Lever 	if (!args->first.iov_base)
37738a70315SChuck Lever 		return 0;
378*09f75a53SChuck Lever 	return svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
3791da177e4SLinus Torvalds }
3801da177e4SLinus Torvalds 
3811da177e4SLinus Torvalds int
382026fec7eSChristoph Hellwig nfssvc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p)
3831da177e4SLinus Torvalds {
3848688361aSChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
385026fec7eSChristoph Hellwig 	struct nfsd_readdirargs *args = rqstp->rq_argp;
386026fec7eSChristoph Hellwig 
3878688361aSChuck Lever 	if (!svcxdr_decode_fhandle(xdr, &args->fh))
3881da177e4SLinus Torvalds 		return 0;
3898688361aSChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->cookie) < 0)
3908688361aSChuck Lever 		return 0;
3918688361aSChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->count) < 0)
3928688361aSChuck Lever 		return 0;
3931da177e4SLinus Torvalds 
3948688361aSChuck Lever 	return 1;
3951da177e4SLinus Torvalds }
3961da177e4SLinus Torvalds 
3971da177e4SLinus Torvalds /*
3981da177e4SLinus Torvalds  * XDR encode functions
3991da177e4SLinus Torvalds  */
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds int
402cc028a10SChuck Lever nfssvc_encode_stat(struct svc_rqst *rqstp, __be32 *p)
403cc028a10SChuck Lever {
404cc028a10SChuck Lever 	struct nfsd_stat *resp = rqstp->rq_resp;
405cc028a10SChuck Lever 
406cc028a10SChuck Lever 	*p++ = resp->status;
407cc028a10SChuck Lever 	return xdr_ressize_check(rqstp, p);
408cc028a10SChuck Lever }
409cc028a10SChuck Lever 
410cc028a10SChuck Lever int
41163f8de37SChristoph Hellwig nfssvc_encode_attrstat(struct svc_rqst *rqstp, __be32 *p)
4121da177e4SLinus Torvalds {
41363f8de37SChristoph Hellwig 	struct nfsd_attrstat *resp = rqstp->rq_resp;
41463f8de37SChristoph Hellwig 
415cc028a10SChuck Lever 	*p++ = resp->status;
416f0af2210SChuck Lever 	if (resp->status != nfs_ok)
417cc028a10SChuck Lever 		goto out;
418a334de28SDavid Shaw 	p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
419cc028a10SChuck Lever out:
4201da177e4SLinus Torvalds 	return xdr_ressize_check(rqstp, p);
4211da177e4SLinus Torvalds }
4221da177e4SLinus Torvalds 
4231da177e4SLinus Torvalds int
42463f8de37SChristoph Hellwig nfssvc_encode_diropres(struct svc_rqst *rqstp, __be32 *p)
4251da177e4SLinus Torvalds {
42663f8de37SChristoph Hellwig 	struct nfsd_diropres *resp = rqstp->rq_resp;
42763f8de37SChristoph Hellwig 
428cc028a10SChuck Lever 	*p++ = resp->status;
429f0af2210SChuck Lever 	if (resp->status != nfs_ok)
430cc028a10SChuck Lever 		goto out;
4311da177e4SLinus Torvalds 	p = encode_fh(p, &resp->fh);
432a334de28SDavid Shaw 	p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
433cc028a10SChuck Lever out:
4341da177e4SLinus Torvalds 	return xdr_ressize_check(rqstp, p);
4351da177e4SLinus Torvalds }
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds int
43863f8de37SChristoph Hellwig nfssvc_encode_readlinkres(struct svc_rqst *rqstp, __be32 *p)
4391da177e4SLinus Torvalds {
44063f8de37SChristoph Hellwig 	struct nfsd_readlinkres *resp = rqstp->rq_resp;
44176e5492bSChuck Lever 	struct kvec *head = rqstp->rq_res.head;
44263f8de37SChristoph Hellwig 
443cc028a10SChuck Lever 	*p++ = resp->status;
444f0af2210SChuck Lever 	if (resp->status != nfs_ok)
445f0af2210SChuck Lever 		return xdr_ressize_check(rqstp, p);
446f0af2210SChuck Lever 
4471da177e4SLinus Torvalds 	*p++ = htonl(resp->len);
4481da177e4SLinus Torvalds 	xdr_ressize_check(rqstp, p);
4491da177e4SLinus Torvalds 	rqstp->rq_res.page_len = resp->len;
4501da177e4SLinus Torvalds 	if (resp->len & 3) {
4511da177e4SLinus Torvalds 		/* need to pad the tail */
4521da177e4SLinus Torvalds 		rqstp->rq_res.tail[0].iov_base = p;
4531da177e4SLinus Torvalds 		*p = 0;
4541da177e4SLinus Torvalds 		rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3);
4551da177e4SLinus Torvalds 	}
45676e5492bSChuck Lever 	if (svc_encode_result_payload(rqstp, head->iov_len, resp->len))
45776e5492bSChuck Lever 		return 0;
4581da177e4SLinus Torvalds 	return 1;
4591da177e4SLinus Torvalds }
4601da177e4SLinus Torvalds 
4611da177e4SLinus Torvalds int
46263f8de37SChristoph Hellwig nfssvc_encode_readres(struct svc_rqst *rqstp, __be32 *p)
4631da177e4SLinus Torvalds {
46463f8de37SChristoph Hellwig 	struct nfsd_readres *resp = rqstp->rq_resp;
46576e5492bSChuck Lever 	struct kvec *head = rqstp->rq_res.head;
46663f8de37SChristoph Hellwig 
467cc028a10SChuck Lever 	*p++ = resp->status;
468f0af2210SChuck Lever 	if (resp->status != nfs_ok)
469f0af2210SChuck Lever 		return xdr_ressize_check(rqstp, p);
470f0af2210SChuck Lever 
471a334de28SDavid Shaw 	p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
4721da177e4SLinus Torvalds 	*p++ = htonl(resp->count);
4731da177e4SLinus Torvalds 	xdr_ressize_check(rqstp, p);
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds 	/* now update rqstp->rq_res to reflect data as well */
4761da177e4SLinus Torvalds 	rqstp->rq_res.page_len = resp->count;
4771da177e4SLinus Torvalds 	if (resp->count & 3) {
4781da177e4SLinus Torvalds 		/* need to pad the tail */
4791da177e4SLinus Torvalds 		rqstp->rq_res.tail[0].iov_base = p;
4801da177e4SLinus Torvalds 		*p = 0;
4811da177e4SLinus Torvalds 		rqstp->rq_res.tail[0].iov_len = 4 - (resp->count&3);
4821da177e4SLinus Torvalds 	}
48376e5492bSChuck Lever 	if (svc_encode_result_payload(rqstp, head->iov_len, resp->count))
48476e5492bSChuck Lever 		return 0;
4851da177e4SLinus Torvalds 	return 1;
4861da177e4SLinus Torvalds }
4871da177e4SLinus Torvalds 
4881da177e4SLinus Torvalds int
48963f8de37SChristoph Hellwig nfssvc_encode_readdirres(struct svc_rqst *rqstp, __be32 *p)
4901da177e4SLinus Torvalds {
49163f8de37SChristoph Hellwig 	struct nfsd_readdirres *resp = rqstp->rq_resp;
49263f8de37SChristoph Hellwig 
493cc028a10SChuck Lever 	*p++ = resp->status;
494f0af2210SChuck Lever 	if (resp->status != nfs_ok)
495f0af2210SChuck Lever 		return xdr_ressize_check(rqstp, p);
496f0af2210SChuck Lever 
4971da177e4SLinus Torvalds 	xdr_ressize_check(rqstp, p);
4981da177e4SLinus Torvalds 	p = resp->buffer;
4991da177e4SLinus Torvalds 	*p++ = 0;			/* no more entries */
5001da177e4SLinus Torvalds 	*p++ = htonl((resp->common.err == nfserr_eof));
5011da177e4SLinus Torvalds 	rqstp->rq_res.page_len = (((unsigned long)p-1) & ~PAGE_MASK)+1;
5021da177e4SLinus Torvalds 
5031da177e4SLinus Torvalds 	return 1;
5041da177e4SLinus Torvalds }
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds int
50763f8de37SChristoph Hellwig nfssvc_encode_statfsres(struct svc_rqst *rqstp, __be32 *p)
5081da177e4SLinus Torvalds {
50963f8de37SChristoph Hellwig 	struct nfsd_statfsres *resp = rqstp->rq_resp;
5101da177e4SLinus Torvalds 	struct kstatfs	*stat = &resp->stats;
5111da177e4SLinus Torvalds 
512cc028a10SChuck Lever 	*p++ = resp->status;
513f0af2210SChuck Lever 	if (resp->status != nfs_ok)
514f0af2210SChuck Lever 		return xdr_ressize_check(rqstp, p);
515f0af2210SChuck Lever 
5167adae489SGreg Banks 	*p++ = htonl(NFSSVC_MAXBLKSIZE_V2);	/* max transfer size */
5171da177e4SLinus Torvalds 	*p++ = htonl(stat->f_bsize);
5181da177e4SLinus Torvalds 	*p++ = htonl(stat->f_blocks);
5191da177e4SLinus Torvalds 	*p++ = htonl(stat->f_bfree);
5201da177e4SLinus Torvalds 	*p++ = htonl(stat->f_bavail);
5211da177e4SLinus Torvalds 	return xdr_ressize_check(rqstp, p);
5221da177e4SLinus Torvalds }
5231da177e4SLinus Torvalds 
5241da177e4SLinus Torvalds int
525a0ad13efSNeilBrown nfssvc_encode_entry(void *ccdv, const char *name,
526a0ad13efSNeilBrown 		    int namlen, loff_t offset, u64 ino, unsigned int d_type)
5271da177e4SLinus Torvalds {
528a0ad13efSNeilBrown 	struct readdir_cd *ccd = ccdv;
5291da177e4SLinus Torvalds 	struct nfsd_readdirres *cd = container_of(ccd, struct nfsd_readdirres, common);
530131a21c2SAl Viro 	__be32	*p = cd->buffer;
5311da177e4SLinus Torvalds 	int	buflen, slen;
5321da177e4SLinus Torvalds 
5331da177e4SLinus Torvalds 	/*
5341da177e4SLinus Torvalds 	dprintk("nfsd: entry(%.*s off %ld ino %ld)\n",
5351da177e4SLinus Torvalds 			namlen, name, offset, ino);
5361da177e4SLinus Torvalds 	 */
5371da177e4SLinus Torvalds 
5381da177e4SLinus Torvalds 	if (offset > ~((u32) 0)) {
5391da177e4SLinus Torvalds 		cd->common.err = nfserr_fbig;
5401da177e4SLinus Torvalds 		return -EINVAL;
5411da177e4SLinus Torvalds 	}
5421da177e4SLinus Torvalds 	if (cd->offset)
5431da177e4SLinus Torvalds 		*cd->offset = htonl(offset);
5441da177e4SLinus Torvalds 
5453c7aa15dSKinglong Mee 	/* truncate filename */
5463c7aa15dSKinglong Mee 	namlen = min(namlen, NFS2_MAXNAMLEN);
5471da177e4SLinus Torvalds 	slen = XDR_QUADLEN(namlen);
5483c7aa15dSKinglong Mee 
5491da177e4SLinus Torvalds 	if ((buflen = cd->buflen - slen - 4) < 0) {
5501da177e4SLinus Torvalds 		cd->common.err = nfserr_toosmall;
5511da177e4SLinus Torvalds 		return -EINVAL;
5521da177e4SLinus Torvalds 	}
55340ee5dc6SPeter Staubach 	if (ino > ~((u32) 0)) {
55440ee5dc6SPeter Staubach 		cd->common.err = nfserr_fbig;
55540ee5dc6SPeter Staubach 		return -EINVAL;
55640ee5dc6SPeter Staubach 	}
5571da177e4SLinus Torvalds 	*p++ = xdr_one;				/* mark entry present */
5581da177e4SLinus Torvalds 	*p++ = htonl((u32) ino);		/* file id */
5591da177e4SLinus Torvalds 	p    = xdr_encode_array(p, name, namlen);/* name length & name */
5601da177e4SLinus Torvalds 	cd->offset = p;			/* remember pointer */
561131a21c2SAl Viro 	*p++ = htonl(~0U);		/* offset of next entry */
5621da177e4SLinus Torvalds 
5631da177e4SLinus Torvalds 	cd->buflen = buflen;
5641da177e4SLinus Torvalds 	cd->buffer = p;
5651da177e4SLinus Torvalds 	cd->common.err = nfs_ok;
5661da177e4SLinus Torvalds 	return 0;
5671da177e4SLinus Torvalds }
5681da177e4SLinus Torvalds 
5691da177e4SLinus Torvalds /*
5701da177e4SLinus Torvalds  * XDR release functions
5711da177e4SLinus Torvalds  */
5721841b9b6SChuck Lever void nfssvc_release_attrstat(struct svc_rqst *rqstp)
5731da177e4SLinus Torvalds {
5741841b9b6SChuck Lever 	struct nfsd_attrstat *resp = rqstp->rq_resp;
5751841b9b6SChuck Lever 
5761841b9b6SChuck Lever 	fh_put(&resp->fh);
5771841b9b6SChuck Lever }
5781841b9b6SChuck Lever 
5791841b9b6SChuck Lever void nfssvc_release_diropres(struct svc_rqst *rqstp)
5801841b9b6SChuck Lever {
5811841b9b6SChuck Lever 	struct nfsd_diropres *resp = rqstp->rq_resp;
5821841b9b6SChuck Lever 
5831841b9b6SChuck Lever 	fh_put(&resp->fh);
5841841b9b6SChuck Lever }
5851841b9b6SChuck Lever 
5861841b9b6SChuck Lever void nfssvc_release_readres(struct svc_rqst *rqstp)
5871841b9b6SChuck Lever {
5881841b9b6SChuck Lever 	struct nfsd_readres *resp = rqstp->rq_resp;
5898537488bSChristoph Hellwig 
5901da177e4SLinus Torvalds 	fh_put(&resp->fh);
5911da177e4SLinus Torvalds }
592