xref: /openbmc/linux/fs/nfsd/nfsxdr.c (revision e3b4ef221ac57c08341c97a10c8a81c041f76716)
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  */
1792b54a4fSChuck Lever static const 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 
29a887eaedSChuck Lever static bool
30a887eaedSChuck Lever svcxdr_encode_stat(struct xdr_stream *xdr, __be32 status)
31a887eaedSChuck Lever {
32a887eaedSChuck Lever 	__be32 *p;
33a887eaedSChuck Lever 
34a887eaedSChuck Lever 	p = xdr_reserve_space(xdr, sizeof(status));
35a887eaedSChuck Lever 	if (!p)
36a887eaedSChuck Lever 		return false;
37a887eaedSChuck Lever 	*p = status;
38a887eaedSChuck Lever 
39a887eaedSChuck Lever 	return true;
40a887eaedSChuck Lever }
41a887eaedSChuck Lever 
42635a45d3SChuck Lever /**
43635a45d3SChuck Lever  * svcxdr_decode_fhandle - Decode an NFSv2 file handle
44635a45d3SChuck Lever  * @xdr: XDR stream positioned at an encoded NFSv2 FH
45635a45d3SChuck Lever  * @fhp: OUT: filled-in server file handle
46635a45d3SChuck Lever  *
47635a45d3SChuck Lever  * Return values:
48635a45d3SChuck Lever  *  %false: The encoded file handle was not valid
49635a45d3SChuck Lever  *  %true: @fhp has been initialized
50635a45d3SChuck Lever  */
51635a45d3SChuck Lever bool
52ebcd8e8bSChuck Lever svcxdr_decode_fhandle(struct xdr_stream *xdr, struct svc_fh *fhp)
53ebcd8e8bSChuck Lever {
54ebcd8e8bSChuck Lever 	__be32 *p;
55ebcd8e8bSChuck Lever 
56ebcd8e8bSChuck Lever 	p = xdr_inline_decode(xdr, NFS_FHSIZE);
57ebcd8e8bSChuck Lever 	if (!p)
58ebcd8e8bSChuck Lever 		return false;
59ebcd8e8bSChuck Lever 	fh_init(fhp, NFS_FHSIZE);
60ebcd8e8bSChuck Lever 	memcpy(&fhp->fh_handle.fh_base, p, NFS_FHSIZE);
61ebcd8e8bSChuck Lever 	fhp->fh_handle.fh_size = NFS_FHSIZE;
62ebcd8e8bSChuck Lever 
63ebcd8e8bSChuck Lever 	return true;
64ebcd8e8bSChuck Lever }
65ebcd8e8bSChuck Lever 
66*e3b4ef22SChuck Lever static bool
67*e3b4ef22SChuck Lever svcxdr_encode_fhandle(struct xdr_stream *xdr, const struct svc_fh *fhp)
681da177e4SLinus Torvalds {
69*e3b4ef22SChuck Lever 	__be32 *p;
70*e3b4ef22SChuck Lever 
71*e3b4ef22SChuck Lever 	p = xdr_reserve_space(xdr, NFS_FHSIZE);
72*e3b4ef22SChuck Lever 	if (!p)
73*e3b4ef22SChuck Lever 		return false;
741da177e4SLinus Torvalds 	memcpy(p, &fhp->fh_handle.fh_base, NFS_FHSIZE);
75*e3b4ef22SChuck Lever 
76*e3b4ef22SChuck Lever 	return true;
771da177e4SLinus Torvalds }
781da177e4SLinus Torvalds 
7992b54a4fSChuck Lever static __be32 *
8092b54a4fSChuck Lever encode_timeval(__be32 *p, const struct timespec64 *time)
8192b54a4fSChuck Lever {
8292b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32)time->tv_sec);
8392b54a4fSChuck Lever 	if (time->tv_nsec)
8492b54a4fSChuck Lever 		*p++ = cpu_to_be32(time->tv_nsec / NSEC_PER_USEC);
8592b54a4fSChuck Lever 	else
8692b54a4fSChuck Lever 		*p++ = xdr_zero;
8792b54a4fSChuck Lever 	return p;
8892b54a4fSChuck Lever }
8992b54a4fSChuck Lever 
906d742c18SChuck Lever static bool
916d742c18SChuck Lever svcxdr_decode_filename(struct xdr_stream *xdr, char **name, unsigned int *len)
926d742c18SChuck Lever {
936d742c18SChuck Lever 	u32 size, i;
946d742c18SChuck Lever 	__be32 *p;
956d742c18SChuck Lever 	char *c;
966d742c18SChuck Lever 
976d742c18SChuck Lever 	if (xdr_stream_decode_u32(xdr, &size) < 0)
986d742c18SChuck Lever 		return false;
996d742c18SChuck Lever 	if (size == 0 || size > NFS_MAXNAMLEN)
1006d742c18SChuck Lever 		return false;
1016d742c18SChuck Lever 	p = xdr_inline_decode(xdr, size);
1026d742c18SChuck Lever 	if (!p)
1036d742c18SChuck Lever 		return false;
1046d742c18SChuck Lever 
1056d742c18SChuck Lever 	*len = size;
1066d742c18SChuck Lever 	*name = (char *)p;
1076d742c18SChuck Lever 	for (i = 0, c = *name; i < size; i++, c++)
1086d742c18SChuck Lever 		if (*c == '\0' || *c == '/')
1096d742c18SChuck Lever 			return false;
1106d742c18SChuck Lever 
1116d742c18SChuck Lever 	return true;
1126d742c18SChuck Lever }
1136d742c18SChuck Lever 
1146d742c18SChuck Lever static bool
1156d742c18SChuck Lever svcxdr_decode_diropargs(struct xdr_stream *xdr, struct svc_fh *fhp,
1166d742c18SChuck Lever 			char **name, unsigned int *len)
1176d742c18SChuck Lever {
1186d742c18SChuck Lever 	return svcxdr_decode_fhandle(xdr, fhp) &&
1196d742c18SChuck Lever 		svcxdr_decode_filename(xdr, name, len);
1206d742c18SChuck Lever }
1216d742c18SChuck Lever 
1222fdd6bd2SChuck Lever static bool
1232fdd6bd2SChuck Lever svcxdr_decode_sattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
1242fdd6bd2SChuck Lever 		    struct iattr *iap)
1252fdd6bd2SChuck Lever {
1262fdd6bd2SChuck Lever 	u32 tmp1, tmp2;
1272fdd6bd2SChuck Lever 	__be32 *p;
1282fdd6bd2SChuck Lever 
1292fdd6bd2SChuck Lever 	p = xdr_inline_decode(xdr, XDR_UNIT * 8);
1302fdd6bd2SChuck Lever 	if (!p)
1312fdd6bd2SChuck Lever 		return false;
1322fdd6bd2SChuck Lever 
1332fdd6bd2SChuck Lever 	iap->ia_valid = 0;
1342fdd6bd2SChuck Lever 
1352fdd6bd2SChuck Lever 	/*
1362fdd6bd2SChuck Lever 	 * Some Sun clients put 0xffff in the mode field when they
1372fdd6bd2SChuck Lever 	 * mean 0xffffffff.
1382fdd6bd2SChuck Lever 	 */
1392fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1402fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1 && tmp1 != 0xffff) {
1412fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_MODE;
1422fdd6bd2SChuck Lever 		iap->ia_mode = tmp1;
1432fdd6bd2SChuck Lever 	}
1442fdd6bd2SChuck Lever 
1452fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1462fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1) {
1472fdd6bd2SChuck Lever 		iap->ia_uid = make_kuid(nfsd_user_namespace(rqstp), tmp1);
1482fdd6bd2SChuck Lever 		if (uid_valid(iap->ia_uid))
1492fdd6bd2SChuck Lever 			iap->ia_valid |= ATTR_UID;
1502fdd6bd2SChuck Lever 	}
1512fdd6bd2SChuck Lever 
1522fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1532fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1) {
1542fdd6bd2SChuck Lever 		iap->ia_gid = make_kgid(nfsd_user_namespace(rqstp), tmp1);
1552fdd6bd2SChuck Lever 		if (gid_valid(iap->ia_gid))
1562fdd6bd2SChuck Lever 			iap->ia_valid |= ATTR_GID;
1572fdd6bd2SChuck Lever 	}
1582fdd6bd2SChuck Lever 
1592fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1602fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1) {
1612fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_SIZE;
1622fdd6bd2SChuck Lever 		iap->ia_size = tmp1;
1632fdd6bd2SChuck Lever 	}
1642fdd6bd2SChuck Lever 
1652fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1662fdd6bd2SChuck Lever 	tmp2 = be32_to_cpup(p++);
1672fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
1682fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
1692fdd6bd2SChuck Lever 		iap->ia_atime.tv_sec = tmp1;
1702fdd6bd2SChuck Lever 		iap->ia_atime.tv_nsec = tmp2 * NSEC_PER_USEC;
1712fdd6bd2SChuck Lever 	}
1722fdd6bd2SChuck Lever 
1732fdd6bd2SChuck Lever 	tmp1 = be32_to_cpup(p++);
1742fdd6bd2SChuck Lever 	tmp2 = be32_to_cpup(p++);
1752fdd6bd2SChuck Lever 	if (tmp1 != (u32)-1 && tmp2 != (u32)-1) {
1762fdd6bd2SChuck Lever 		iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
1772fdd6bd2SChuck Lever 		iap->ia_mtime.tv_sec = tmp1;
1782fdd6bd2SChuck Lever 		iap->ia_mtime.tv_nsec = tmp2 * NSEC_PER_USEC;
1792fdd6bd2SChuck Lever 		/*
1802fdd6bd2SChuck Lever 		 * Passing the invalid value useconds=1000000 for mtime
1812fdd6bd2SChuck Lever 		 * is a Sun convention for "set both mtime and atime to
1822fdd6bd2SChuck Lever 		 * current server time".  It's needed to make permissions
1832fdd6bd2SChuck Lever 		 * checks for the "touch" program across v2 mounts to
1842fdd6bd2SChuck Lever 		 * Solaris and Irix boxes work correctly. See description of
1852fdd6bd2SChuck Lever 		 * sattr in section 6.1 of "NFS Illustrated" by
1862fdd6bd2SChuck Lever 		 * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
1872fdd6bd2SChuck Lever 		 */
1882fdd6bd2SChuck Lever 		if (tmp2 == 1000000)
1892fdd6bd2SChuck Lever 			iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET);
1902fdd6bd2SChuck Lever 	}
1912fdd6bd2SChuck Lever 
1922fdd6bd2SChuck Lever 	return true;
1932fdd6bd2SChuck Lever }
1942fdd6bd2SChuck Lever 
195131a21c2SAl Viro static __be32 *
196131a21c2SAl Viro encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
197a334de28SDavid Shaw 	     struct kstat *stat)
1981da177e4SLinus Torvalds {
199e45d1a18STrond Myklebust 	struct user_namespace *userns = nfsd_user_namespace(rqstp);
2001da177e4SLinus Torvalds 	struct dentry	*dentry = fhp->fh_dentry;
2011da177e4SLinus Torvalds 	int type;
20295582b00SDeepa Dinamani 	struct timespec64 time;
203af6a4e28SNeilBrown 	u32 f;
2041da177e4SLinus Torvalds 
205a334de28SDavid Shaw 	type = (stat->mode & S_IFMT);
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds 	*p++ = htonl(nfs_ftypes[type >> 12]);
208082f31a2SJ. Bruce Fields 	*p++ = htonl((u32) stat->mode);
209a334de28SDavid Shaw 	*p++ = htonl((u32) stat->nlink);
210e45d1a18STrond Myklebust 	*p++ = htonl((u32) from_kuid_munged(userns, stat->uid));
211e45d1a18STrond Myklebust 	*p++ = htonl((u32) from_kgid_munged(userns, stat->gid));
2121da177e4SLinus Torvalds 
213a334de28SDavid Shaw 	if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) {
2141da177e4SLinus Torvalds 		*p++ = htonl(NFS_MAXPATHLEN);
2151da177e4SLinus Torvalds 	} else {
216a334de28SDavid Shaw 		*p++ = htonl((u32) stat->size);
2171da177e4SLinus Torvalds 	}
218a334de28SDavid Shaw 	*p++ = htonl((u32) stat->blksize);
2191da177e4SLinus Torvalds 	if (S_ISCHR(type) || S_ISBLK(type))
220a334de28SDavid Shaw 		*p++ = htonl(new_encode_dev(stat->rdev));
2211da177e4SLinus Torvalds 	else
2221da177e4SLinus Torvalds 		*p++ = htonl(0xffffffff);
223a334de28SDavid Shaw 	*p++ = htonl((u32) stat->blocks);
224af6a4e28SNeilBrown 	switch (fsid_source(fhp)) {
225af6a4e28SNeilBrown 	default:
226af6a4e28SNeilBrown 	case FSIDSOURCE_DEV:
227a334de28SDavid Shaw 		*p++ = htonl(new_encode_dev(stat->dev));
228af6a4e28SNeilBrown 		break;
229af6a4e28SNeilBrown 	case FSIDSOURCE_FSID:
230af6a4e28SNeilBrown 		*p++ = htonl((u32) fhp->fh_export->ex_fsid);
231af6a4e28SNeilBrown 		break;
232af6a4e28SNeilBrown 	case FSIDSOURCE_UUID:
233af6a4e28SNeilBrown 		f = ((u32*)fhp->fh_export->ex_uuid)[0];
234af6a4e28SNeilBrown 		f ^= ((u32*)fhp->fh_export->ex_uuid)[1];
235af6a4e28SNeilBrown 		f ^= ((u32*)fhp->fh_export->ex_uuid)[2];
236af6a4e28SNeilBrown 		f ^= ((u32*)fhp->fh_export->ex_uuid)[3];
237af6a4e28SNeilBrown 		*p++ = htonl(f);
238af6a4e28SNeilBrown 		break;
239af6a4e28SNeilBrown 	}
240a334de28SDavid Shaw 	*p++ = htonl((u32) stat->ino);
241a334de28SDavid Shaw 	*p++ = htonl((u32) stat->atime.tv_sec);
242a334de28SDavid Shaw 	*p++ = htonl(stat->atime.tv_nsec ? stat->atime.tv_nsec / 1000 : 0);
24376c47948SAmir Goldstein 	time = stat->mtime;
2442b0143b5SDavid Howells 	lease_get_mtime(d_inode(dentry), &time);
2451da177e4SLinus Torvalds 	*p++ = htonl((u32) time.tv_sec);
2461da177e4SLinus Torvalds 	*p++ = htonl(time.tv_nsec ? time.tv_nsec / 1000 : 0);
247a334de28SDavid Shaw 	*p++ = htonl((u32) stat->ctime.tv_sec);
248a334de28SDavid Shaw 	*p++ = htonl(stat->ctime.tv_nsec ? stat->ctime.tv_nsec / 1000 : 0);
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds 	return p;
2511da177e4SLinus Torvalds }
2521da177e4SLinus Torvalds 
253*e3b4ef22SChuck Lever static bool
25492b54a4fSChuck Lever svcxdr_encode_fattr(struct svc_rqst *rqstp, struct xdr_stream *xdr,
25592b54a4fSChuck Lever 		    const struct svc_fh *fhp, const struct kstat *stat)
25692b54a4fSChuck Lever {
25792b54a4fSChuck Lever 	struct user_namespace *userns = nfsd_user_namespace(rqstp);
25892b54a4fSChuck Lever 	struct dentry *dentry = fhp->fh_dentry;
25992b54a4fSChuck Lever 	int type = stat->mode & S_IFMT;
26092b54a4fSChuck Lever 	struct timespec64 time;
26192b54a4fSChuck Lever 	__be32 *p;
26292b54a4fSChuck Lever 	u32 fsid;
26392b54a4fSChuck Lever 
26492b54a4fSChuck Lever 	p = xdr_reserve_space(xdr, XDR_UNIT * 17);
26592b54a4fSChuck Lever 	if (!p)
266*e3b4ef22SChuck Lever 		return false;
26792b54a4fSChuck Lever 
26892b54a4fSChuck Lever 	*p++ = cpu_to_be32(nfs_ftypes[type >> 12]);
26992b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32)stat->mode);
27092b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32)stat->nlink);
27192b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32)from_kuid_munged(userns, stat->uid));
27292b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32)from_kgid_munged(userns, stat->gid));
27392b54a4fSChuck Lever 
27492b54a4fSChuck Lever 	if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN)
27592b54a4fSChuck Lever 		*p++ = cpu_to_be32(NFS_MAXPATHLEN);
27692b54a4fSChuck Lever 	else
27792b54a4fSChuck Lever 		*p++ = cpu_to_be32((u32) stat->size);
27892b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32) stat->blksize);
27992b54a4fSChuck Lever 	if (S_ISCHR(type) || S_ISBLK(type))
28092b54a4fSChuck Lever 		*p++ = cpu_to_be32(new_encode_dev(stat->rdev));
28192b54a4fSChuck Lever 	else
28292b54a4fSChuck Lever 		*p++ = cpu_to_be32(0xffffffff);
28392b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32)stat->blocks);
28492b54a4fSChuck Lever 
28592b54a4fSChuck Lever 	switch (fsid_source(fhp)) {
28692b54a4fSChuck Lever 	case FSIDSOURCE_FSID:
28792b54a4fSChuck Lever 		fsid = (u32)fhp->fh_export->ex_fsid;
28892b54a4fSChuck Lever 		break;
28992b54a4fSChuck Lever 	case FSIDSOURCE_UUID:
29092b54a4fSChuck Lever 		fsid = ((u32 *)fhp->fh_export->ex_uuid)[0];
29192b54a4fSChuck Lever 		fsid ^= ((u32 *)fhp->fh_export->ex_uuid)[1];
29292b54a4fSChuck Lever 		fsid ^= ((u32 *)fhp->fh_export->ex_uuid)[2];
29392b54a4fSChuck Lever 		fsid ^= ((u32 *)fhp->fh_export->ex_uuid)[3];
29492b54a4fSChuck Lever 		break;
29592b54a4fSChuck Lever 	default:
29692b54a4fSChuck Lever 		fsid = new_encode_dev(stat->dev);
29792b54a4fSChuck Lever 		break;
29892b54a4fSChuck Lever 	}
29992b54a4fSChuck Lever 	*p++ = cpu_to_be32(fsid);
30092b54a4fSChuck Lever 
30192b54a4fSChuck Lever 	*p++ = cpu_to_be32((u32)stat->ino);
30292b54a4fSChuck Lever 	p = encode_timeval(p, &stat->atime);
30392b54a4fSChuck Lever 	time = stat->mtime;
30492b54a4fSChuck Lever 	lease_get_mtime(d_inode(dentry), &time);
30592b54a4fSChuck Lever 	p = encode_timeval(p, &time);
30692b54a4fSChuck Lever 	encode_timeval(p, &stat->ctime);
30792b54a4fSChuck Lever 
308*e3b4ef22SChuck Lever 	return true;
30992b54a4fSChuck Lever }
31092b54a4fSChuck Lever 
311a257cdd0SAndreas Gruenbacher /* Helper function for NFSv2 ACL code */
3124f4a4fadSJ. Bruce Fields __be32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, struct kstat *stat)
313a257cdd0SAndreas Gruenbacher {
3144f4a4fadSJ. Bruce Fields 	return encode_fattr(rqstp, p, fhp, stat);
315a257cdd0SAndreas Gruenbacher }
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds /*
3181da177e4SLinus Torvalds  * XDR decode functions
3191da177e4SLinus Torvalds  */
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds int
322ebcd8e8bSChuck Lever nfssvc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p)
3231da177e4SLinus Torvalds {
324ebcd8e8bSChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
325026fec7eSChristoph Hellwig 	struct nfsd_fhandle *args = rqstp->rq_argp;
326026fec7eSChristoph Hellwig 
327ebcd8e8bSChuck Lever 	return svcxdr_decode_fhandle(xdr, &args->fh);
3281da177e4SLinus Torvalds }
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds int
331026fec7eSChristoph Hellwig nfssvc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p)
3321da177e4SLinus Torvalds {
3332fdd6bd2SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
334026fec7eSChristoph Hellwig 	struct nfsd_sattrargs *args = rqstp->rq_argp;
335026fec7eSChristoph Hellwig 
3362fdd6bd2SChuck Lever 	return svcxdr_decode_fhandle(xdr, &args->fh) &&
3372fdd6bd2SChuck Lever 		svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
3381da177e4SLinus Torvalds }
3391da177e4SLinus Torvalds 
3401da177e4SLinus Torvalds int
341026fec7eSChristoph Hellwig nfssvc_decode_diropargs(struct svc_rqst *rqstp, __be32 *p)
3421da177e4SLinus Torvalds {
3436d742c18SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
344026fec7eSChristoph Hellwig 	struct nfsd_diropargs *args = rqstp->rq_argp;
345026fec7eSChristoph Hellwig 
3466d742c18SChuck Lever 	return svcxdr_decode_diropargs(xdr, &args->fh, &args->name, &args->len);
3471da177e4SLinus Torvalds }
3481da177e4SLinus Torvalds 
3491da177e4SLinus Torvalds int
350026fec7eSChristoph Hellwig nfssvc_decode_readargs(struct svc_rqst *rqstp, __be32 *p)
3511da177e4SLinus Torvalds {
3528c293ef9SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
353026fec7eSChristoph Hellwig 	struct nfsd_readargs *args = rqstp->rq_argp;
3548c293ef9SChuck Lever 	u32 totalcount;
3558c293ef9SChuck Lever 
3568c293ef9SChuck Lever 	if (!svcxdr_decode_fhandle(xdr, &args->fh))
3578c293ef9SChuck Lever 		return 0;
3588c293ef9SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->offset) < 0)
3598c293ef9SChuck Lever 		return 0;
3608c293ef9SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->count) < 0)
3618c293ef9SChuck Lever 		return 0;
3628c293ef9SChuck Lever 	/* totalcount is ignored */
3638c293ef9SChuck Lever 	if (xdr_stream_decode_u32(xdr, &totalcount) < 0)
3641da177e4SLinus Torvalds 		return 0;
3651da177e4SLinus Torvalds 
3668c293ef9SChuck Lever 	return 1;
3671da177e4SLinus Torvalds }
3681da177e4SLinus Torvalds 
3691da177e4SLinus Torvalds int
370026fec7eSChristoph Hellwig nfssvc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p)
3711da177e4SLinus Torvalds {
372a51b5b73SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
373026fec7eSChristoph Hellwig 	struct nfsd_writeargs *args = rqstp->rq_argp;
374db44bac4SJ. Bruce Fields 	struct kvec *head = rqstp->rq_arg.head;
375a51b5b73SChuck Lever 	struct kvec *tail = rqstp->rq_arg.tail;
376a51b5b73SChuck Lever 	u32 beginoffset, totalcount;
377a51b5b73SChuck Lever 	size_t remaining;
378f34b9568SPeter Staubach 
379a51b5b73SChuck Lever 	if (!svcxdr_decode_fhandle(xdr, &args->fh))
380a51b5b73SChuck Lever 		return 0;
381a51b5b73SChuck Lever 	/* beginoffset is ignored */
382a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &beginoffset) < 0)
383a51b5b73SChuck Lever 		return 0;
384a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->offset) < 0)
385a51b5b73SChuck Lever 		return 0;
386a51b5b73SChuck Lever 	/* totalcount is ignored */
387a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &totalcount) < 0)
3881da177e4SLinus Torvalds 		return 0;
3891da177e4SLinus Torvalds 
390a51b5b73SChuck Lever 	/* opaque data */
391a51b5b73SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->len) < 0)
392f34b9568SPeter Staubach 		return 0;
393a51b5b73SChuck Lever 	if (args->len > NFSSVC_MAXBLKSIZE_V2)
39413bf9fbfSJ. Bruce Fields 		return 0;
395a51b5b73SChuck Lever 	remaining = head->iov_len + rqstp->rq_arg.page_len + tail->iov_len;
396a51b5b73SChuck Lever 	remaining -= xdr_stream_pos(xdr);
397a51b5b73SChuck Lever 	if (remaining < xdr_align_size(args->len))
398f34b9568SPeter Staubach 		return 0;
399a51b5b73SChuck Lever 	args->first.iov_base = xdr->p;
400a51b5b73SChuck Lever 	args->first.iov_len = head->iov_len - xdr_stream_pos(xdr);
401f34b9568SPeter Staubach 
402f34b9568SPeter Staubach 	return 1;
4031da177e4SLinus Torvalds }
4041da177e4SLinus Torvalds 
4051da177e4SLinus Torvalds int
406026fec7eSChristoph Hellwig nfssvc_decode_createargs(struct svc_rqst *rqstp, __be32 *p)
4071da177e4SLinus Torvalds {
4087dcf65b9SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
409026fec7eSChristoph Hellwig 	struct nfsd_createargs *args = rqstp->rq_argp;
410026fec7eSChristoph Hellwig 
4117dcf65b9SChuck Lever 	return svcxdr_decode_diropargs(xdr, &args->fh,
4127dcf65b9SChuck Lever 				       &args->name, &args->len) &&
4137dcf65b9SChuck Lever 		svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
4141da177e4SLinus Torvalds }
4151da177e4SLinus Torvalds 
4161da177e4SLinus Torvalds int
417026fec7eSChristoph Hellwig nfssvc_decode_renameargs(struct svc_rqst *rqstp, __be32 *p)
4181da177e4SLinus Torvalds {
41962aa557eSChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
420026fec7eSChristoph Hellwig 	struct nfsd_renameargs *args = rqstp->rq_argp;
421026fec7eSChristoph Hellwig 
42262aa557eSChuck Lever 	return svcxdr_decode_diropargs(xdr, &args->ffh,
42362aa557eSChuck Lever 				       &args->fname, &args->flen) &&
42462aa557eSChuck Lever 		svcxdr_decode_diropargs(xdr, &args->tfh,
42562aa557eSChuck Lever 					&args->tname, &args->tlen);
4261da177e4SLinus Torvalds }
4271da177e4SLinus Torvalds 
4281da177e4SLinus Torvalds int
429026fec7eSChristoph Hellwig nfssvc_decode_linkargs(struct svc_rqst *rqstp, __be32 *p)
4301da177e4SLinus Torvalds {
43177edcdf9SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
432026fec7eSChristoph Hellwig 	struct nfsd_linkargs *args = rqstp->rq_argp;
433026fec7eSChristoph Hellwig 
43477edcdf9SChuck Lever 	return svcxdr_decode_fhandle(xdr, &args->ffh) &&
43577edcdf9SChuck Lever 		svcxdr_decode_diropargs(xdr, &args->tfh,
43677edcdf9SChuck Lever 					&args->tname, &args->tlen);
4371da177e4SLinus Torvalds }
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds int
440026fec7eSChristoph Hellwig nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
4411da177e4SLinus Torvalds {
44209f75a53SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
443026fec7eSChristoph Hellwig 	struct nfsd_symlinkargs *args = rqstp->rq_argp;
44409f75a53SChuck Lever 	struct kvec *head = rqstp->rq_arg.head;
445026fec7eSChristoph Hellwig 
44609f75a53SChuck Lever 	if (!svcxdr_decode_diropargs(xdr, &args->ffh, &args->fname, &args->flen))
4471da177e4SLinus Torvalds 		return 0;
44809f75a53SChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->tlen) < 0)
44909f75a53SChuck Lever 		return 0;
45038a70315SChuck Lever 	if (args->tlen == 0)
45138a70315SChuck Lever 		return 0;
45238a70315SChuck Lever 
45309f75a53SChuck Lever 	args->first.iov_len = head->iov_len - xdr_stream_pos(xdr);
45409f75a53SChuck Lever 	args->first.iov_base = xdr_inline_decode(xdr, args->tlen);
45509f75a53SChuck Lever 	if (!args->first.iov_base)
45638a70315SChuck Lever 		return 0;
45709f75a53SChuck Lever 	return svcxdr_decode_sattr(rqstp, xdr, &args->attrs);
4581da177e4SLinus Torvalds }
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds int
461026fec7eSChristoph Hellwig nfssvc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p)
4621da177e4SLinus Torvalds {
4638688361aSChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_arg_stream;
464026fec7eSChristoph Hellwig 	struct nfsd_readdirargs *args = rqstp->rq_argp;
465026fec7eSChristoph Hellwig 
4668688361aSChuck Lever 	if (!svcxdr_decode_fhandle(xdr, &args->fh))
4671da177e4SLinus Torvalds 		return 0;
4688688361aSChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->cookie) < 0)
4698688361aSChuck Lever 		return 0;
4708688361aSChuck Lever 	if (xdr_stream_decode_u32(xdr, &args->count) < 0)
4718688361aSChuck Lever 		return 0;
4721da177e4SLinus Torvalds 
4738688361aSChuck Lever 	return 1;
4741da177e4SLinus Torvalds }
4751da177e4SLinus Torvalds 
4761da177e4SLinus Torvalds /*
4771da177e4SLinus Torvalds  * XDR encode functions
4781da177e4SLinus Torvalds  */
4791da177e4SLinus Torvalds 
4801da177e4SLinus Torvalds int
481a887eaedSChuck Lever nfssvc_encode_statres(struct svc_rqst *rqstp, __be32 *p)
482cc028a10SChuck Lever {
483a887eaedSChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_res_stream;
484cc028a10SChuck Lever 	struct nfsd_stat *resp = rqstp->rq_resp;
485cc028a10SChuck Lever 
486a887eaedSChuck Lever 	return svcxdr_encode_stat(xdr, resp->status);
487cc028a10SChuck Lever }
488cc028a10SChuck Lever 
489cc028a10SChuck Lever int
49092b54a4fSChuck Lever nfssvc_encode_attrstatres(struct svc_rqst *rqstp, __be32 *p)
4911da177e4SLinus Torvalds {
49292b54a4fSChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_res_stream;
49363f8de37SChristoph Hellwig 	struct nfsd_attrstat *resp = rqstp->rq_resp;
49463f8de37SChristoph Hellwig 
49592b54a4fSChuck Lever 	if (!svcxdr_encode_stat(xdr, resp->status))
49692b54a4fSChuck Lever 		return 0;
49792b54a4fSChuck Lever 	switch (resp->status) {
49892b54a4fSChuck Lever 	case nfs_ok:
49992b54a4fSChuck Lever 		if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
50092b54a4fSChuck Lever 			return 0;
50192b54a4fSChuck Lever 		break;
50292b54a4fSChuck Lever 	}
50392b54a4fSChuck Lever 
50492b54a4fSChuck Lever 	return 1;
5051da177e4SLinus Torvalds }
5061da177e4SLinus Torvalds 
5071da177e4SLinus Torvalds int
50863f8de37SChristoph Hellwig nfssvc_encode_diropres(struct svc_rqst *rqstp, __be32 *p)
5091da177e4SLinus Torvalds {
510*e3b4ef22SChuck Lever 	struct xdr_stream *xdr = &rqstp->rq_res_stream;
51163f8de37SChristoph Hellwig 	struct nfsd_diropres *resp = rqstp->rq_resp;
51263f8de37SChristoph Hellwig 
513*e3b4ef22SChuck Lever 	if (!svcxdr_encode_stat(xdr, resp->status))
514*e3b4ef22SChuck Lever 		return 0;
515*e3b4ef22SChuck Lever 	switch (resp->status) {
516*e3b4ef22SChuck Lever 	case nfs_ok:
517*e3b4ef22SChuck Lever 		if (!svcxdr_encode_fhandle(xdr, &resp->fh))
518*e3b4ef22SChuck Lever 			return 0;
519*e3b4ef22SChuck Lever 		if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
520*e3b4ef22SChuck Lever 			return 0;
521*e3b4ef22SChuck Lever 		break;
522*e3b4ef22SChuck Lever 	}
523*e3b4ef22SChuck Lever 
524*e3b4ef22SChuck Lever 	return 1;
5251da177e4SLinus Torvalds }
5261da177e4SLinus Torvalds 
5271da177e4SLinus Torvalds int
52863f8de37SChristoph Hellwig nfssvc_encode_readlinkres(struct svc_rqst *rqstp, __be32 *p)
5291da177e4SLinus Torvalds {
53063f8de37SChristoph Hellwig 	struct nfsd_readlinkres *resp = rqstp->rq_resp;
53176e5492bSChuck Lever 	struct kvec *head = rqstp->rq_res.head;
53263f8de37SChristoph Hellwig 
533cc028a10SChuck Lever 	*p++ = resp->status;
534f0af2210SChuck Lever 	if (resp->status != nfs_ok)
535f0af2210SChuck Lever 		return xdr_ressize_check(rqstp, p);
536f0af2210SChuck Lever 
5371da177e4SLinus Torvalds 	*p++ = htonl(resp->len);
5381da177e4SLinus Torvalds 	xdr_ressize_check(rqstp, p);
5391da177e4SLinus Torvalds 	rqstp->rq_res.page_len = resp->len;
5401da177e4SLinus Torvalds 	if (resp->len & 3) {
5411da177e4SLinus Torvalds 		/* need to pad the tail */
5421da177e4SLinus Torvalds 		rqstp->rq_res.tail[0].iov_base = p;
5431da177e4SLinus Torvalds 		*p = 0;
5441da177e4SLinus Torvalds 		rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3);
5451da177e4SLinus Torvalds 	}
54676e5492bSChuck Lever 	if (svc_encode_result_payload(rqstp, head->iov_len, resp->len))
54776e5492bSChuck Lever 		return 0;
5481da177e4SLinus Torvalds 	return 1;
5491da177e4SLinus Torvalds }
5501da177e4SLinus Torvalds 
5511da177e4SLinus Torvalds int
55263f8de37SChristoph Hellwig nfssvc_encode_readres(struct svc_rqst *rqstp, __be32 *p)
5531da177e4SLinus Torvalds {
55463f8de37SChristoph Hellwig 	struct nfsd_readres *resp = rqstp->rq_resp;
55576e5492bSChuck Lever 	struct kvec *head = rqstp->rq_res.head;
55663f8de37SChristoph Hellwig 
557cc028a10SChuck Lever 	*p++ = resp->status;
558f0af2210SChuck Lever 	if (resp->status != nfs_ok)
559f0af2210SChuck Lever 		return xdr_ressize_check(rqstp, p);
560f0af2210SChuck Lever 
561a334de28SDavid Shaw 	p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
5621da177e4SLinus Torvalds 	*p++ = htonl(resp->count);
5631da177e4SLinus Torvalds 	xdr_ressize_check(rqstp, p);
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds 	/* now update rqstp->rq_res to reflect data as well */
5661da177e4SLinus Torvalds 	rqstp->rq_res.page_len = resp->count;
5671da177e4SLinus Torvalds 	if (resp->count & 3) {
5681da177e4SLinus Torvalds 		/* need to pad the tail */
5691da177e4SLinus Torvalds 		rqstp->rq_res.tail[0].iov_base = p;
5701da177e4SLinus Torvalds 		*p = 0;
5711da177e4SLinus Torvalds 		rqstp->rq_res.tail[0].iov_len = 4 - (resp->count&3);
5721da177e4SLinus Torvalds 	}
57376e5492bSChuck Lever 	if (svc_encode_result_payload(rqstp, head->iov_len, resp->count))
57476e5492bSChuck Lever 		return 0;
5751da177e4SLinus Torvalds 	return 1;
5761da177e4SLinus Torvalds }
5771da177e4SLinus Torvalds 
5781da177e4SLinus Torvalds int
57963f8de37SChristoph Hellwig nfssvc_encode_readdirres(struct svc_rqst *rqstp, __be32 *p)
5801da177e4SLinus Torvalds {
58163f8de37SChristoph Hellwig 	struct nfsd_readdirres *resp = rqstp->rq_resp;
58263f8de37SChristoph Hellwig 
583cc028a10SChuck Lever 	*p++ = resp->status;
584f0af2210SChuck Lever 	if (resp->status != nfs_ok)
585f0af2210SChuck Lever 		return xdr_ressize_check(rqstp, p);
586f0af2210SChuck Lever 
5871da177e4SLinus Torvalds 	xdr_ressize_check(rqstp, p);
5881da177e4SLinus Torvalds 	p = resp->buffer;
5891da177e4SLinus Torvalds 	*p++ = 0;			/* no more entries */
5901da177e4SLinus Torvalds 	*p++ = htonl((resp->common.err == nfserr_eof));
5911da177e4SLinus Torvalds 	rqstp->rq_res.page_len = (((unsigned long)p-1) & ~PAGE_MASK)+1;
5921da177e4SLinus Torvalds 
5931da177e4SLinus Torvalds 	return 1;
5941da177e4SLinus Torvalds }
5951da177e4SLinus Torvalds 
5961da177e4SLinus Torvalds int
59763f8de37SChristoph Hellwig nfssvc_encode_statfsres(struct svc_rqst *rqstp, __be32 *p)
5981da177e4SLinus Torvalds {
59963f8de37SChristoph Hellwig 	struct nfsd_statfsres *resp = rqstp->rq_resp;
6001da177e4SLinus Torvalds 	struct kstatfs	*stat = &resp->stats;
6011da177e4SLinus Torvalds 
602cc028a10SChuck Lever 	*p++ = resp->status;
603f0af2210SChuck Lever 	if (resp->status != nfs_ok)
604f0af2210SChuck Lever 		return xdr_ressize_check(rqstp, p);
605f0af2210SChuck Lever 
6067adae489SGreg Banks 	*p++ = htonl(NFSSVC_MAXBLKSIZE_V2);	/* max transfer size */
6071da177e4SLinus Torvalds 	*p++ = htonl(stat->f_bsize);
6081da177e4SLinus Torvalds 	*p++ = htonl(stat->f_blocks);
6091da177e4SLinus Torvalds 	*p++ = htonl(stat->f_bfree);
6101da177e4SLinus Torvalds 	*p++ = htonl(stat->f_bavail);
6111da177e4SLinus Torvalds 	return xdr_ressize_check(rqstp, p);
6121da177e4SLinus Torvalds }
6131da177e4SLinus Torvalds 
6141da177e4SLinus Torvalds int
615a0ad13efSNeilBrown nfssvc_encode_entry(void *ccdv, const char *name,
616a0ad13efSNeilBrown 		    int namlen, loff_t offset, u64 ino, unsigned int d_type)
6171da177e4SLinus Torvalds {
618a0ad13efSNeilBrown 	struct readdir_cd *ccd = ccdv;
6191da177e4SLinus Torvalds 	struct nfsd_readdirres *cd = container_of(ccd, struct nfsd_readdirres, common);
620131a21c2SAl Viro 	__be32	*p = cd->buffer;
6211da177e4SLinus Torvalds 	int	buflen, slen;
6221da177e4SLinus Torvalds 
6231da177e4SLinus Torvalds 	/*
6241da177e4SLinus Torvalds 	dprintk("nfsd: entry(%.*s off %ld ino %ld)\n",
6251da177e4SLinus Torvalds 			namlen, name, offset, ino);
6261da177e4SLinus Torvalds 	 */
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds 	if (offset > ~((u32) 0)) {
6291da177e4SLinus Torvalds 		cd->common.err = nfserr_fbig;
6301da177e4SLinus Torvalds 		return -EINVAL;
6311da177e4SLinus Torvalds 	}
6321da177e4SLinus Torvalds 	if (cd->offset)
6331da177e4SLinus Torvalds 		*cd->offset = htonl(offset);
6341da177e4SLinus Torvalds 
6353c7aa15dSKinglong Mee 	/* truncate filename */
6363c7aa15dSKinglong Mee 	namlen = min(namlen, NFS2_MAXNAMLEN);
6371da177e4SLinus Torvalds 	slen = XDR_QUADLEN(namlen);
6383c7aa15dSKinglong Mee 
6391da177e4SLinus Torvalds 	if ((buflen = cd->buflen - slen - 4) < 0) {
6401da177e4SLinus Torvalds 		cd->common.err = nfserr_toosmall;
6411da177e4SLinus Torvalds 		return -EINVAL;
6421da177e4SLinus Torvalds 	}
64340ee5dc6SPeter Staubach 	if (ino > ~((u32) 0)) {
64440ee5dc6SPeter Staubach 		cd->common.err = nfserr_fbig;
64540ee5dc6SPeter Staubach 		return -EINVAL;
64640ee5dc6SPeter Staubach 	}
6471da177e4SLinus Torvalds 	*p++ = xdr_one;				/* mark entry present */
6481da177e4SLinus Torvalds 	*p++ = htonl((u32) ino);		/* file id */
6491da177e4SLinus Torvalds 	p    = xdr_encode_array(p, name, namlen);/* name length & name */
6501da177e4SLinus Torvalds 	cd->offset = p;			/* remember pointer */
651131a21c2SAl Viro 	*p++ = htonl(~0U);		/* offset of next entry */
6521da177e4SLinus Torvalds 
6531da177e4SLinus Torvalds 	cd->buflen = buflen;
6541da177e4SLinus Torvalds 	cd->buffer = p;
6551da177e4SLinus Torvalds 	cd->common.err = nfs_ok;
6561da177e4SLinus Torvalds 	return 0;
6571da177e4SLinus Torvalds }
6581da177e4SLinus Torvalds 
6591da177e4SLinus Torvalds /*
6601da177e4SLinus Torvalds  * XDR release functions
6611da177e4SLinus Torvalds  */
6621841b9b6SChuck Lever void nfssvc_release_attrstat(struct svc_rqst *rqstp)
6631da177e4SLinus Torvalds {
6641841b9b6SChuck Lever 	struct nfsd_attrstat *resp = rqstp->rq_resp;
6651841b9b6SChuck Lever 
6661841b9b6SChuck Lever 	fh_put(&resp->fh);
6671841b9b6SChuck Lever }
6681841b9b6SChuck Lever 
6691841b9b6SChuck Lever void nfssvc_release_diropres(struct svc_rqst *rqstp)
6701841b9b6SChuck Lever {
6711841b9b6SChuck Lever 	struct nfsd_diropres *resp = rqstp->rq_resp;
6721841b9b6SChuck Lever 
6731841b9b6SChuck Lever 	fh_put(&resp->fh);
6741841b9b6SChuck Lever }
6751841b9b6SChuck Lever 
6761841b9b6SChuck Lever void nfssvc_release_readres(struct svc_rqst *rqstp)
6771841b9b6SChuck Lever {
6781841b9b6SChuck Lever 	struct nfsd_readres *resp = rqstp->rq_resp;
6798537488bSChristoph Hellwig 
6801da177e4SLinus Torvalds 	fh_put(&resp->fh);
6811da177e4SLinus Torvalds }
682