xref: /openbmc/linux/fs/nfsd/nfsxdr.c (revision 130e2054d4a652a2bd79fb1557ddcd19c053cb37)
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 /*
131da177e4SLinus Torvalds  * Mapping of S_IF* types to NFS file types
141da177e4SLinus Torvalds  */
1592b54a4fSChuck Lever static const u32 nfs_ftypes[] = {
161da177e4SLinus Torvalds 	NFNON,  NFCHR,  NFCHR, NFBAD,
171da177e4SLinus Torvalds 	NFDIR,  NFBAD,  NFBLK, NFBAD,
181da177e4SLinus Torvalds 	NFREG,  NFBAD,  NFLNK, NFBAD,
191da177e4SLinus Torvalds 	NFSOCK, NFBAD,  NFLNK, NFBAD,
201da177e4SLinus Torvalds };
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds /*
24ebcd8e8bSChuck Lever  * Basic NFSv2 data types (RFC 1094 Section 2.3)
251da177e4SLinus Torvalds  */
26ebcd8e8bSChuck Lever 
27f8cba473SChuck Lever /**
28f8cba473SChuck Lever  * svcxdr_encode_stat - Encode an NFSv2 status code
29f8cba473SChuck Lever  * @xdr: XDR stream
30f8cba473SChuck Lever  * @status: status value to encode
31f8cba473SChuck Lever  *
32f8cba473SChuck Lever  * Return values:
33f8cba473SChuck Lever  *   %false: Send buffer space was exhausted
34f8cba473SChuck Lever  *   %true: Success
35f8cba473SChuck Lever  */
36f8cba473SChuck Lever bool
37a887eaedSChuck Lever svcxdr_encode_stat(struct xdr_stream *xdr, __be32 status)
38a887eaedSChuck Lever {
39a887eaedSChuck Lever 	__be32 *p;
40a887eaedSChuck Lever 
41a887eaedSChuck Lever 	p = xdr_reserve_space(xdr, sizeof(status));
42a887eaedSChuck Lever 	if (!p)
43a887eaedSChuck Lever 		return false;
44a887eaedSChuck Lever 	*p = status;
45a887eaedSChuck Lever 
46a887eaedSChuck Lever 	return true;
47a887eaedSChuck Lever }
48a887eaedSChuck Lever 
49635a45d3SChuck Lever /**
50635a45d3SChuck Lever  * svcxdr_decode_fhandle - Decode an NFSv2 file handle
51635a45d3SChuck Lever  * @xdr: XDR stream positioned at an encoded NFSv2 FH
52635a45d3SChuck Lever  * @fhp: OUT: filled-in server file handle
53635a45d3SChuck Lever  *
54635a45d3SChuck Lever  * Return values:
55635a45d3SChuck Lever  *  %false: The encoded file handle was not valid
56635a45d3SChuck Lever  *  %true: @fhp has been initialized
57635a45d3SChuck Lever  */
58635a45d3SChuck Lever bool
59ebcd8e8bSChuck Lever svcxdr_decode_fhandle(struct xdr_stream *xdr, struct svc_fh *fhp)
60ebcd8e8bSChuck Lever {
61ebcd8e8bSChuck Lever 	__be32 *p;
62ebcd8e8bSChuck Lever 
63ebcd8e8bSChuck Lever 	p = xdr_inline_decode(xdr, NFS_FHSIZE);
64ebcd8e8bSChuck Lever 	if (!p)
65ebcd8e8bSChuck Lever 		return false;
66ebcd8e8bSChuck Lever 	fh_init(fhp, NFS_FHSIZE);
67d8b26071SNeilBrown 	memcpy(&fhp->fh_handle.fh_raw, p, NFS_FHSIZE);
68ebcd8e8bSChuck Lever 	fhp->fh_handle.fh_size = NFS_FHSIZE;
69ebcd8e8bSChuck Lever 
70ebcd8e8bSChuck Lever 	return true;
71ebcd8e8bSChuck Lever }
72ebcd8e8bSChuck Lever 
73e3b4ef22SChuck Lever static bool
74e3b4ef22SChuck Lever svcxdr_encode_fhandle(struct xdr_stream *xdr, const struct svc_fh *fhp)
751da177e4SLinus Torvalds {
76e3b4ef22SChuck Lever 	__be32 *p;
77e3b4ef22SChuck Lever 
78e3b4ef22SChuck Lever 	p = xdr_reserve_space(xdr, NFS_FHSIZE);
79e3b4ef22SChuck Lever 	if (!p)
80e3b4ef22SChuck Lever 		return false;
81d8b26071SNeilBrown 	memcpy(p, &fhp->fh_handle.fh_raw, NFS_FHSIZE);
82e3b4ef22SChuck Lever 
83e3b4ef22SChuck Lever 	return true;
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds 
8692b54a4fSChuck Lever static __be32 *
8792b54a4fSChuck Lever encode_timeval(__be32 *p, const struct timespec64 *time)
8892b54a4fSChuck Lever {
8992b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32)time->tv_sec);
9092b54a4fSChuck Lever 	if (time->tv_nsec)
9192b54a4fSChuck Lever 		*p++ = cpu_to_be32(time->tv_nsec / NSEC_PER_USEC);
9292b54a4fSChuck Lever 	else
9392b54a4fSChuck Lever 		*p++ = xdr_zero;
9492b54a4fSChuck Lever 	return p;
9592b54a4fSChuck Lever }
9692b54a4fSChuck Lever 
976d742c18SChuck Lever static bool
986d742c18SChuck Lever svcxdr_decode_filename(struct xdr_stream *xdr, char **name, unsigned int *len)
996d742c18SChuck Lever {
1006d742c18SChuck Lever 	u32 size, i;
1016d742c18SChuck Lever 	__be32 *p;
1026d742c18SChuck Lever 	char *c;
1036d742c18SChuck Lever 
1046d742c18SChuck Lever 	if (xdr_stream_decode_u32(xdr, &size) < 0)
1056d742c18SChuck Lever 		return false;
1066d742c18SChuck Lever 	if (size == 0 || size > NFS_MAXNAMLEN)
1076d742c18SChuck Lever 		return false;
1086d742c18SChuck Lever 	p = xdr_inline_decode(xdr, size);
1096d742c18SChuck Lever 	if (!p)
1106d742c18SChuck Lever 		return false;
1116d742c18SChuck Lever 
1126d742c18SChuck Lever 	*len = size;
1136d742c18SChuck Lever 	*name = (char *)p;
1146d742c18SChuck Lever 	for (i = 0, c = *name; i < size; i++, c++)
1156d742c18SChuck Lever 		if (*c == '\0' || *c == '/')
1166d742c18SChuck Lever 			return false;
1176d742c18SChuck Lever 
1186d742c18SChuck Lever 	return true;
1196d742c18SChuck Lever }
1206d742c18SChuck Lever 
1216d742c18SChuck Lever static bool
1226d742c18SChuck Lever svcxdr_decode_diropargs(struct xdr_stream *xdr, struct svc_fh *fhp,
1236d742c18SChuck Lever 			char **name, unsigned int *len)
1246d742c18SChuck Lever {
1256d742c18SChuck Lever 	return svcxdr_decode_fhandle(xdr, fhp) &&
1266d742c18SChuck Lever 		svcxdr_decode_filename(xdr, name, len);
1276d742c18SChuck Lever }
1286d742c18SChuck Lever 
1292fdd6bd2SChuck Lever static bool
1302fdd6bd2SChuck Lever svcxdr_decode_sattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
1312fdd6bd2SChuck Lever 		    struct iattr *iap)
1322fdd6bd2SChuck Lever {
1332fdd6bd2SChuck Lever 	u32 tmp1, tmp2;
1342fdd6bd2SChuck Lever 	__be32 *p;
1352fdd6bd2SChuck Lever 
1362fdd6bd2SChuck Lever 	p = xdr_inline_decode(xdr, XDR_UNIT * 8);
1372fdd6bd2SChuck Lever 	if (!p)
1382fdd6bd2SChuck Lever 		return false;
1392fdd6bd2SChuck Lever 
1402fdd6bd2SChuck Lever 	iap->ia_valid = 0;
1412fdd6bd2SChuck Lever 
1422fdd6bd2SChuck Lever 	/*
1432fdd6bd2SChuck Lever 	 * Some Sun clients put 0xffff in the mode field when they
1442fdd6bd2SChuck Lever 	 * mean 0xffffffff.
1452fdd6bd2SChuck Lever 	 */
1462fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1472fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1 && tmp1 != 0xffff) {
1482fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_MODE;
1492fdd6bd2SChuck Lever 		iap->ia_mode = tmp1;
1502fdd6bd2SChuck Lever 	}
1512fdd6bd2SChuck Lever 
1522fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1532fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1) {
1542fdd6bd2SChuck Lever 		iap->ia_uid = make_kuid(nfsd_user_namespace(rqstp), tmp1);
1552fdd6bd2SChuck Lever 		if (uid_valid(iap->ia_uid))
1562fdd6bd2SChuck Lever 			iap->ia_valid |= ATTR_UID;
1572fdd6bd2SChuck Lever 	}
1582fdd6bd2SChuck Lever 
1592fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1602fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1) {
1612fdd6bd2SChuck Lever 		iap->ia_gid = make_kgid(nfsd_user_namespace(rqstp), tmp1);
1622fdd6bd2SChuck Lever 		if (gid_valid(iap->ia_gid))
1632fdd6bd2SChuck Lever 			iap->ia_valid |= ATTR_GID;
1642fdd6bd2SChuck Lever 	}
1652fdd6bd2SChuck Lever 
1662fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1672fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1) {
1682fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_SIZE;
1692fdd6bd2SChuck Lever 		iap->ia_size = tmp1;
1702fdd6bd2SChuck Lever 	}
1712fdd6bd2SChuck Lever 
1722fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1732fdd6bd2SChuck Lever 	tmp2 = be32_to_cpup(p++);
1742fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
1752fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
1762fdd6bd2SChuck Lever 		iap->ia_atime.tv_sec = tmp1;
1772fdd6bd2SChuck Lever 		iap->ia_atime.tv_nsec = tmp2 * NSEC_PER_USEC;
1782fdd6bd2SChuck Lever 	}
1792fdd6bd2SChuck Lever 
1802fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1812fdd6bd2SChuck Lever 	tmp2 = be32_to_cpup(p++);
1822fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
1832fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
1842fdd6bd2SChuck Lever 		iap->ia_mtime.tv_sec = tmp1;
1852fdd6bd2SChuck Lever 		iap->ia_mtime.tv_nsec = tmp2 * NSEC_PER_USEC;
1862fdd6bd2SChuck Lever 		/*
1872fdd6bd2SChuck Lever 		 * Passing the invalid value useconds=1000000 for mtime
1882fdd6bd2SChuck Lever 		 * is a Sun convention for "set both mtime and atime to
1892fdd6bd2SChuck Lever 		 * current server time".  It's needed to make permissions
1902fdd6bd2SChuck Lever 		 * checks for the "touch" program across v2 mounts to
1912fdd6bd2SChuck Lever 		 * Solaris and Irix boxes work correctly. See description of
1922fdd6bd2SChuck Lever 		 * sattr in section 6.1 of "NFS Illustrated" by
1932fdd6bd2SChuck Lever 		 * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
1942fdd6bd2SChuck Lever 		 */
1952fdd6bd2SChuck Lever 		if (tmp2 == 1000000)
1962fdd6bd2SChuck Lever 			iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET);
1972fdd6bd2SChuck Lever 	}
1982fdd6bd2SChuck Lever 
1992fdd6bd2SChuck Lever 	return true;
2002fdd6bd2SChuck Lever }
2012fdd6bd2SChuck Lever 
202f8cba473SChuck Lever /**
203f8cba473SChuck Lever  * svcxdr_encode_fattr - Encode NFSv2 file attributes
204f8cba473SChuck Lever  * @rqstp: Context of a completed RPC transaction
205f8cba473SChuck Lever  * @xdr: XDR stream
206f8cba473SChuck Lever  * @fhp: File handle to encode
207f8cba473SChuck Lever  * @stat: Attributes to encode
208f8cba473SChuck Lever  *
209f8cba473SChuck Lever  * Return values:
210f8cba473SChuck Lever  *   %false: Send buffer space was exhausted
211f8cba473SChuck Lever  *   %true: Success
212f8cba473SChuck Lever  */
213f8cba473SChuck Lever bool
21492b54a4fSChuck Lever svcxdr_encode_fattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
21592b54a4fSChuck Lever 		    const struct svc_fh *fhp, const struct kstat *stat)
21692b54a4fSChuck Lever {
21792b54a4fSChuck Lever 	struct user_namespace *userns = nfsd_user_namespace(rqstp);
21892b54a4fSChuck Lever 	struct dentry *dentry = fhp->fh_dentry;
21992b54a4fSChuck Lever 	int type = stat->mode & S_IFMT;
22092b54a4fSChuck Lever 	struct timespec64 time;
22192b54a4fSChuck Lever 	__be32 *p;
22292b54a4fSChuck Lever 	u32 fsid;
22392b54a4fSChuck Lever 
22492b54a4fSChuck Lever 	p = xdr_reserve_space(xdr, XDR_UNIT * 17);
22592b54a4fSChuck Lever 	if (!p)
226e3b4ef22SChuck Lever 		return false;
22792b54a4fSChuck Lever 
22892b54a4fSChuck Lever 	*p++ = cpu_to_be32(nfs_ftypes[type >> 12]);
22992b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32)stat->mode);
23092b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32)stat->nlink);
23192b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32)from_kuid_munged(userns, stat->uid));
23292b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32)from_kgid_munged(userns, stat->gid));
23392b54a4fSChuck Lever 
23492b54a4fSChuck Lever 	if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN)
23592b54a4fSChuck Lever 		*p++ = cpu_to_be32(NFS_MAXPATHLEN);
23692b54a4fSChuck Lever 	else
23792b54a4fSChuck Lever 		*p++ = cpu_to_be32((u32) stat->size);
23892b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32) stat->blksize);
23992b54a4fSChuck Lever 	if (S_ISCHR(type) || S_ISBLK(type))
24092b54a4fSChuck Lever 		*p++ = cpu_to_be32(new_encode_dev(stat->rdev));
24192b54a4fSChuck Lever 	else
24292b54a4fSChuck Lever 		*p++ = cpu_to_be32(0xffffffff);
24392b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32)stat->blocks);
24492b54a4fSChuck Lever 
24592b54a4fSChuck Lever 	switch (fsid_source(fhp)) {
24692b54a4fSChuck Lever 	case FSIDSOURCE_FSID:
24792b54a4fSChuck Lever 		fsid = (u32)fhp->fh_export->ex_fsid;
24892b54a4fSChuck Lever 		break;
24992b54a4fSChuck Lever 	case FSIDSOURCE_UUID:
25092b54a4fSChuck Lever 		fsid = ((u32 *)fhp->fh_export->ex_uuid)[0];
25192b54a4fSChuck Lever 		fsid ^= ((u32 *)fhp->fh_export->ex_uuid)[1];
25292b54a4fSChuck Lever 		fsid ^= ((u32 *)fhp->fh_export->ex_uuid)[2];
25392b54a4fSChuck Lever 		fsid ^= ((u32 *)fhp->fh_export->ex_uuid)[3];
25492b54a4fSChuck Lever 		break;
25592b54a4fSChuck Lever 	default:
25692b54a4fSChuck Lever 		fsid = new_encode_dev(stat->dev);
25792b54a4fSChuck Lever 		break;
25892b54a4fSChuck Lever 	}
25992b54a4fSChuck Lever 	*p++ = cpu_to_be32(fsid);
26092b54a4fSChuck Lever 
26192b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32)stat->ino);
26292b54a4fSChuck Lever 	p = encode_timeval(p, &stat->atime);
26392b54a4fSChuck Lever 	time = stat->mtime;
26492b54a4fSChuck Lever 	lease_get_mtime(d_inode(dentry), &time);
26592b54a4fSChuck Lever 	p = encode_timeval(p, &time);
26692b54a4fSChuck Lever 	encode_timeval(p, &stat->ctime);
26792b54a4fSChuck Lever 
268e3b4ef22SChuck Lever 	return true;
26992b54a4fSChuck Lever }
27092b54a4fSChuck Lever 
2711da177e4SLinus Torvalds /*
2721da177e4SLinus Torvalds  * XDR decode functions
2731da177e4SLinus Torvalds  */
2741da177e4SLinus Torvalds 
275c44b31c2SChuck Lever bool
27616c66364SChuck Lever nfssvc_decode_fhandleargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
2771da177e4SLinus Torvalds {
278026fec7eSChristoph Hellwig 	struct nfsd_fhandle *args = rqstp->rq_argp;
279026fec7eSChristoph Hellwig 
280ebcd8e8bSChuck Lever 	return svcxdr_decode_fhandle(xdr, &args->fh);
2811da177e4SLinus Torvalds }
2821da177e4SLinus Torvalds 
283c44b31c2SChuck Lever bool
28416c66364SChuck Lever nfssvc_decode_sattrargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
2851da177e4SLinus Torvalds {
286026fec7eSChristoph Hellwig 	struct nfsd_sattrargs *args = rqstp->rq_argp;
287026fec7eSChristoph Hellwig 
2882fdd6bd2SChuck Lever 	return svcxdr_decode_fhandle(xdr, &args->fh) &&
2892fdd6bd2SChuck Lever 		svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
2901da177e4SLinus Torvalds }
2911da177e4SLinus Torvalds 
292c44b31c2SChuck Lever bool
29316c66364SChuck Lever nfssvc_decode_diropargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
2941da177e4SLinus Torvalds {
295026fec7eSChristoph Hellwig 	struct nfsd_diropargs *args = rqstp->rq_argp;
296026fec7eSChristoph Hellwig 
2976d742c18SChuck Lever 	return svcxdr_decode_diropargs(xdr, &args->fh, &args->name, &args->len);
2981da177e4SLinus Torvalds }
2991da177e4SLinus Torvalds 
300c44b31c2SChuck Lever bool
30116c66364SChuck Lever nfssvc_decode_readargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
3021da177e4SLinus Torvalds {
303026fec7eSChristoph Hellwig 	struct nfsd_readargs *args = rqstp->rq_argp;
3048c293ef9SChuck Lever 	u32 totalcount;
3058c293ef9SChuck Lever 
3068c293ef9SChuck Lever 	if (!svcxdr_decode_fhandle(xdr, &args->fh))
307c44b31c2SChuck Lever 		return false;
3088c293ef9SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->offset) < 0)
309c44b31c2SChuck Lever 		return false;
3108c293ef9SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->count) < 0)
311c44b31c2SChuck Lever 		return false;
3128c293ef9SChuck Lever 	/* totalcount is ignored */
3138c293ef9SChuck Lever 	if (xdr_stream_decode_u32(xdr, &totalcount) < 0)
314c44b31c2SChuck Lever 		return false;
3151da177e4SLinus Torvalds 
316c44b31c2SChuck Lever 	return true;
3171da177e4SLinus Torvalds }
3181da177e4SLinus Torvalds 
319c44b31c2SChuck Lever bool
32016c66364SChuck Lever nfssvc_decode_writeargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
3211da177e4SLinus Torvalds {
322026fec7eSChristoph Hellwig 	struct nfsd_writeargs *args = rqstp->rq_argp;
323a51b5b73SChuck Lever 	u32 beginoffset, totalcount;
324f34b9568SPeter Staubach 
325a51b5b73SChuck Lever 	if (!svcxdr_decode_fhandle(xdr, &args->fh))
326c44b31c2SChuck Lever 		return false;
327a51b5b73SChuck Lever 	/* beginoffset is ignored */
328a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &beginoffset) < 0)
329c44b31c2SChuck Lever 		return false;
330a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->offset) < 0)
331c44b31c2SChuck Lever 		return false;
332a51b5b73SChuck Lever 	/* totalcount is ignored */
333a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &totalcount) < 0)
334c44b31c2SChuck Lever 		return false;
3351da177e4SLinus Torvalds 
336a51b5b73SChuck Lever 	/* opaque data */
337a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->len) < 0)
338c44b31c2SChuck Lever 		return false;
339a51b5b73SChuck Lever 	if (args->len > NFSSVC_MAXBLKSIZE_V2)
340c44b31c2SChuck Lever 		return false;
341dae9a6caSChuck Lever 	if (!xdr_stream_subsegment(xdr, &args->payload, args->len))
342c44b31c2SChuck Lever 		return false;
343f34b9568SPeter Staubach 
344c44b31c2SChuck Lever 	return true;
3451da177e4SLinus Torvalds }
3461da177e4SLinus Torvalds 
347c44b31c2SChuck Lever bool
34816c66364SChuck Lever nfssvc_decode_createargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
3491da177e4SLinus Torvalds {
350026fec7eSChristoph Hellwig 	struct nfsd_createargs *args = rqstp->rq_argp;
351026fec7eSChristoph Hellwig 
3527dcf65b9SChuck Lever 	return svcxdr_decode_diropargs(xdr, &args->fh,
3537dcf65b9SChuck Lever 				       &args->name, &args->len) &&
3547dcf65b9SChuck Lever 		svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
3551da177e4SLinus Torvalds }
3561da177e4SLinus Torvalds 
357c44b31c2SChuck Lever bool
35816c66364SChuck Lever nfssvc_decode_renameargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
3591da177e4SLinus Torvalds {
360026fec7eSChristoph Hellwig 	struct nfsd_renameargs *args = rqstp->rq_argp;
361026fec7eSChristoph Hellwig 
36262aa557eSChuck Lever 	return svcxdr_decode_diropargs(xdr, &args->ffh,
36362aa557eSChuck Lever 				       &args->fname, &args->flen) &&
36462aa557eSChuck Lever 		svcxdr_decode_diropargs(xdr, &args->tfh,
36562aa557eSChuck Lever 					&args->tname, &args->tlen);
3661da177e4SLinus Torvalds }
3671da177e4SLinus Torvalds 
368c44b31c2SChuck Lever bool
36916c66364SChuck Lever nfssvc_decode_linkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
3701da177e4SLinus Torvalds {
371026fec7eSChristoph Hellwig 	struct nfsd_linkargs *args = rqstp->rq_argp;
372026fec7eSChristoph Hellwig 
37377edcdf9SChuck Lever 	return svcxdr_decode_fhandle(xdr, &args->ffh) &&
37477edcdf9SChuck Lever 		svcxdr_decode_diropargs(xdr, &args->tfh,
37577edcdf9SChuck Lever 					&args->tname, &args->tlen);
3761da177e4SLinus Torvalds }
3771da177e4SLinus Torvalds 
378c44b31c2SChuck Lever bool
37916c66364SChuck Lever nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
3801da177e4SLinus Torvalds {
381026fec7eSChristoph Hellwig 	struct nfsd_symlinkargs *args = rqstp->rq_argp;
38209f75a53SChuck Lever 	struct kvec *head = rqstp->rq_arg.head;
383026fec7eSChristoph Hellwig 
38409f75a53SChuck Lever 	if (!svcxdr_decode_diropargs(xdr, &args->ffh, &args->fname, &args->flen))
385c44b31c2SChuck Lever 		return false;
38609f75a53SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->tlen) < 0)
387c44b31c2SChuck Lever 		return false;
38838a70315SChuck Lever 	if (args->tlen == 0)
389c44b31c2SChuck Lever 		return false;
39038a70315SChuck Lever 
39109f75a53SChuck Lever 	args->first.iov_len = head->iov_len - xdr_stream_pos(xdr);
39209f75a53SChuck Lever 	args->first.iov_base = xdr_inline_decode(xdr, args->tlen);
39309f75a53SChuck Lever 	if (!args->first.iov_base)
394c44b31c2SChuck Lever 		return false;
39509f75a53SChuck Lever 	return svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
3961da177e4SLinus Torvalds }
3971da177e4SLinus Torvalds 
398c44b31c2SChuck Lever bool
39916c66364SChuck Lever nfssvc_decode_readdirargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
4001da177e4SLinus Torvalds {
401026fec7eSChristoph Hellwig 	struct nfsd_readdirargs *args = rqstp->rq_argp;
402026fec7eSChristoph Hellwig 
4038688361aSChuck Lever 	if (!svcxdr_decode_fhandle(xdr, &args->fh))
404c44b31c2SChuck Lever 		return false;
4058688361aSChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->cookie) < 0)
406c44b31c2SChuck Lever 		return false;
4078688361aSChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->count) < 0)
408c44b31c2SChuck Lever 		return false;
4091da177e4SLinus Torvalds 
410c44b31c2SChuck Lever 	return true;
4111da177e4SLinus Torvalds }
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds /*
4141da177e4SLinus Torvalds  * XDR encode functions
4151da177e4SLinus Torvalds  */
4161da177e4SLinus Torvalds 
417*130e2054SChuck Lever bool
418fda49441SChuck Lever nfssvc_encode_statres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
419cc028a10SChuck Lever {
420cc028a10SChuck Lever 	struct nfsd_stat *resp = rqstp->rq_resp;
421cc028a10SChuck Lever 
422a887eaedSChuck Lever 	return svcxdr_encode_stat(xdr, resp->status);
423cc028a10SChuck Lever }
424cc028a10SChuck Lever 
425*130e2054SChuck Lever bool
426fda49441SChuck Lever nfssvc_encode_attrstatres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
4271da177e4SLinus Torvalds {
42863f8de37SChristoph Hellwig 	struct nfsd_attrstat *resp = rqstp->rq_resp;
42963f8de37SChristoph Hellwig 
43092b54a4fSChuck Lever 	if (!svcxdr_encode_stat(xdr, resp->status))
431*130e2054SChuck Lever 		return false;
43292b54a4fSChuck Lever 	switch (resp->status) {
43392b54a4fSChuck Lever 	case nfs_ok:
43492b54a4fSChuck Lever 		if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
435*130e2054SChuck Lever 			return false;
43692b54a4fSChuck Lever 		break;
43792b54a4fSChuck Lever 	}
43892b54a4fSChuck Lever 
439*130e2054SChuck Lever 	return true;
4401da177e4SLinus Torvalds }
4411da177e4SLinus Torvalds 
442*130e2054SChuck Lever bool
443fda49441SChuck Lever nfssvc_encode_diropres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
4441da177e4SLinus Torvalds {
44563f8de37SChristoph Hellwig 	struct nfsd_diropres *resp = rqstp->rq_resp;
44663f8de37SChristoph Hellwig 
447e3b4ef22SChuck Lever 	if (!svcxdr_encode_stat(xdr, resp->status))
448*130e2054SChuck Lever 		return false;
449e3b4ef22SChuck Lever 	switch (resp->status) {
450e3b4ef22SChuck Lever 	case nfs_ok:
451e3b4ef22SChuck Lever 		if (!svcxdr_encode_fhandle(xdr, &resp->fh))
452*130e2054SChuck Lever 			return false;
453e3b4ef22SChuck Lever 		if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
454*130e2054SChuck Lever 			return false;
455e3b4ef22SChuck Lever 		break;
456e3b4ef22SChuck Lever 	}
457e3b4ef22SChuck Lever 
458*130e2054SChuck Lever 	return true;
4591da177e4SLinus Torvalds }
4601da177e4SLinus Torvalds 
461*130e2054SChuck Lever bool
462fda49441SChuck Lever nfssvc_encode_readlinkres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
4631da177e4SLinus Torvalds {
46463f8de37SChristoph Hellwig 	struct nfsd_readlinkres *resp = rqstp->rq_resp;
46576e5492bSChuck Lever 	struct kvec *head = rqstp->rq_res.head;
46663f8de37SChristoph Hellwig 
467d9014b0fSChuck Lever 	if (!svcxdr_encode_stat(xdr, resp->status))
468*130e2054SChuck Lever 		return false;
469d9014b0fSChuck Lever 	switch (resp->status) {
470d9014b0fSChuck Lever 	case nfs_ok:
471d9014b0fSChuck Lever 		if (xdr_stream_encode_u32(xdr, resp->len) < 0)
472*130e2054SChuck Lever 			return false;
473d9014b0fSChuck Lever 		xdr_write_pages(xdr, &resp->page, 0, resp->len);
474d9014b0fSChuck Lever 		if (svc_encode_result_payload(rqstp, head->iov_len, resp->len) < 0)
475*130e2054SChuck Lever 			return false;
476d9014b0fSChuck Lever 		break;
477d9014b0fSChuck Lever 	}
478d9014b0fSChuck Lever 
479*130e2054SChuck Lever 	return true;
4801da177e4SLinus Torvalds }
4811da177e4SLinus Torvalds 
482*130e2054SChuck Lever bool
483fda49441SChuck Lever nfssvc_encode_readres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
4841da177e4SLinus Torvalds {
48563f8de37SChristoph Hellwig 	struct nfsd_readres *resp = rqstp->rq_resp;
48676e5492bSChuck Lever 	struct kvec *head = rqstp->rq_res.head;
48763f8de37SChristoph Hellwig 
488a6f8d9dcSChuck Lever 	if (!svcxdr_encode_stat(xdr, resp->status))
489*130e2054SChuck Lever 		return false;
490a6f8d9dcSChuck Lever 	switch (resp->status) {
491a6f8d9dcSChuck Lever 	case nfs_ok:
492a6f8d9dcSChuck Lever 		if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
493*130e2054SChuck Lever 			return false;
494a6f8d9dcSChuck Lever 		if (xdr_stream_encode_u32(xdr, resp->count) < 0)
495*130e2054SChuck Lever 			return false;
496a6f8d9dcSChuck Lever 		xdr_write_pages(xdr, resp->pages, rqstp->rq_res.page_base,
497a6f8d9dcSChuck Lever 				resp->count);
498a6f8d9dcSChuck Lever 		if (svc_encode_result_payload(rqstp, head->iov_len, resp->count) < 0)
499*130e2054SChuck Lever 			return false;
500a6f8d9dcSChuck Lever 		break;
501a6f8d9dcSChuck Lever 	}
502a6f8d9dcSChuck Lever 
503*130e2054SChuck Lever 	return true;
5041da177e4SLinus Torvalds }
5051da177e4SLinus Torvalds 
506*130e2054SChuck Lever bool
507fda49441SChuck Lever nfssvc_encode_readdirres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5081da177e4SLinus Torvalds {
50963f8de37SChristoph Hellwig 	struct nfsd_readdirres *resp = rqstp->rq_resp;
510f5dcccd6SChuck Lever 	struct xdr_buf *dirlist = &resp->dirlist;
51163f8de37SChristoph Hellwig 
51294c8f8c6SChuck Lever 	if (!svcxdr_encode_stat(xdr, resp->status))
513*130e2054SChuck Lever 		return false;
51494c8f8c6SChuck Lever 	switch (resp->status) {
51594c8f8c6SChuck Lever 	case nfs_ok:
516f5dcccd6SChuck Lever 		xdr_write_pages(xdr, dirlist->pages, 0, dirlist->len);
51794c8f8c6SChuck Lever 		/* no more entries */
51894c8f8c6SChuck Lever 		if (xdr_stream_encode_item_absent(xdr) < 0)
519*130e2054SChuck Lever 			return false;
52094c8f8c6SChuck Lever 		if (xdr_stream_encode_bool(xdr, resp->common.err == nfserr_eof) < 0)
521*130e2054SChuck Lever 			return false;
52294c8f8c6SChuck Lever 		break;
52394c8f8c6SChuck Lever 	}
5241da177e4SLinus Torvalds 
525*130e2054SChuck Lever 	return true;
5261da177e4SLinus Torvalds }
5271da177e4SLinus Torvalds 
528*130e2054SChuck Lever bool
529fda49441SChuck Lever nfssvc_encode_statfsres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
5301da177e4SLinus Torvalds {
53163f8de37SChristoph Hellwig 	struct nfsd_statfsres *resp = rqstp->rq_resp;
5321da177e4SLinus Torvalds 	struct kstatfs	*stat = &resp->stats;
533fda49441SChuck Lever 	__be32 *p;
5341da177e4SLinus Torvalds 
535bf15229fSChuck Lever 	if (!svcxdr_encode_stat(xdr, resp->status))
536*130e2054SChuck Lever 		return false;
537bf15229fSChuck Lever 	switch (resp->status) {
538bf15229fSChuck Lever 	case nfs_ok:
539bf15229fSChuck Lever 		p = xdr_reserve_space(xdr, XDR_UNIT * 5);
540bf15229fSChuck Lever 		if (!p)
541*130e2054SChuck Lever 			return false;
542bf15229fSChuck Lever 		*p++ = cpu_to_be32(NFSSVC_MAXBLKSIZE_V2);
543bf15229fSChuck Lever 		*p++ = cpu_to_be32(stat->f_bsize);
544bf15229fSChuck Lever 		*p++ = cpu_to_be32(stat->f_blocks);
545bf15229fSChuck Lever 		*p++ = cpu_to_be32(stat->f_bfree);
546bf15229fSChuck Lever 		*p = cpu_to_be32(stat->f_bavail);
547bf15229fSChuck Lever 		break;
548bf15229fSChuck Lever 	}
549f0af2210SChuck Lever 
550*130e2054SChuck Lever 	return true;
5511da177e4SLinus Torvalds }
5521da177e4SLinus Torvalds 
553d5253200SChuck Lever /**
554d5253200SChuck Lever  * nfssvc_encode_nfscookie - Encode a directory offset cookie
555d5253200SChuck Lever  * @resp: readdir result context
556d5253200SChuck Lever  * @offset: offset cookie to encode
557d5253200SChuck Lever  *
558f5dcccd6SChuck Lever  * The buffer space for the offset cookie has already been reserved
559f5dcccd6SChuck Lever  * by svcxdr_encode_entry_common().
560d5253200SChuck Lever  */
561d5253200SChuck Lever void nfssvc_encode_nfscookie(struct nfsd_readdirres *resp, u32 offset)
562d5253200SChuck Lever {
563f5dcccd6SChuck Lever 	__be32 cookie = cpu_to_be32(offset);
564f5dcccd6SChuck Lever 
565f5dcccd6SChuck Lever 	if (!resp->cookie_offset)
566d5253200SChuck Lever 		return;
567d5253200SChuck Lever 
568f5dcccd6SChuck Lever 	write_bytes_to_xdr_buf(&resp->dirlist, resp->cookie_offset, &cookie,
569f5dcccd6SChuck Lever 			       sizeof(cookie));
570f5dcccd6SChuck Lever 	resp->cookie_offset = 0;
571f5dcccd6SChuck Lever }
572f5dcccd6SChuck Lever 
573f5dcccd6SChuck Lever static bool
574f5dcccd6SChuck Lever svcxdr_encode_entry_common(struct nfsd_readdirres *resp, const char *name,
575f5dcccd6SChuck Lever 			   int namlen, loff_t offset, u64 ino)
576f5dcccd6SChuck Lever {
577f5dcccd6SChuck Lever 	struct xdr_buf *dirlist = &resp->dirlist;
578f5dcccd6SChuck Lever 	struct xdr_stream *xdr = &resp->xdr;
579f5dcccd6SChuck Lever 
580f5dcccd6SChuck Lever 	if (xdr_stream_encode_item_present(xdr) < 0)
581f5dcccd6SChuck Lever 		return false;
582f5dcccd6SChuck Lever 	/* fileid */
583f5dcccd6SChuck Lever 	if (xdr_stream_encode_u32(xdr, (u32)ino) < 0)
584f5dcccd6SChuck Lever 		return false;
585f5dcccd6SChuck Lever 	/* name */
586f5dcccd6SChuck Lever 	if (xdr_stream_encode_opaque(xdr, name, min(namlen, NFS2_MAXNAMLEN)) < 0)
587f5dcccd6SChuck Lever 		return false;
588f5dcccd6SChuck Lever 	/* cookie */
589f5dcccd6SChuck Lever 	resp->cookie_offset = dirlist->len;
590f5dcccd6SChuck Lever 	if (xdr_stream_encode_u32(xdr, ~0U) < 0)
591f5dcccd6SChuck Lever 		return false;
592f5dcccd6SChuck Lever 
593f5dcccd6SChuck Lever 	return true;
594f5dcccd6SChuck Lever }
595f5dcccd6SChuck Lever 
596f5dcccd6SChuck Lever /**
5978a2cf9f5SChuck Lever  * nfssvc_encode_entry - encode one NFSv2 READDIR entry
598f5dcccd6SChuck Lever  * @data: directory context
599f5dcccd6SChuck Lever  * @name: name of the object to be encoded
600f5dcccd6SChuck Lever  * @namlen: length of that name, in bytes
601f5dcccd6SChuck Lever  * @offset: the offset of the previous entry
602f5dcccd6SChuck Lever  * @ino: the fileid of this entry
603f5dcccd6SChuck Lever  * @d_type: unused
604f5dcccd6SChuck Lever  *
605f5dcccd6SChuck Lever  * Return values:
606f5dcccd6SChuck Lever  *   %0: Entry was successfully encoded.
607f5dcccd6SChuck Lever  *   %-EINVAL: An encoding problem occured, secondary status code in resp->common.err
608f5dcccd6SChuck Lever  *
609f5dcccd6SChuck Lever  * On exit, the following fields are updated:
610f5dcccd6SChuck Lever  *   - resp->xdr
611f5dcccd6SChuck Lever  *   - resp->common.err
612f5dcccd6SChuck Lever  *   - resp->cookie_offset
613f5dcccd6SChuck Lever  */
6148a2cf9f5SChuck Lever int nfssvc_encode_entry(void *data, const char *name, int namlen,
615f5dcccd6SChuck Lever 			loff_t offset, u64 ino, unsigned int d_type)
616f5dcccd6SChuck Lever {
617f5dcccd6SChuck Lever 	struct readdir_cd *ccd = data;
618f5dcccd6SChuck Lever 	struct nfsd_readdirres *resp = container_of(ccd,
619f5dcccd6SChuck Lever 						    struct nfsd_readdirres,
620f5dcccd6SChuck Lever 						    common);
621f5dcccd6SChuck Lever 	unsigned int starting_length = resp->dirlist.len;
622f5dcccd6SChuck Lever 
623f5dcccd6SChuck Lever 	/* The offset cookie for the previous entry */
624f5dcccd6SChuck Lever 	nfssvc_encode_nfscookie(resp, offset);
625f5dcccd6SChuck Lever 
626f5dcccd6SChuck Lever 	if (!svcxdr_encode_entry_common(resp, name, namlen, offset, ino))
627f5dcccd6SChuck Lever 		goto out_toosmall;
628f5dcccd6SChuck Lever 
629f5dcccd6SChuck Lever 	xdr_commit_encode(&resp->xdr);
630f5dcccd6SChuck Lever 	resp->common.err = nfs_ok;
631f5dcccd6SChuck Lever 	return 0;
632f5dcccd6SChuck Lever 
633f5dcccd6SChuck Lever out_toosmall:
634f5dcccd6SChuck Lever 	resp->cookie_offset = 0;
635f5dcccd6SChuck Lever 	resp->common.err = nfserr_toosmall;
636f5dcccd6SChuck Lever 	resp->dirlist.len = starting_length;
637f5dcccd6SChuck Lever 	return -EINVAL;
638d5253200SChuck Lever }
639d5253200SChuck Lever 
6401da177e4SLinus Torvalds /*
6411da177e4SLinus Torvalds  * XDR release functions
6421da177e4SLinus Torvalds  */
6431841b9b6SChuck Lever void nfssvc_release_attrstat(struct svc_rqst *rqstp)
6441da177e4SLinus Torvalds {
6451841b9b6SChuck Lever 	struct nfsd_attrstat *resp = rqstp->rq_resp;
6461841b9b6SChuck Lever 
6471841b9b6SChuck Lever 	fh_put(&resp->fh);
6481841b9b6SChuck Lever }
6491841b9b6SChuck Lever 
6501841b9b6SChuck Lever void nfssvc_release_diropres(struct svc_rqst *rqstp)
6511841b9b6SChuck Lever {
6521841b9b6SChuck Lever 	struct nfsd_diropres *resp = rqstp->rq_resp;
6531841b9b6SChuck Lever 
6541841b9b6SChuck Lever 	fh_put(&resp->fh);
6551841b9b6SChuck Lever }
6561841b9b6SChuck Lever 
6571841b9b6SChuck Lever void nfssvc_release_readres(struct svc_rqst *rqstp)
6581841b9b6SChuck Lever {
6591841b9b6SChuck Lever 	struct nfsd_readres *resp = rqstp->rq_resp;
6608537488bSChristoph Hellwig 
6611da177e4SLinus Torvalds 	fh_put(&resp->fh);
6621da177e4SLinus Torvalds }
663