xref: /openbmc/linux/fs/nfsd/nfsxdr.c (revision 2fdd6bd293b9e7dda61220538b2759fbf06f5af0)
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 
691da177e4SLinus Torvalds /*
701da177e4SLinus Torvalds  * Decode a file name and make sure that the path contains
711da177e4SLinus Torvalds  * no slashes or null bytes.
721da177e4SLinus Torvalds  */
733ee6f61cSAdrian Bunk static __be32 *
74ee1a95b3SChuck Lever decode_filename(__be32 *p, char **namp, unsigned int *lenp)
751da177e4SLinus Torvalds {
761da177e4SLinus Torvalds 	char		*name;
77ee1a95b3SChuck Lever 	unsigned int	i;
781da177e4SLinus Torvalds 
791da177e4SLinus Torvalds 	if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS_MAXNAMLEN)) != NULL) {
801da177e4SLinus Torvalds 		for (i = 0, name = *namp; i < *lenp; i++, name++) {
811da177e4SLinus Torvalds 			if (*name == '\0' || *name == '/')
821da177e4SLinus Torvalds 				return NULL;
831da177e4SLinus Torvalds 		}
841da177e4SLinus Torvalds 	}
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds 	return p;
871da177e4SLinus Torvalds }
881da177e4SLinus Torvalds 
896d742c18SChuck Lever static bool
906d742c18SChuck Lever svcxdr_decode_filename(struct xdr_stream *xdr, char **name, unsigned int *len)
916d742c18SChuck Lever {
926d742c18SChuck Lever 	u32 size, i;
936d742c18SChuck Lever 	__be32 *p;
946d742c18SChuck Lever 	char *c;
956d742c18SChuck Lever 
966d742c18SChuck Lever 	if (xdr_stream_decode_u32(xdr, &size) < 0)
976d742c18SChuck Lever 		return false;
986d742c18SChuck Lever 	if (size == 0 || size > NFS_MAXNAMLEN)
996d742c18SChuck Lever 		return false;
1006d742c18SChuck Lever 	p = xdr_inline_decode(xdr, size);
1016d742c18SChuck Lever 	if (!p)
1026d742c18SChuck Lever 		return false;
1036d742c18SChuck Lever 
1046d742c18SChuck Lever 	*len = size;
1056d742c18SChuck Lever 	*name = (char *)p;
1066d742c18SChuck Lever 	for (i = 0, c = *name; i < size; i++, c++)
1076d742c18SChuck Lever 		if (*c == '\0' || *c == '/')
1086d742c18SChuck Lever 			return false;
1096d742c18SChuck Lever 
1106d742c18SChuck Lever 	return true;
1116d742c18SChuck Lever }
1126d742c18SChuck Lever 
1136d742c18SChuck Lever static bool
1146d742c18SChuck Lever svcxdr_decode_diropargs(struct xdr_stream *xdr, struct svc_fh *fhp,
1156d742c18SChuck Lever 			char **name, unsigned int *len)
1166d742c18SChuck Lever {
1176d742c18SChuck Lever 	return svcxdr_decode_fhandle(xdr, fhp) &&
1186d742c18SChuck Lever 		svcxdr_decode_filename(xdr, name, len);
1196d742c18SChuck Lever }
1206d742c18SChuck Lever 
1213ee6f61cSAdrian Bunk static __be32 *
122e45d1a18STrond Myklebust decode_sattr(__be32 *p, struct iattr *iap, struct user_namespace *userns)
1231da177e4SLinus Torvalds {
1241da177e4SLinus Torvalds 	u32	tmp, tmp1;
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 	iap->ia_valid = 0;
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds 	/* Sun client bug compatibility check: some sun clients seem to
1291da177e4SLinus Torvalds 	 * put 0xffff in the mode field when they mean 0xffffffff.
1301da177e4SLinus Torvalds 	 * Quoting the 4.4BSD nfs server code: Nah nah nah nah na nah.
1311da177e4SLinus Torvalds 	 */
1321da177e4SLinus Torvalds 	if ((tmp = ntohl(*p++)) != (u32)-1 && tmp != 0xffff) {
1331da177e4SLinus Torvalds 		iap->ia_valid |= ATTR_MODE;
1341da177e4SLinus Torvalds 		iap->ia_mode = tmp;
1351da177e4SLinus Torvalds 	}
1361da177e4SLinus Torvalds 	if ((tmp = ntohl(*p++)) != (u32)-1) {
137e45d1a18STrond Myklebust 		iap->ia_uid = make_kuid(userns, tmp);
1387c19723eSEric W. Biederman 		if (uid_valid(iap->ia_uid))
1391da177e4SLinus Torvalds 			iap->ia_valid |= ATTR_UID;
1401da177e4SLinus Torvalds 	}
1411da177e4SLinus Torvalds 	if ((tmp = ntohl(*p++)) != (u32)-1) {
142e45d1a18STrond Myklebust 		iap->ia_gid = make_kgid(userns, tmp);
1437c19723eSEric W. Biederman 		if (gid_valid(iap->ia_gid))
1441da177e4SLinus Torvalds 			iap->ia_valid |= ATTR_GID;
1451da177e4SLinus Torvalds 	}
1461da177e4SLinus Torvalds 	if ((tmp = ntohl(*p++)) != (u32)-1) {
1471da177e4SLinus Torvalds 		iap->ia_valid |= ATTR_SIZE;
1481da177e4SLinus Torvalds 		iap->ia_size = tmp;
1491da177e4SLinus Torvalds 	}
1501da177e4SLinus Torvalds 	tmp  = ntohl(*p++); tmp1 = ntohl(*p++);
1511da177e4SLinus Torvalds 	if (tmp != (u32)-1 && tmp1 != (u32)-1) {
1521da177e4SLinus Torvalds 		iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
1531da177e4SLinus Torvalds 		iap->ia_atime.tv_sec = tmp;
1541da177e4SLinus Torvalds 		iap->ia_atime.tv_nsec = tmp1 * 1000;
1551da177e4SLinus Torvalds 	}
1561da177e4SLinus Torvalds 	tmp  = ntohl(*p++); tmp1 = ntohl(*p++);
1571da177e4SLinus Torvalds 	if (tmp != (u32)-1 && tmp1 != (u32)-1) {
1581da177e4SLinus Torvalds 		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
1591da177e4SLinus Torvalds 		iap->ia_mtime.tv_sec = tmp;
1601da177e4SLinus Torvalds 		iap->ia_mtime.tv_nsec = tmp1 * 1000;
1611da177e4SLinus Torvalds 		/*
1621da177e4SLinus Torvalds 		 * Passing the invalid value useconds=1000000 for mtime
1631da177e4SLinus Torvalds 		 * is a Sun convention for "set both mtime and atime to
1641da177e4SLinus Torvalds 		 * current server time".  It's needed to make permissions
1651da177e4SLinus Torvalds 		 * checks for the "touch" program across v2 mounts to
1661da177e4SLinus Torvalds 		 * Solaris and Irix boxes work correctly. See description of
1671da177e4SLinus Torvalds 		 * sattr in section 6.1 of "NFS Illustrated" by
1681da177e4SLinus Torvalds 		 * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
1691da177e4SLinus Torvalds 		 */
1701da177e4SLinus Torvalds 		if (tmp1 == 1000000)
1711da177e4SLinus Torvalds 			iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET);
1721da177e4SLinus Torvalds 	}
1731da177e4SLinus Torvalds 	return p;
1741da177e4SLinus Torvalds }
1751da177e4SLinus Torvalds 
176*2fdd6bd2SChuck Lever static bool
177*2fdd6bd2SChuck Lever svcxdr_decode_sattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
178*2fdd6bd2SChuck Lever 		    struct iattr *iap)
179*2fdd6bd2SChuck Lever {
180*2fdd6bd2SChuck Lever 	u32 tmp1, tmp2;
181*2fdd6bd2SChuck Lever 	__be32 *p;
182*2fdd6bd2SChuck Lever 
183*2fdd6bd2SChuck Lever 	p = xdr_inline_decode(xdr, XDR_UNIT * 8);
184*2fdd6bd2SChuck Lever 	if (!p)
185*2fdd6bd2SChuck Lever 		return false;
186*2fdd6bd2SChuck Lever 
187*2fdd6bd2SChuck Lever 	iap->ia_valid = 0;
188*2fdd6bd2SChuck Lever 
189*2fdd6bd2SChuck Lever 	/*
190*2fdd6bd2SChuck Lever 	 * Some Sun clients put 0xffff in the mode field when they
191*2fdd6bd2SChuck Lever 	 * mean 0xffffffff.
192*2fdd6bd2SChuck Lever 	 */
193*2fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
194*2fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1 && tmp1 != 0xffff) {
195*2fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_MODE;
196*2fdd6bd2SChuck Lever 		iap->ia_mode = tmp1;
197*2fdd6bd2SChuck Lever 	}
198*2fdd6bd2SChuck Lever 
199*2fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
200*2fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1) {
201*2fdd6bd2SChuck Lever 		iap->ia_uid = make_kuid(nfsd_user_namespace(rqstp), tmp1);
202*2fdd6bd2SChuck Lever 		if (uid_valid(iap->ia_uid))
203*2fdd6bd2SChuck Lever 			iap->ia_valid |= ATTR_UID;
204*2fdd6bd2SChuck Lever 	}
205*2fdd6bd2SChuck Lever 
206*2fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
207*2fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1) {
208*2fdd6bd2SChuck Lever 		iap->ia_gid = make_kgid(nfsd_user_namespace(rqstp), tmp1);
209*2fdd6bd2SChuck Lever 		if (gid_valid(iap->ia_gid))
210*2fdd6bd2SChuck Lever 			iap->ia_valid |= ATTR_GID;
211*2fdd6bd2SChuck Lever 	}
212*2fdd6bd2SChuck Lever 
213*2fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
214*2fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1) {
215*2fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_SIZE;
216*2fdd6bd2SChuck Lever 		iap->ia_size = tmp1;
217*2fdd6bd2SChuck Lever 	}
218*2fdd6bd2SChuck Lever 
219*2fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
220*2fdd6bd2SChuck Lever 	tmp2 = be32_to_cpup(p++);
221*2fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
222*2fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
223*2fdd6bd2SChuck Lever 		iap->ia_atime.tv_sec = tmp1;
224*2fdd6bd2SChuck Lever 		iap->ia_atime.tv_nsec = tmp2 * NSEC_PER_USEC;
225*2fdd6bd2SChuck Lever 	}
226*2fdd6bd2SChuck Lever 
227*2fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
228*2fdd6bd2SChuck Lever 	tmp2 = be32_to_cpup(p++);
229*2fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
230*2fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
231*2fdd6bd2SChuck Lever 		iap->ia_mtime.tv_sec = tmp1;
232*2fdd6bd2SChuck Lever 		iap->ia_mtime.tv_nsec = tmp2 * NSEC_PER_USEC;
233*2fdd6bd2SChuck Lever 		/*
234*2fdd6bd2SChuck Lever 		 * Passing the invalid value useconds=1000000 for mtime
235*2fdd6bd2SChuck Lever 		 * is a Sun convention for "set both mtime and atime to
236*2fdd6bd2SChuck Lever 		 * current server time".  It's needed to make permissions
237*2fdd6bd2SChuck Lever 		 * checks for the "touch" program across v2 mounts to
238*2fdd6bd2SChuck Lever 		 * Solaris and Irix boxes work correctly. See description of
239*2fdd6bd2SChuck Lever 		 * sattr in section 6.1 of "NFS Illustrated" by
240*2fdd6bd2SChuck Lever 		 * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
241*2fdd6bd2SChuck Lever 		 */
242*2fdd6bd2SChuck Lever 		if (tmp2 == 1000000)
243*2fdd6bd2SChuck Lever 			iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET);
244*2fdd6bd2SChuck Lever 	}
245*2fdd6bd2SChuck Lever 
246*2fdd6bd2SChuck Lever 	return true;
247*2fdd6bd2SChuck Lever }
248*2fdd6bd2SChuck Lever 
249131a21c2SAl Viro static __be32 *
250131a21c2SAl Viro encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
251a334de28SDavid Shaw 	     struct kstat *stat)
2521da177e4SLinus Torvalds {
253e45d1a18STrond Myklebust 	struct user_namespace *userns = nfsd_user_namespace(rqstp);
2541da177e4SLinus Torvalds 	struct dentry	*dentry = fhp->fh_dentry;
2551da177e4SLinus Torvalds 	int type;
25695582b00SDeepa Dinamani 	struct timespec64 time;
257af6a4e28SNeilBrown 	u32 f;
2581da177e4SLinus Torvalds 
259a334de28SDavid Shaw 	type = (stat->mode & S_IFMT);
2601da177e4SLinus Torvalds 
2611da177e4SLinus Torvalds 	*p++ = htonl(nfs_ftypes[type >> 12]);
262082f31a2SJ. Bruce Fields 	*p++ = htonl((u32) stat->mode);
263a334de28SDavid Shaw 	*p++ = htonl((u32) stat->nlink);
264e45d1a18STrond Myklebust 	*p++ = htonl((u32) from_kuid_munged(userns, stat->uid));
265e45d1a18STrond Myklebust 	*p++ = htonl((u32) from_kgid_munged(userns, stat->gid));
2661da177e4SLinus Torvalds 
267a334de28SDavid Shaw 	if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) {
2681da177e4SLinus Torvalds 		*p++ = htonl(NFS_MAXPATHLEN);
2691da177e4SLinus Torvalds 	} else {
270a334de28SDavid Shaw 		*p++ = htonl((u32) stat->size);
2711da177e4SLinus Torvalds 	}
272a334de28SDavid Shaw 	*p++ = htonl((u32) stat->blksize);
2731da177e4SLinus Torvalds 	if (S_ISCHR(type) || S_ISBLK(type))
274a334de28SDavid Shaw 		*p++ = htonl(new_encode_dev(stat->rdev));
2751da177e4SLinus Torvalds 	else
2761da177e4SLinus Torvalds 		*p++ = htonl(0xffffffff);
277a334de28SDavid Shaw 	*p++ = htonl((u32) stat->blocks);
278af6a4e28SNeilBrown 	switch (fsid_source(fhp)) {
279af6a4e28SNeilBrown 	default:
280af6a4e28SNeilBrown 	case FSIDSOURCE_DEV:
281a334de28SDavid Shaw 		*p++ = htonl(new_encode_dev(stat->dev));
282af6a4e28SNeilBrown 		break;
283af6a4e28SNeilBrown 	case FSIDSOURCE_FSID:
284af6a4e28SNeilBrown 		*p++ = htonl((u32) fhp->fh_export->ex_fsid);
285af6a4e28SNeilBrown 		break;
286af6a4e28SNeilBrown 	case FSIDSOURCE_UUID:
287af6a4e28SNeilBrown 		f = ((u32*)fhp->fh_export->ex_uuid)[0];
288af6a4e28SNeilBrown 		f ^= ((u32*)fhp->fh_export->ex_uuid)[1];
289af6a4e28SNeilBrown 		f ^= ((u32*)fhp->fh_export->ex_uuid)[2];
290af6a4e28SNeilBrown 		f ^= ((u32*)fhp->fh_export->ex_uuid)[3];
291af6a4e28SNeilBrown 		*p++ = htonl(f);
292af6a4e28SNeilBrown 		break;
293af6a4e28SNeilBrown 	}
294a334de28SDavid Shaw 	*p++ = htonl((u32) stat->ino);
295a334de28SDavid Shaw 	*p++ = htonl((u32) stat->atime.tv_sec);
296a334de28SDavid Shaw 	*p++ = htonl(stat->atime.tv_nsec ? stat->atime.tv_nsec / 1000 : 0);
29776c47948SAmir Goldstein 	time = stat->mtime;
2982b0143b5SDavid Howells 	lease_get_mtime(d_inode(dentry), &time);
2991da177e4SLinus Torvalds 	*p++ = htonl((u32) time.tv_sec);
3001da177e4SLinus Torvalds 	*p++ = htonl(time.tv_nsec ? time.tv_nsec / 1000 : 0);
301a334de28SDavid Shaw 	*p++ = htonl((u32) stat->ctime.tv_sec);
302a334de28SDavid Shaw 	*p++ = htonl(stat->ctime.tv_nsec ? stat->ctime.tv_nsec / 1000 : 0);
3031da177e4SLinus Torvalds 
3041da177e4SLinus Torvalds 	return p;
3051da177e4SLinus Torvalds }
3061da177e4SLinus Torvalds 
307a257cdd0SAndreas Gruenbacher /* Helper function for NFSv2 ACL code */
3084f4a4fadSJ. Bruce Fields __be32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, struct kstat *stat)
309a257cdd0SAndreas Gruenbacher {
3104f4a4fadSJ. Bruce Fields 	return encode_fattr(rqstp, p, fhp, stat);
311a257cdd0SAndreas Gruenbacher }
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds /*
3141da177e4SLinus Torvalds  * XDR decode functions
3151da177e4SLinus Torvalds  */
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds int
318ebcd8e8bSChuck Lever nfssvc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p)
3191da177e4SLinus Torvalds {
320ebcd8e8bSChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
321026fec7eSChristoph Hellwig 	struct nfsd_fhandle *args = rqstp->rq_argp;
322026fec7eSChristoph Hellwig 
323ebcd8e8bSChuck Lever 	return svcxdr_decode_fhandle(xdr, &args->fh);
3241da177e4SLinus Torvalds }
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds int
327026fec7eSChristoph Hellwig nfssvc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p)
3281da177e4SLinus Torvalds {
329*2fdd6bd2SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
330026fec7eSChristoph Hellwig 	struct nfsd_sattrargs *args = rqstp->rq_argp;
331026fec7eSChristoph Hellwig 
332*2fdd6bd2SChuck Lever 	return svcxdr_decode_fhandle(xdr, &args->fh) &&
333*2fdd6bd2SChuck Lever 		svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
3341da177e4SLinus Torvalds }
3351da177e4SLinus Torvalds 
3361da177e4SLinus Torvalds int
337026fec7eSChristoph Hellwig nfssvc_decode_diropargs(struct svc_rqst *rqstp, __be32 *p)
3381da177e4SLinus Torvalds {
3396d742c18SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
340026fec7eSChristoph Hellwig 	struct nfsd_diropargs *args = rqstp->rq_argp;
341026fec7eSChristoph Hellwig 
3426d742c18SChuck Lever 	return svcxdr_decode_diropargs(xdr, &args->fh, &args->name, &args->len);
3431da177e4SLinus Torvalds }
3441da177e4SLinus Torvalds 
3451da177e4SLinus Torvalds int
346026fec7eSChristoph Hellwig nfssvc_decode_readargs(struct svc_rqst *rqstp, __be32 *p)
3471da177e4SLinus Torvalds {
3488c293ef9SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
349026fec7eSChristoph Hellwig 	struct nfsd_readargs *args = rqstp->rq_argp;
3508c293ef9SChuck Lever 	u32 totalcount;
3518c293ef9SChuck Lever 
3528c293ef9SChuck Lever 	if (!svcxdr_decode_fhandle(xdr, &args->fh))
3538c293ef9SChuck Lever 		return 0;
3548c293ef9SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->offset) < 0)
3558c293ef9SChuck Lever 		return 0;
3568c293ef9SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->count) < 0)
3578c293ef9SChuck Lever 		return 0;
3588c293ef9SChuck Lever 	/* totalcount is ignored */
3598c293ef9SChuck Lever 	if (xdr_stream_decode_u32(xdr, &totalcount) < 0)
3601da177e4SLinus Torvalds 		return 0;
3611da177e4SLinus Torvalds 
3628c293ef9SChuck Lever 	return 1;
3631da177e4SLinus Torvalds }
3641da177e4SLinus Torvalds 
3651da177e4SLinus Torvalds int
366026fec7eSChristoph Hellwig nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p)
3671da177e4SLinus Torvalds {
368a51b5b73SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
369026fec7eSChristoph Hellwig 	struct nfsd_writeargs *args = rqstp->rq_argp;
370db44bac4SJ. Bruce Fields 	struct kvec *head = rqstp->rq_arg.head;
371a51b5b73SChuck Lever 	struct kvec *tail = rqstp->rq_arg.tail;
372a51b5b73SChuck Lever 	u32 beginoffset, totalcount;
373a51b5b73SChuck Lever 	size_t remaining;
374f34b9568SPeter Staubach 
375a51b5b73SChuck Lever 	if (!svcxdr_decode_fhandle(xdr, &args->fh))
376a51b5b73SChuck Lever 		return 0;
377a51b5b73SChuck Lever 	/* beginoffset is ignored */
378a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &beginoffset) < 0)
379a51b5b73SChuck Lever 		return 0;
380a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->offset) < 0)
381a51b5b73SChuck Lever 		return 0;
382a51b5b73SChuck Lever 	/* totalcount is ignored */
383a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &totalcount) < 0)
3841da177e4SLinus Torvalds 		return 0;
3851da177e4SLinus Torvalds 
386a51b5b73SChuck Lever 	/* opaque data */
387a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->len) < 0)
388f34b9568SPeter Staubach 		return 0;
389a51b5b73SChuck Lever 	if (args->len > NFSSVC_MAXBLKSIZE_V2)
39013bf9fbfSJ. Bruce Fields 		return 0;
391a51b5b73SChuck Lever 	remaining = head->iov_len + rqstp->rq_arg.page_len + tail->iov_len;
392a51b5b73SChuck Lever 	remaining -= xdr_stream_pos(xdr);
393a51b5b73SChuck Lever 	if (remaining < xdr_align_size(args->len))
394f34b9568SPeter Staubach 		return 0;
395a51b5b73SChuck Lever 	args->first.iov_base = xdr->p;
396a51b5b73SChuck Lever 	args->first.iov_len = head->iov_len - xdr_stream_pos(xdr);
397f34b9568SPeter Staubach 
398f34b9568SPeter Staubach 	return 1;
3991da177e4SLinus Torvalds }
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds int
402026fec7eSChristoph Hellwig nfssvc_decode_createargs(struct svc_rqst *rqstp, __be32 *p)
4031da177e4SLinus Torvalds {
404026fec7eSChristoph Hellwig 	struct nfsd_createargs *args = rqstp->rq_argp;
405026fec7eSChristoph Hellwig 
4061da177e4SLinus Torvalds 	if (   !(p = decode_fh(p, &args->fh))
407072f62edSNeilBrown 	    || !(p = decode_filename(p, &args->name, &args->len)))
4081da177e4SLinus Torvalds 		return 0;
409e45d1a18STrond Myklebust 	p = decode_sattr(p, &args->attrs, nfsd_user_namespace(rqstp));
4101da177e4SLinus Torvalds 
4111da177e4SLinus Torvalds 	return xdr_argsize_check(rqstp, p);
4121da177e4SLinus Torvalds }
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds int
415026fec7eSChristoph Hellwig nfssvc_decode_renameargs(struct svc_rqst *rqstp, __be32 *p)
4161da177e4SLinus Torvalds {
41762aa557eSChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
418026fec7eSChristoph Hellwig 	struct nfsd_renameargs *args = rqstp->rq_argp;
419026fec7eSChristoph Hellwig 
42062aa557eSChuck Lever 	return svcxdr_decode_diropargs(xdr, &args->ffh,
42162aa557eSChuck Lever 				       &args->fname, &args->flen) &&
42262aa557eSChuck Lever 		svcxdr_decode_diropargs(xdr, &args->tfh,
42362aa557eSChuck Lever 					&args->tname, &args->tlen);
4241da177e4SLinus Torvalds }
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds int
427026fec7eSChristoph Hellwig nfssvc_decode_linkargs(struct svc_rqst *rqstp, __be32 *p)
4281da177e4SLinus Torvalds {
42977edcdf9SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
430026fec7eSChristoph Hellwig 	struct nfsd_linkargs *args = rqstp->rq_argp;
431026fec7eSChristoph Hellwig 
43277edcdf9SChuck Lever 	return svcxdr_decode_fhandle(xdr, &args->ffh) &&
43377edcdf9SChuck Lever 		svcxdr_decode_diropargs(xdr, &args->tfh,
43477edcdf9SChuck Lever 					&args->tname, &args->tlen);
4351da177e4SLinus Torvalds }
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds int
438026fec7eSChristoph Hellwig nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
4391da177e4SLinus Torvalds {
440026fec7eSChristoph Hellwig 	struct nfsd_symlinkargs *args = rqstp->rq_argp;
44138a70315SChuck Lever 	char *base = (char *)p;
44238a70315SChuck Lever 	size_t xdrlen;
443026fec7eSChristoph Hellwig 
4441da177e4SLinus Torvalds 	if (   !(p = decode_fh(p, &args->ffh))
44538a70315SChuck Lever 	    || !(p = decode_filename(p, &args->fname, &args->flen)))
4461da177e4SLinus Torvalds 		return 0;
4471da177e4SLinus Torvalds 
44838a70315SChuck Lever 	args->tlen = ntohl(*p++);
44938a70315SChuck Lever 	if (args->tlen == 0)
45038a70315SChuck Lever 		return 0;
45138a70315SChuck Lever 
45238a70315SChuck Lever 	args->first.iov_base = p;
45338a70315SChuck Lever 	args->first.iov_len = rqstp->rq_arg.head[0].iov_len;
45438a70315SChuck Lever 	args->first.iov_len -= (char *)p - base;
45538a70315SChuck Lever 
45638a70315SChuck Lever 	/* This request is never larger than a page. Therefore,
45738a70315SChuck Lever 	 * transport will deliver either:
45838a70315SChuck Lever 	 * 1. pathname in the pagelist -> sattr is in the tail.
45938a70315SChuck Lever 	 * 2. everything in the head buffer -> sattr is in the head.
46038a70315SChuck Lever 	 */
46138a70315SChuck Lever 	if (rqstp->rq_arg.page_len) {
46238a70315SChuck Lever 		if (args->tlen != rqstp->rq_arg.page_len)
46338a70315SChuck Lever 			return 0;
46438a70315SChuck Lever 		p = rqstp->rq_arg.tail[0].iov_base;
46538a70315SChuck Lever 	} else {
46638a70315SChuck Lever 		xdrlen = XDR_QUADLEN(args->tlen);
46738a70315SChuck Lever 		if (xdrlen > args->first.iov_len - (8 * sizeof(__be32)))
46838a70315SChuck Lever 			return 0;
46938a70315SChuck Lever 		p += xdrlen;
47038a70315SChuck Lever 	}
471e45d1a18STrond Myklebust 	decode_sattr(p, &args->attrs, nfsd_user_namespace(rqstp));
47238a70315SChuck Lever 
47338a70315SChuck Lever 	return 1;
4741da177e4SLinus Torvalds }
4751da177e4SLinus Torvalds 
4761da177e4SLinus Torvalds int
477026fec7eSChristoph Hellwig nfssvc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p)
4781da177e4SLinus Torvalds {
4798688361aSChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
480026fec7eSChristoph Hellwig 	struct nfsd_readdirargs *args = rqstp->rq_argp;
481026fec7eSChristoph Hellwig 
4828688361aSChuck Lever 	if (!svcxdr_decode_fhandle(xdr, &args->fh))
4831da177e4SLinus Torvalds 		return 0;
4848688361aSChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->cookie) < 0)
4858688361aSChuck Lever 		return 0;
4868688361aSChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->count) < 0)
4878688361aSChuck Lever 		return 0;
4881da177e4SLinus Torvalds 
4898688361aSChuck Lever 	return 1;
4901da177e4SLinus Torvalds }
4911da177e4SLinus Torvalds 
4921da177e4SLinus Torvalds /*
4931da177e4SLinus Torvalds  * XDR encode functions
4941da177e4SLinus Torvalds  */
4951da177e4SLinus Torvalds 
4961da177e4SLinus Torvalds int
497cc028a10SChuck Lever nfssvc_encode_stat(struct svc_rqst *rqstp, __be32 *p)
498cc028a10SChuck Lever {
499cc028a10SChuck Lever 	struct nfsd_stat *resp = rqstp->rq_resp;
500cc028a10SChuck Lever 
501cc028a10SChuck Lever 	*p++ = resp->status;
502cc028a10SChuck Lever 	return xdr_ressize_check(rqstp, p);
503cc028a10SChuck Lever }
504cc028a10SChuck Lever 
505cc028a10SChuck Lever int
50663f8de37SChristoph Hellwig nfssvc_encode_attrstat(struct svc_rqst *rqstp, __be32 *p)
5071da177e4SLinus Torvalds {
50863f8de37SChristoph Hellwig 	struct nfsd_attrstat *resp = rqstp->rq_resp;
50963f8de37SChristoph Hellwig 
510cc028a10SChuck Lever 	*p++ = resp->status;
511f0af2210SChuck Lever 	if (resp->status != nfs_ok)
512cc028a10SChuck Lever 		goto out;
513a334de28SDavid Shaw 	p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
514cc028a10SChuck Lever out:
5151da177e4SLinus Torvalds 	return xdr_ressize_check(rqstp, p);
5161da177e4SLinus Torvalds }
5171da177e4SLinus Torvalds 
5181da177e4SLinus Torvalds int
51963f8de37SChristoph Hellwig nfssvc_encode_diropres(struct svc_rqst *rqstp, __be32 *p)
5201da177e4SLinus Torvalds {
52163f8de37SChristoph Hellwig 	struct nfsd_diropres *resp = rqstp->rq_resp;
52263f8de37SChristoph Hellwig 
523cc028a10SChuck Lever 	*p++ = resp->status;
524f0af2210SChuck Lever 	if (resp->status != nfs_ok)
525cc028a10SChuck Lever 		goto out;
5261da177e4SLinus Torvalds 	p = encode_fh(p, &resp->fh);
527a334de28SDavid Shaw 	p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
528cc028a10SChuck Lever out:
5291da177e4SLinus Torvalds 	return xdr_ressize_check(rqstp, p);
5301da177e4SLinus Torvalds }
5311da177e4SLinus Torvalds 
5321da177e4SLinus Torvalds int
53363f8de37SChristoph Hellwig nfssvc_encode_readlinkres(struct svc_rqst *rqstp, __be32 *p)
5341da177e4SLinus Torvalds {
53563f8de37SChristoph Hellwig 	struct nfsd_readlinkres *resp = rqstp->rq_resp;
53676e5492bSChuck Lever 	struct kvec *head = rqstp->rq_res.head;
53763f8de37SChristoph Hellwig 
538cc028a10SChuck Lever 	*p++ = resp->status;
539f0af2210SChuck Lever 	if (resp->status != nfs_ok)
540f0af2210SChuck Lever 		return xdr_ressize_check(rqstp, p);
541f0af2210SChuck Lever 
5421da177e4SLinus Torvalds 	*p++ = htonl(resp->len);
5431da177e4SLinus Torvalds 	xdr_ressize_check(rqstp, p);
5441da177e4SLinus Torvalds 	rqstp->rq_res.page_len = resp->len;
5451da177e4SLinus Torvalds 	if (resp->len & 3) {
5461da177e4SLinus Torvalds 		/* need to pad the tail */
5471da177e4SLinus Torvalds 		rqstp->rq_res.tail[0].iov_base = p;
5481da177e4SLinus Torvalds 		*p = 0;
5491da177e4SLinus Torvalds 		rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3);
5501da177e4SLinus Torvalds 	}
55176e5492bSChuck Lever 	if (svc_encode_result_payload(rqstp, head->iov_len, resp->len))
55276e5492bSChuck Lever 		return 0;
5531da177e4SLinus Torvalds 	return 1;
5541da177e4SLinus Torvalds }
5551da177e4SLinus Torvalds 
5561da177e4SLinus Torvalds int
55763f8de37SChristoph Hellwig nfssvc_encode_readres(struct svc_rqst *rqstp, __be32 *p)
5581da177e4SLinus Torvalds {
55963f8de37SChristoph Hellwig 	struct nfsd_readres *resp = rqstp->rq_resp;
56076e5492bSChuck Lever 	struct kvec *head = rqstp->rq_res.head;
56163f8de37SChristoph Hellwig 
562cc028a10SChuck Lever 	*p++ = resp->status;
563f0af2210SChuck Lever 	if (resp->status != nfs_ok)
564f0af2210SChuck Lever 		return xdr_ressize_check(rqstp, p);
565f0af2210SChuck Lever 
566a334de28SDavid Shaw 	p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
5671da177e4SLinus Torvalds 	*p++ = htonl(resp->count);
5681da177e4SLinus Torvalds 	xdr_ressize_check(rqstp, p);
5691da177e4SLinus Torvalds 
5701da177e4SLinus Torvalds 	/* now update rqstp->rq_res to reflect data as well */
5711da177e4SLinus Torvalds 	rqstp->rq_res.page_len = resp->count;
5721da177e4SLinus Torvalds 	if (resp->count & 3) {
5731da177e4SLinus Torvalds 		/* need to pad the tail */
5741da177e4SLinus Torvalds 		rqstp->rq_res.tail[0].iov_base = p;
5751da177e4SLinus Torvalds 		*p = 0;
5761da177e4SLinus Torvalds 		rqstp->rq_res.tail[0].iov_len = 4 - (resp->count&3);
5771da177e4SLinus Torvalds 	}
57876e5492bSChuck Lever 	if (svc_encode_result_payload(rqstp, head->iov_len, resp->count))
57976e5492bSChuck Lever 		return 0;
5801da177e4SLinus Torvalds 	return 1;
5811da177e4SLinus Torvalds }
5821da177e4SLinus Torvalds 
5831da177e4SLinus Torvalds int
58463f8de37SChristoph Hellwig nfssvc_encode_readdirres(struct svc_rqst *rqstp, __be32 *p)
5851da177e4SLinus Torvalds {
58663f8de37SChristoph Hellwig 	struct nfsd_readdirres *resp = rqstp->rq_resp;
58763f8de37SChristoph Hellwig 
588cc028a10SChuck Lever 	*p++ = resp->status;
589f0af2210SChuck Lever 	if (resp->status != nfs_ok)
590f0af2210SChuck Lever 		return xdr_ressize_check(rqstp, p);
591f0af2210SChuck Lever 
5921da177e4SLinus Torvalds 	xdr_ressize_check(rqstp, p);
5931da177e4SLinus Torvalds 	p = resp->buffer;
5941da177e4SLinus Torvalds 	*p++ = 0;			/* no more entries */
5951da177e4SLinus Torvalds 	*p++ = htonl((resp->common.err == nfserr_eof));
5961da177e4SLinus Torvalds 	rqstp->rq_res.page_len = (((unsigned long)p-1) & ~PAGE_MASK)+1;
5971da177e4SLinus Torvalds 
5981da177e4SLinus Torvalds 	return 1;
5991da177e4SLinus Torvalds }
6001da177e4SLinus Torvalds 
6011da177e4SLinus Torvalds int
60263f8de37SChristoph Hellwig nfssvc_encode_statfsres(struct svc_rqst *rqstp, __be32 *p)
6031da177e4SLinus Torvalds {
60463f8de37SChristoph Hellwig 	struct nfsd_statfsres *resp = rqstp->rq_resp;
6051da177e4SLinus Torvalds 	struct kstatfs	*stat = &resp->stats;
6061da177e4SLinus Torvalds 
607cc028a10SChuck Lever 	*p++ = resp->status;
608f0af2210SChuck Lever 	if (resp->status != nfs_ok)
609f0af2210SChuck Lever 		return xdr_ressize_check(rqstp, p);
610f0af2210SChuck Lever 
6117adae489SGreg Banks 	*p++ = htonl(NFSSVC_MAXBLKSIZE_V2);	/* max transfer size */
6121da177e4SLinus Torvalds 	*p++ = htonl(stat->f_bsize);
6131da177e4SLinus Torvalds 	*p++ = htonl(stat->f_blocks);
6141da177e4SLinus Torvalds 	*p++ = htonl(stat->f_bfree);
6151da177e4SLinus Torvalds 	*p++ = htonl(stat->f_bavail);
6161da177e4SLinus Torvalds 	return xdr_ressize_check(rqstp, p);
6171da177e4SLinus Torvalds }
6181da177e4SLinus Torvalds 
6191da177e4SLinus Torvalds int
620a0ad13efSNeilBrown nfssvc_encode_entry(void *ccdv, const char *name,
621a0ad13efSNeilBrown 		    int namlen, loff_t offset, u64 ino, unsigned int d_type)
6221da177e4SLinus Torvalds {
623a0ad13efSNeilBrown 	struct readdir_cd *ccd = ccdv;
6241da177e4SLinus Torvalds 	struct nfsd_readdirres *cd = container_of(ccd, struct nfsd_readdirres, common);
625131a21c2SAl Viro 	__be32	*p = cd->buffer;
6261da177e4SLinus Torvalds 	int	buflen, slen;
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds 	/*
6291da177e4SLinus Torvalds 	dprintk("nfsd: entry(%.*s off %ld ino %ld)\n",
6301da177e4SLinus Torvalds 			namlen, name, offset, ino);
6311da177e4SLinus Torvalds 	 */
6321da177e4SLinus Torvalds 
6331da177e4SLinus Torvalds 	if (offset > ~((u32) 0)) {
6341da177e4SLinus Torvalds 		cd->common.err = nfserr_fbig;
6351da177e4SLinus Torvalds 		return -EINVAL;
6361da177e4SLinus Torvalds 	}
6371da177e4SLinus Torvalds 	if (cd->offset)
6381da177e4SLinus Torvalds 		*cd->offset = htonl(offset);
6391da177e4SLinus Torvalds 
6403c7aa15dSKinglong Mee 	/* truncate filename */
6413c7aa15dSKinglong Mee 	namlen = min(namlen, NFS2_MAXNAMLEN);
6421da177e4SLinus Torvalds 	slen = XDR_QUADLEN(namlen);
6433c7aa15dSKinglong Mee 
6441da177e4SLinus Torvalds 	if ((buflen = cd->buflen - slen - 4) < 0) {
6451da177e4SLinus Torvalds 		cd->common.err = nfserr_toosmall;
6461da177e4SLinus Torvalds 		return -EINVAL;
6471da177e4SLinus Torvalds 	}
64840ee5dc6SPeter Staubach 	if (ino > ~((u32) 0)) {
64940ee5dc6SPeter Staubach 		cd->common.err = nfserr_fbig;
65040ee5dc6SPeter Staubach 		return -EINVAL;
65140ee5dc6SPeter Staubach 	}
6521da177e4SLinus Torvalds 	*p++ = xdr_one;				/* mark entry present */
6531da177e4SLinus Torvalds 	*p++ = htonl((u32) ino);		/* file id */
6541da177e4SLinus Torvalds 	p    = xdr_encode_array(p, name, namlen);/* name length & name */
6551da177e4SLinus Torvalds 	cd->offset = p;			/* remember pointer */
656131a21c2SAl Viro 	*p++ = htonl(~0U);		/* offset of next entry */
6571da177e4SLinus Torvalds 
6581da177e4SLinus Torvalds 	cd->buflen = buflen;
6591da177e4SLinus Torvalds 	cd->buffer = p;
6601da177e4SLinus Torvalds 	cd->common.err = nfs_ok;
6611da177e4SLinus Torvalds 	return 0;
6621da177e4SLinus Torvalds }
6631da177e4SLinus Torvalds 
6641da177e4SLinus Torvalds /*
6651da177e4SLinus Torvalds  * XDR release functions
6661da177e4SLinus Torvalds  */
6671841b9b6SChuck Lever void nfssvc_release_attrstat(struct svc_rqst *rqstp)
6681da177e4SLinus Torvalds {
6691841b9b6SChuck Lever 	struct nfsd_attrstat *resp = rqstp->rq_resp;
6701841b9b6SChuck Lever 
6711841b9b6SChuck Lever 	fh_put(&resp->fh);
6721841b9b6SChuck Lever }
6731841b9b6SChuck Lever 
6741841b9b6SChuck Lever void nfssvc_release_diropres(struct svc_rqst *rqstp)
6751841b9b6SChuck Lever {
6761841b9b6SChuck Lever 	struct nfsd_diropres *resp = rqstp->rq_resp;
6771841b9b6SChuck Lever 
6781841b9b6SChuck Lever 	fh_put(&resp->fh);
6791841b9b6SChuck Lever }
6801841b9b6SChuck Lever 
6811841b9b6SChuck Lever void nfssvc_release_readres(struct svc_rqst *rqstp)
6821841b9b6SChuck Lever {
6831841b9b6SChuck Lever 	struct nfsd_readres *resp = rqstp->rq_resp;
6848537488bSChristoph Hellwig 
6851da177e4SLinus Torvalds 	fh_put(&resp->fh);
6861da177e4SLinus Torvalds }
687