xref: /openbmc/linux/fs/nfs/nfs2xdr.c (revision e86d5a02)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * linux/fs/nfs/nfs2xdr.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * XDR functions to encode/decode NFS RPC arguments and results.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Copyright (C) 1992, 1993, 1994  Rick Sladkey
81da177e4SLinus Torvalds  * Copyright (C) 1996 Olaf Kirch
91da177e4SLinus Torvalds  * 04 Aug 1998  Ion Badulescu <ionut@cs.columbia.edu>
101da177e4SLinus Torvalds  * 		FIFO's need special handling in NFSv2
111da177e4SLinus Torvalds  */
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds #include <linux/param.h>
141da177e4SLinus Torvalds #include <linux/time.h>
151da177e4SLinus Torvalds #include <linux/mm.h>
161da177e4SLinus Torvalds #include <linux/errno.h>
171da177e4SLinus Torvalds #include <linux/string.h>
181da177e4SLinus Torvalds #include <linux/in.h>
191da177e4SLinus Torvalds #include <linux/pagemap.h>
201da177e4SLinus Torvalds #include <linux/proc_fs.h>
211da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
221da177e4SLinus Torvalds #include <linux/nfs.h>
231da177e4SLinus Torvalds #include <linux/nfs2.h>
241da177e4SLinus Torvalds #include <linux/nfs_fs.h>
25f23f6584SChuck Lever #include "nfstrace.h"
26816724e6STrond Myklebust #include "internal.h"
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds #define NFSDBG_FACILITY		NFSDBG_XDR
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds /* Mapping from NFS error code to "errno" error code. */
311da177e4SLinus Torvalds #define errno_NFSERR_IO		EIO
321da177e4SLinus Torvalds 
331da177e4SLinus Torvalds /*
341da177e4SLinus Torvalds  * Declare the space requirements for NFS arguments and replies as
351da177e4SLinus Torvalds  * number of 32bit-words
361da177e4SLinus Torvalds  */
371da177e4SLinus Torvalds #define NFS_fhandle_sz		(8)
381da177e4SLinus Torvalds #define NFS_sattr_sz		(8)
391da177e4SLinus Torvalds #define NFS_filename_sz		(1+(NFS2_MAXNAMLEN>>2))
401da177e4SLinus Torvalds #define NFS_path_sz		(1+(NFS2_MAXPATHLEN>>2))
411da177e4SLinus Torvalds #define NFS_fattr_sz		(17)
421da177e4SLinus Torvalds #define NFS_info_sz		(5)
431da177e4SLinus Torvalds #define NFS_entry_sz		(NFS_filename_sz+3)
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds #define NFS_diropargs_sz	(NFS_fhandle_sz+NFS_filename_sz)
464fdc17b2STrond Myklebust #define NFS_removeargs_sz	(NFS_fhandle_sz+NFS_filename_sz)
471da177e4SLinus Torvalds #define NFS_sattrargs_sz	(NFS_fhandle_sz+NFS_sattr_sz)
481da177e4SLinus Torvalds #define NFS_readlinkargs_sz	(NFS_fhandle_sz)
491da177e4SLinus Torvalds #define NFS_readargs_sz		(NFS_fhandle_sz+3)
501da177e4SLinus Torvalds #define NFS_writeargs_sz	(NFS_fhandle_sz+4)
511da177e4SLinus Torvalds #define NFS_createargs_sz	(NFS_diropargs_sz+NFS_sattr_sz)
521da177e4SLinus Torvalds #define NFS_renameargs_sz	(NFS_diropargs_sz+NFS_diropargs_sz)
531da177e4SLinus Torvalds #define NFS_linkargs_sz		(NFS_fhandle_sz+NFS_diropargs_sz)
5494a6d753SChuck Lever #define NFS_symlinkargs_sz	(NFS_diropargs_sz+1+NFS_sattr_sz)
551da177e4SLinus Torvalds #define NFS_readdirargs_sz	(NFS_fhandle_sz+2)
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds #define NFS_attrstat_sz		(1+NFS_fattr_sz)
581da177e4SLinus Torvalds #define NFS_diropres_sz		(1+NFS_fhandle_sz+NFS_fattr_sz)
5902ef04e4SChuck Lever #define NFS_readlinkres_sz	(2+1)
6002ef04e4SChuck Lever #define NFS_readres_sz		(1+NFS_fattr_sz+1+1)
611da177e4SLinus Torvalds #define NFS_writeres_sz         (NFS_attrstat_sz)
621da177e4SLinus Torvalds #define NFS_stat_sz		(1)
6302ef04e4SChuck Lever #define NFS_readdirres_sz	(1+1)
641da177e4SLinus Torvalds #define NFS_statfsres_sz	(1+NFS_info_sz)
651da177e4SLinus Torvalds 
665e7e5a0dSBryan Schumaker static int nfs_stat_to_errno(enum nfs_stat);
6725a0866cSChuck Lever 
6825a0866cSChuck Lever /*
6925a0866cSChuck Lever  * Encode/decode NFSv2 basic data types
7025a0866cSChuck Lever  *
7125a0866cSChuck Lever  * Basic NFSv2 data types are defined in section 2.3 of RFC 1094:
7225a0866cSChuck Lever  * "NFS: Network File System Protocol Specification".
7325a0866cSChuck Lever  *
7425a0866cSChuck Lever  * Not all basic data types have their own encoding and decoding
7525a0866cSChuck Lever  * functions.  For run-time efficiency, some data types are encoded
7625a0866cSChuck Lever  * or decoded inline.
7725a0866cSChuck Lever  */
7825a0866cSChuck Lever 
79c207db2fSTrond Myklebust static struct user_namespace *rpc_userns(const struct rpc_clnt *clnt)
80c207db2fSTrond Myklebust {
81c207db2fSTrond Myklebust 	if (clnt && clnt->cl_cred)
82c207db2fSTrond Myklebust 		return clnt->cl_cred->user_ns;
83c207db2fSTrond Myklebust 	return &init_user_ns;
84c207db2fSTrond Myklebust }
85c207db2fSTrond Myklebust 
86c207db2fSTrond Myklebust static struct user_namespace *rpc_rqst_userns(const struct rpc_rqst *rqstp)
87c207db2fSTrond Myklebust {
88c207db2fSTrond Myklebust 	if (rqstp->rq_task)
89c207db2fSTrond Myklebust 		return rpc_userns(rqstp->rq_task->tk_client);
90c207db2fSTrond Myklebust 	return &init_user_ns;
91c207db2fSTrond Myklebust }
92c207db2fSTrond Myklebust 
9325a0866cSChuck Lever /*
94f796f8b3SChuck Lever  *	typedef opaque	nfsdata<>;
95f796f8b3SChuck Lever  */
969137bdf3SAnna Schumaker static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_pgio_res *result)
97f796f8b3SChuck Lever {
98f796f8b3SChuck Lever 	u32 recvd, count;
99f796f8b3SChuck Lever 	__be32 *p;
100f796f8b3SChuck Lever 
101f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
102eb72f484SChuck Lever 	if (unlikely(!p))
103eb72f484SChuck Lever 		return -EIO;
104f796f8b3SChuck Lever 	count = be32_to_cpup(p);
10564bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, count);
106f796f8b3SChuck Lever 	if (unlikely(count > recvd))
107f796f8b3SChuck Lever 		goto out_cheating;
108f796f8b3SChuck Lever out:
109f796f8b3SChuck Lever 	result->eof = 0;	/* NFSv2 does not pass EOF flag on the wire. */
110f796f8b3SChuck Lever 	result->count = count;
111f796f8b3SChuck Lever 	return count;
112f796f8b3SChuck Lever out_cheating:
113f796f8b3SChuck Lever 	dprintk("NFS: server cheating in read result: "
114f796f8b3SChuck Lever 		"count %u > recvd %u\n", count, recvd);
115f796f8b3SChuck Lever 	count = recvd;
116f796f8b3SChuck Lever 	goto out;
117f796f8b3SChuck Lever }
118f796f8b3SChuck Lever 
119f796f8b3SChuck Lever /*
120f796f8b3SChuck Lever  *	enum stat {
121f796f8b3SChuck Lever  *		NFS_OK = 0,
122f796f8b3SChuck Lever  *		NFSERR_PERM = 1,
123f796f8b3SChuck Lever  *		NFSERR_NOENT = 2,
124f796f8b3SChuck Lever  *		NFSERR_IO = 5,
125f796f8b3SChuck Lever  *		NFSERR_NXIO = 6,
126f796f8b3SChuck Lever  *		NFSERR_ACCES = 13,
127f796f8b3SChuck Lever  *		NFSERR_EXIST = 17,
128f796f8b3SChuck Lever  *		NFSERR_NODEV = 19,
129f796f8b3SChuck Lever  *		NFSERR_NOTDIR = 20,
130f796f8b3SChuck Lever  *		NFSERR_ISDIR = 21,
131f796f8b3SChuck Lever  *		NFSERR_FBIG = 27,
132f796f8b3SChuck Lever  *		NFSERR_NOSPC = 28,
133f796f8b3SChuck Lever  *		NFSERR_ROFS = 30,
134f796f8b3SChuck Lever  *		NFSERR_NAMETOOLONG = 63,
135f796f8b3SChuck Lever  *		NFSERR_NOTEMPTY = 66,
136f796f8b3SChuck Lever  *		NFSERR_DQUOT = 69,
137f796f8b3SChuck Lever  *		NFSERR_STALE = 70,
138f796f8b3SChuck Lever  *		NFSERR_WFLUSH = 99
139f796f8b3SChuck Lever  *	};
140f796f8b3SChuck Lever  */
141f796f8b3SChuck Lever static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status)
142f796f8b3SChuck Lever {
143f796f8b3SChuck Lever 	__be32 *p;
144f796f8b3SChuck Lever 
145f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
146eb72f484SChuck Lever 	if (unlikely(!p))
147eb72f484SChuck Lever 		return -EIO;
148f23f6584SChuck Lever 	if (unlikely(*p != cpu_to_be32(NFS_OK)))
149f23f6584SChuck Lever 		goto out_status;
150f23f6584SChuck Lever 	*status = 0;
151f23f6584SChuck Lever 	return 0;
152f23f6584SChuck Lever out_status:
153f796f8b3SChuck Lever 	*status = be32_to_cpup(p);
15462a92ba9SChuck Lever 	trace_nfs_xdr_status(xdr, (int)*status);
155f796f8b3SChuck Lever 	return 0;
156f796f8b3SChuck Lever }
157f796f8b3SChuck Lever 
158f796f8b3SChuck Lever /*
1595f96e5e3SChuck Lever  * 2.3.2.  ftype
1605f96e5e3SChuck Lever  *
1615f96e5e3SChuck Lever  *	enum ftype {
1625f96e5e3SChuck Lever  *		NFNON = 0,
1635f96e5e3SChuck Lever  *		NFREG = 1,
1645f96e5e3SChuck Lever  *		NFDIR = 2,
1655f96e5e3SChuck Lever  *		NFBLK = 3,
1665f96e5e3SChuck Lever  *		NFCHR = 4,
1675f96e5e3SChuck Lever  *		NFLNK = 5
1685f96e5e3SChuck Lever  *	};
1695f96e5e3SChuck Lever  *
1705f96e5e3SChuck Lever  */
1715f96e5e3SChuck Lever static __be32 *xdr_decode_ftype(__be32 *p, u32 *type)
1725f96e5e3SChuck Lever {
1735f96e5e3SChuck Lever 	*type = be32_to_cpup(p++);
1745f96e5e3SChuck Lever 	if (unlikely(*type > NF2FIFO))
1755f96e5e3SChuck Lever 		*type = NFBAD;
1765f96e5e3SChuck Lever 	return p;
1775f96e5e3SChuck Lever }
1785f96e5e3SChuck Lever 
1795f96e5e3SChuck Lever /*
18025a0866cSChuck Lever  * 2.3.3.  fhandle
18125a0866cSChuck Lever  *
18225a0866cSChuck Lever  *	typedef opaque fhandle[FHSIZE];
18325a0866cSChuck Lever  */
18425a0866cSChuck Lever static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh)
18525a0866cSChuck Lever {
18625a0866cSChuck Lever 	__be32 *p;
18725a0866cSChuck Lever 
18825a0866cSChuck Lever 	p = xdr_reserve_space(xdr, NFS2_FHSIZE);
18925a0866cSChuck Lever 	memcpy(p, fh->data, NFS2_FHSIZE);
19025a0866cSChuck Lever }
19125a0866cSChuck Lever 
192f796f8b3SChuck Lever static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh)
193f796f8b3SChuck Lever {
194f796f8b3SChuck Lever 	__be32 *p;
195f796f8b3SChuck Lever 
196f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS2_FHSIZE);
197eb72f484SChuck Lever 	if (unlikely(!p))
198eb72f484SChuck Lever 		return -EIO;
199f796f8b3SChuck Lever 	fh->size = NFS2_FHSIZE;
200f796f8b3SChuck Lever 	memcpy(fh->data, p, NFS2_FHSIZE);
201f796f8b3SChuck Lever 	return 0;
202f796f8b3SChuck Lever }
203f796f8b3SChuck Lever 
20425a0866cSChuck Lever /*
205282ac2a5SChuck Lever  * 2.3.4.  timeval
206282ac2a5SChuck Lever  *
207282ac2a5SChuck Lever  *	struct timeval {
208282ac2a5SChuck Lever  *		unsigned int seconds;
209282ac2a5SChuck Lever  *		unsigned int useconds;
210282ac2a5SChuck Lever  *	};
211282ac2a5SChuck Lever  */
212282ac2a5SChuck Lever static __be32 *xdr_encode_time(__be32 *p, const struct timespec *timep)
213282ac2a5SChuck Lever {
214282ac2a5SChuck Lever 	*p++ = cpu_to_be32(timep->tv_sec);
215282ac2a5SChuck Lever 	if (timep->tv_nsec != 0)
216282ac2a5SChuck Lever 		*p++ = cpu_to_be32(timep->tv_nsec / NSEC_PER_USEC);
217282ac2a5SChuck Lever 	else
218282ac2a5SChuck Lever 		*p++ = cpu_to_be32(0);
219282ac2a5SChuck Lever 	return p;
220282ac2a5SChuck Lever }
221282ac2a5SChuck Lever 
222282ac2a5SChuck Lever /*
223282ac2a5SChuck Lever  * Passing the invalid value useconds=1000000 is a Sun convention for
224282ac2a5SChuck Lever  * "set to current server time".  It's needed to make permissions checks
225282ac2a5SChuck Lever  * for the "touch" program across v2 mounts to Solaris and Irix servers
226282ac2a5SChuck Lever  * work correctly.  See description of sattr in section 6.1 of "NFS
227282ac2a5SChuck Lever  * Illustrated" by Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5.
228282ac2a5SChuck Lever  */
229282ac2a5SChuck Lever static __be32 *xdr_encode_current_server_time(__be32 *p,
230282ac2a5SChuck Lever 					      const struct timespec *timep)
231282ac2a5SChuck Lever {
232282ac2a5SChuck Lever 	*p++ = cpu_to_be32(timep->tv_sec);
233282ac2a5SChuck Lever 	*p++ = cpu_to_be32(1000000);
234282ac2a5SChuck Lever 	return p;
235282ac2a5SChuck Lever }
236282ac2a5SChuck Lever 
237e86d5a02STrond Myklebust static __be32 *xdr_decode_time(__be32 *p, struct timespec64 *timep)
2385f96e5e3SChuck Lever {
2395f96e5e3SChuck Lever 	timep->tv_sec = be32_to_cpup(p++);
2405f96e5e3SChuck Lever 	timep->tv_nsec = be32_to_cpup(p++) * NSEC_PER_USEC;
2415f96e5e3SChuck Lever 	return p;
2425f96e5e3SChuck Lever }
2435f96e5e3SChuck Lever 
244282ac2a5SChuck Lever /*
245f796f8b3SChuck Lever  * 2.3.5.  fattr
246f796f8b3SChuck Lever  *
247f796f8b3SChuck Lever  *	struct fattr {
248f796f8b3SChuck Lever  *		ftype		type;
249f796f8b3SChuck Lever  *		unsigned int	mode;
250f796f8b3SChuck Lever  *		unsigned int	nlink;
251f796f8b3SChuck Lever  *		unsigned int	uid;
252f796f8b3SChuck Lever  *		unsigned int	gid;
253f796f8b3SChuck Lever  *		unsigned int	size;
254f796f8b3SChuck Lever  *		unsigned int	blocksize;
255f796f8b3SChuck Lever  *		unsigned int	rdev;
256f796f8b3SChuck Lever  *		unsigned int	blocks;
257f796f8b3SChuck Lever  *		unsigned int	fsid;
258f796f8b3SChuck Lever  *		unsigned int	fileid;
259f796f8b3SChuck Lever  *		timeval		atime;
260f796f8b3SChuck Lever  *		timeval		mtime;
261f796f8b3SChuck Lever  *		timeval		ctime;
262f796f8b3SChuck Lever  *	};
263f796f8b3SChuck Lever  *
264f796f8b3SChuck Lever  */
265c207db2fSTrond Myklebust static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
266c207db2fSTrond Myklebust 		struct user_namespace *userns)
267f796f8b3SChuck Lever {
2685f96e5e3SChuck Lever 	u32 rdev, type;
269f796f8b3SChuck Lever 	__be32 *p;
270f796f8b3SChuck Lever 
271f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS_fattr_sz << 2);
272eb72f484SChuck Lever 	if (unlikely(!p))
273eb72f484SChuck Lever 		return -EIO;
2745f96e5e3SChuck Lever 
2755f96e5e3SChuck Lever 	fattr->valid |= NFS_ATTR_FATTR_V2;
2765f96e5e3SChuck Lever 
2775f96e5e3SChuck Lever 	p = xdr_decode_ftype(p, &type);
2785f96e5e3SChuck Lever 
2795f96e5e3SChuck Lever 	fattr->mode = be32_to_cpup(p++);
2805f96e5e3SChuck Lever 	fattr->nlink = be32_to_cpup(p++);
281c207db2fSTrond Myklebust 	fattr->uid = make_kuid(userns, be32_to_cpup(p++));
282cfa0898dSEric W. Biederman 	if (!uid_valid(fattr->uid))
283cfa0898dSEric W. Biederman 		goto out_uid;
284c207db2fSTrond Myklebust 	fattr->gid = make_kgid(userns, be32_to_cpup(p++));
285cfa0898dSEric W. Biederman 	if (!gid_valid(fattr->gid))
286cfa0898dSEric W. Biederman 		goto out_gid;
287cfa0898dSEric W. Biederman 
2885f96e5e3SChuck Lever 	fattr->size = be32_to_cpup(p++);
2895f96e5e3SChuck Lever 	fattr->du.nfs2.blocksize = be32_to_cpup(p++);
2905f96e5e3SChuck Lever 
2915f96e5e3SChuck Lever 	rdev = be32_to_cpup(p++);
2925f96e5e3SChuck Lever 	fattr->rdev = new_decode_dev(rdev);
2935f96e5e3SChuck Lever 	if (type == (u32)NFCHR && rdev == (u32)NFS2_FIFO_DEV) {
2945f96e5e3SChuck Lever 		fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
2955f96e5e3SChuck Lever 		fattr->rdev = 0;
2965f96e5e3SChuck Lever 	}
2975f96e5e3SChuck Lever 
2985f96e5e3SChuck Lever 	fattr->du.nfs2.blocks = be32_to_cpup(p++);
2995f96e5e3SChuck Lever 	fattr->fsid.major = be32_to_cpup(p++);
3005f96e5e3SChuck Lever 	fattr->fsid.minor = 0;
3015f96e5e3SChuck Lever 	fattr->fileid = be32_to_cpup(p++);
3025f96e5e3SChuck Lever 
3035f96e5e3SChuck Lever 	p = xdr_decode_time(p, &fattr->atime);
3045f96e5e3SChuck Lever 	p = xdr_decode_time(p, &fattr->mtime);
3055f96e5e3SChuck Lever 	xdr_decode_time(p, &fattr->ctime);
3063a1556e8STrond Myklebust 	fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime);
3073a1556e8STrond Myklebust 
308f796f8b3SChuck Lever 	return 0;
309cfa0898dSEric W. Biederman out_uid:
310cfa0898dSEric W. Biederman 	dprintk("NFS: returned invalid uid\n");
311cfa0898dSEric W. Biederman 	return -EINVAL;
312cfa0898dSEric W. Biederman out_gid:
313cfa0898dSEric W. Biederman 	dprintk("NFS: returned invalid gid\n");
314cfa0898dSEric W. Biederman 	return -EINVAL;
315f796f8b3SChuck Lever }
316f796f8b3SChuck Lever 
317f796f8b3SChuck Lever /*
31825a0866cSChuck Lever  * 2.3.6.  sattr
31925a0866cSChuck Lever  *
32025a0866cSChuck Lever  *	struct sattr {
32125a0866cSChuck Lever  *		unsigned int	mode;
32225a0866cSChuck Lever  *		unsigned int	uid;
32325a0866cSChuck Lever  *		unsigned int	gid;
32425a0866cSChuck Lever  *		unsigned int	size;
32525a0866cSChuck Lever  *		timeval		atime;
32625a0866cSChuck Lever  *		timeval		mtime;
32725a0866cSChuck Lever  *	};
32825a0866cSChuck Lever  */
32925a0866cSChuck Lever 
33025a0866cSChuck Lever #define NFS2_SATTR_NOT_SET	(0xffffffff)
33125a0866cSChuck Lever 
33225a0866cSChuck Lever static __be32 *xdr_time_not_set(__be32 *p)
33325a0866cSChuck Lever {
33425a0866cSChuck Lever 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
33525a0866cSChuck Lever 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
33625a0866cSChuck Lever 	return p;
33725a0866cSChuck Lever }
33825a0866cSChuck Lever 
339c207db2fSTrond Myklebust static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr,
340c207db2fSTrond Myklebust 		struct user_namespace *userns)
34125a0866cSChuck Lever {
34295582b00SDeepa Dinamani 	struct timespec ts;
34325a0866cSChuck Lever 	__be32 *p;
34425a0866cSChuck Lever 
34525a0866cSChuck Lever 	p = xdr_reserve_space(xdr, NFS_sattr_sz << 2);
34625a0866cSChuck Lever 
34725a0866cSChuck Lever 	if (attr->ia_valid & ATTR_MODE)
34825a0866cSChuck Lever 		*p++ = cpu_to_be32(attr->ia_mode);
34925a0866cSChuck Lever 	else
35025a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
35125a0866cSChuck Lever 	if (attr->ia_valid & ATTR_UID)
352c207db2fSTrond Myklebust 		*p++ = cpu_to_be32(from_kuid_munged(userns, attr->ia_uid));
35325a0866cSChuck Lever 	else
35425a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
35525a0866cSChuck Lever 	if (attr->ia_valid & ATTR_GID)
356c207db2fSTrond Myklebust 		*p++ = cpu_to_be32(from_kgid_munged(userns, attr->ia_gid));
35725a0866cSChuck Lever 	else
35825a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
35925a0866cSChuck Lever 	if (attr->ia_valid & ATTR_SIZE)
36025a0866cSChuck Lever 		*p++ = cpu_to_be32((u32)attr->ia_size);
36125a0866cSChuck Lever 	else
36225a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
36325a0866cSChuck Lever 
36495582b00SDeepa Dinamani 	if (attr->ia_valid & ATTR_ATIME_SET) {
36595582b00SDeepa Dinamani 		ts = timespec64_to_timespec(attr->ia_atime);
36695582b00SDeepa Dinamani 		p = xdr_encode_time(p, &ts);
36795582b00SDeepa Dinamani 	} else if (attr->ia_valid & ATTR_ATIME) {
36895582b00SDeepa Dinamani 		ts = timespec64_to_timespec(attr->ia_atime);
36995582b00SDeepa Dinamani 		p = xdr_encode_current_server_time(p, &ts);
37095582b00SDeepa Dinamani 	} else
37125a0866cSChuck Lever 		p = xdr_time_not_set(p);
37295582b00SDeepa Dinamani 	if (attr->ia_valid & ATTR_MTIME_SET) {
37395582b00SDeepa Dinamani 		ts = timespec64_to_timespec(attr->ia_atime);
37495582b00SDeepa Dinamani 		xdr_encode_time(p, &ts);
37595582b00SDeepa Dinamani 	} else if (attr->ia_valid & ATTR_MTIME) {
37695582b00SDeepa Dinamani 		ts = timespec64_to_timespec(attr->ia_mtime);
37795582b00SDeepa Dinamani 		xdr_encode_current_server_time(p, &ts);
37895582b00SDeepa Dinamani 	} else
37925a0866cSChuck Lever 		xdr_time_not_set(p);
38025a0866cSChuck Lever }
38125a0866cSChuck Lever 
38225a0866cSChuck Lever /*
38325a0866cSChuck Lever  * 2.3.7.  filename
38425a0866cSChuck Lever  *
38525a0866cSChuck Lever  *	typedef string filename<MAXNAMLEN>;
38625a0866cSChuck Lever  */
38725a0866cSChuck Lever static void encode_filename(struct xdr_stream *xdr,
38825a0866cSChuck Lever 			    const char *name, u32 length)
38925a0866cSChuck Lever {
39025a0866cSChuck Lever 	__be32 *p;
39125a0866cSChuck Lever 
3927fc38846STrond Myklebust 	WARN_ON_ONCE(length > NFS2_MAXNAMLEN);
39325a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + length);
39425a0866cSChuck Lever 	xdr_encode_opaque(p, name, length);
39525a0866cSChuck Lever }
39625a0866cSChuck Lever 
397f796f8b3SChuck Lever static int decode_filename_inline(struct xdr_stream *xdr,
398f796f8b3SChuck Lever 				  const char **name, u32 *length)
399f796f8b3SChuck Lever {
400f796f8b3SChuck Lever 	__be32 *p;
401f796f8b3SChuck Lever 	u32 count;
402f796f8b3SChuck Lever 
403f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
404eb72f484SChuck Lever 	if (unlikely(!p))
405eb72f484SChuck Lever 		return -EIO;
406f796f8b3SChuck Lever 	count = be32_to_cpup(p);
407f796f8b3SChuck Lever 	if (count > NFS3_MAXNAMLEN)
408f796f8b3SChuck Lever 		goto out_nametoolong;
409f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, count);
410eb72f484SChuck Lever 	if (unlikely(!p))
411eb72f484SChuck Lever 		return -EIO;
412f796f8b3SChuck Lever 	*name = (const char *)p;
413f796f8b3SChuck Lever 	*length = count;
414f796f8b3SChuck Lever 	return 0;
415f796f8b3SChuck Lever out_nametoolong:
416f796f8b3SChuck Lever 	dprintk("NFS: returned filename too long: %u\n", count);
417f796f8b3SChuck Lever 	return -ENAMETOOLONG;
418f796f8b3SChuck Lever }
419f796f8b3SChuck Lever 
42025a0866cSChuck Lever /*
42125a0866cSChuck Lever  * 2.3.8.  path
42225a0866cSChuck Lever  *
42325a0866cSChuck Lever  *	typedef string path<MAXPATHLEN>;
42425a0866cSChuck Lever  */
42525a0866cSChuck Lever static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length)
42625a0866cSChuck Lever {
42725a0866cSChuck Lever 	__be32 *p;
42825a0866cSChuck Lever 
42925a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4);
43025a0866cSChuck Lever 	*p = cpu_to_be32(length);
43125a0866cSChuck Lever 	xdr_write_pages(xdr, pages, 0, length);
43225a0866cSChuck Lever }
43325a0866cSChuck Lever 
434f796f8b3SChuck Lever static int decode_path(struct xdr_stream *xdr)
435f796f8b3SChuck Lever {
436f796f8b3SChuck Lever 	u32 length, recvd;
437f796f8b3SChuck Lever 	__be32 *p;
438f796f8b3SChuck Lever 
439f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
440eb72f484SChuck Lever 	if (unlikely(!p))
441eb72f484SChuck Lever 		return -EIO;
442f796f8b3SChuck Lever 	length = be32_to_cpup(p);
443f796f8b3SChuck Lever 	if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN))
444f796f8b3SChuck Lever 		goto out_size;
44564bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, length);
446f796f8b3SChuck Lever 	if (unlikely(length > recvd))
447f796f8b3SChuck Lever 		goto out_cheating;
448f796f8b3SChuck Lever 	xdr_terminate_string(xdr->buf, length);
449f796f8b3SChuck Lever 	return 0;
450f796f8b3SChuck Lever out_size:
451f796f8b3SChuck Lever 	dprintk("NFS: returned pathname too long: %u\n", length);
452f796f8b3SChuck Lever 	return -ENAMETOOLONG;
453f796f8b3SChuck Lever out_cheating:
454f796f8b3SChuck Lever 	dprintk("NFS: server cheating in pathname result: "
455f796f8b3SChuck Lever 		"length %u > received %u\n", length, recvd);
456f796f8b3SChuck Lever 	return -EIO;
457f796f8b3SChuck Lever }
458f796f8b3SChuck Lever 
459f796f8b3SChuck Lever /*
460f796f8b3SChuck Lever  * 2.3.9.  attrstat
461f796f8b3SChuck Lever  *
462f796f8b3SChuck Lever  *	union attrstat switch (stat status) {
463f796f8b3SChuck Lever  *	case NFS_OK:
464f796f8b3SChuck Lever  *		fattr attributes;
465f796f8b3SChuck Lever  *	default:
466f796f8b3SChuck Lever  *		void;
467f796f8b3SChuck Lever  *	};
468f796f8b3SChuck Lever  */
469aabff4ddSPeng Tao static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result,
470c207db2fSTrond Myklebust 			   __u32 *op_status,
471c207db2fSTrond Myklebust 			   struct user_namespace *userns)
472f796f8b3SChuck Lever {
473f796f8b3SChuck Lever 	enum nfs_stat status;
474f796f8b3SChuck Lever 	int error;
475f796f8b3SChuck Lever 
476f796f8b3SChuck Lever 	error = decode_stat(xdr, &status);
477f796f8b3SChuck Lever 	if (unlikely(error))
478f796f8b3SChuck Lever 		goto out;
479aabff4ddSPeng Tao 	if (op_status)
480aabff4ddSPeng Tao 		*op_status = status;
481f796f8b3SChuck Lever 	if (status != NFS_OK)
482f796f8b3SChuck Lever 		goto out_default;
483c207db2fSTrond Myklebust 	error = decode_fattr(xdr, result, userns);
484f796f8b3SChuck Lever out:
485f796f8b3SChuck Lever 	return error;
486f796f8b3SChuck Lever out_default:
487f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
488f796f8b3SChuck Lever }
489f796f8b3SChuck Lever 
49025a0866cSChuck Lever /*
49125a0866cSChuck Lever  * 2.3.10.  diropargs
49225a0866cSChuck Lever  *
49325a0866cSChuck Lever  *	struct diropargs {
49425a0866cSChuck Lever  *		fhandle  dir;
49525a0866cSChuck Lever  *		filename name;
49625a0866cSChuck Lever  *	};
49725a0866cSChuck Lever  */
49825a0866cSChuck Lever static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh,
49925a0866cSChuck Lever 			     const char *name, u32 length)
50025a0866cSChuck Lever {
50125a0866cSChuck Lever 	encode_fhandle(xdr, fh);
50225a0866cSChuck Lever 	encode_filename(xdr, name, length);
50325a0866cSChuck Lever }
50425a0866cSChuck Lever 
505f796f8b3SChuck Lever /*
506f796f8b3SChuck Lever  * 2.3.11.  diropres
507f796f8b3SChuck Lever  *
508f796f8b3SChuck Lever  *	union diropres switch (stat status) {
509f796f8b3SChuck Lever  *	case NFS_OK:
510f796f8b3SChuck Lever  *		struct {
511f796f8b3SChuck Lever  *			fhandle file;
512f796f8b3SChuck Lever  *			fattr   attributes;
513f796f8b3SChuck Lever  *		} diropok;
514f796f8b3SChuck Lever  *	default:
515f796f8b3SChuck Lever  *		void;
516f796f8b3SChuck Lever  *	};
517f796f8b3SChuck Lever  */
518c207db2fSTrond Myklebust static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result,
519c207db2fSTrond Myklebust 		struct user_namespace *userns)
520f796f8b3SChuck Lever {
521f796f8b3SChuck Lever 	int error;
522f796f8b3SChuck Lever 
523f796f8b3SChuck Lever 	error = decode_fhandle(xdr, result->fh);
524f796f8b3SChuck Lever 	if (unlikely(error))
525f796f8b3SChuck Lever 		goto out;
526c207db2fSTrond Myklebust 	error = decode_fattr(xdr, result->fattr, userns);
527f796f8b3SChuck Lever out:
528f796f8b3SChuck Lever 	return error;
529f796f8b3SChuck Lever }
530f796f8b3SChuck Lever 
531c207db2fSTrond Myklebust static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result,
532c207db2fSTrond Myklebust 		struct user_namespace *userns)
533f796f8b3SChuck Lever {
534f796f8b3SChuck Lever 	enum nfs_stat status;
535f796f8b3SChuck Lever 	int error;
536f796f8b3SChuck Lever 
537f796f8b3SChuck Lever 	error = decode_stat(xdr, &status);
538f796f8b3SChuck Lever 	if (unlikely(error))
539f796f8b3SChuck Lever 		goto out;
540f796f8b3SChuck Lever 	if (status != NFS_OK)
541f796f8b3SChuck Lever 		goto out_default;
542c207db2fSTrond Myklebust 	error = decode_diropok(xdr, result, userns);
543f796f8b3SChuck Lever out:
544f796f8b3SChuck Lever 	return error;
545f796f8b3SChuck Lever out_default:
546f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
547f796f8b3SChuck Lever }
548f796f8b3SChuck Lever 
54925a0866cSChuck Lever 
55025a0866cSChuck Lever /*
5512d70f533SChuck Lever  * NFSv2 XDR encode functions
5522d70f533SChuck Lever  *
5532d70f533SChuck Lever  * NFSv2 argument types are defined in section 2.2 of RFC 1094:
5542d70f533SChuck Lever  * "NFS: Network File System Protocol Specification".
5551da177e4SLinus Torvalds  */
5561da177e4SLinus Torvalds 
5579f06c719SChuck Lever static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req,
5589f06c719SChuck Lever 				 struct xdr_stream *xdr,
559fcc85819SChristoph Hellwig 				 const void *data)
56025a0866cSChuck Lever {
561fcc85819SChristoph Hellwig 	const struct nfs_fh *fh = data;
562fcc85819SChristoph Hellwig 
5639f06c719SChuck Lever 	encode_fhandle(xdr, fh);
56425a0866cSChuck Lever }
56525a0866cSChuck Lever 
5661da177e4SLinus Torvalds /*
56725a0866cSChuck Lever  * 2.2.3.  sattrargs
56825a0866cSChuck Lever  *
56925a0866cSChuck Lever  *	struct sattrargs {
57025a0866cSChuck Lever  *		fhandle file;
57125a0866cSChuck Lever  *		sattr attributes;
57225a0866cSChuck Lever  *	};
57325a0866cSChuck Lever  */
5749f06c719SChuck Lever static void nfs2_xdr_enc_sattrargs(struct rpc_rqst *req,
5759f06c719SChuck Lever 				   struct xdr_stream *xdr,
576fcc85819SChristoph Hellwig 				   const void *data)
57725a0866cSChuck Lever {
578fcc85819SChristoph Hellwig 	const struct nfs_sattrargs *args = data;
579fcc85819SChristoph Hellwig 
5809f06c719SChuck Lever 	encode_fhandle(xdr, args->fh);
581c207db2fSTrond Myklebust 	encode_sattr(xdr, args->sattr, rpc_rqst_userns(req));
58225a0866cSChuck Lever }
58325a0866cSChuck Lever 
5849f06c719SChuck Lever static void nfs2_xdr_enc_diropargs(struct rpc_rqst *req,
5859f06c719SChuck Lever 				   struct xdr_stream *xdr,
586fcc85819SChristoph Hellwig 				   const void *data)
58725a0866cSChuck Lever {
588fcc85819SChristoph Hellwig 	const struct nfs_diropargs *args = data;
589fcc85819SChristoph Hellwig 
5909f06c719SChuck Lever 	encode_diropargs(xdr, args->fh, args->name, args->len);
59125a0866cSChuck Lever }
59225a0866cSChuck Lever 
5939f06c719SChuck Lever static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req,
5949f06c719SChuck Lever 				      struct xdr_stream *xdr,
595fcc85819SChristoph Hellwig 				      const void *data)
59625a0866cSChuck Lever {
597fcc85819SChristoph Hellwig 	const struct nfs_readlinkargs *args = data;
598fcc85819SChristoph Hellwig 
5999f06c719SChuck Lever 	encode_fhandle(xdr, args->fh);
600cf500bacSChuck Lever 	rpc_prepare_reply_pages(req, args->pages, args->pgbase,
60125a0866cSChuck Lever 				args->pglen, NFS_readlinkres_sz);
60225a0866cSChuck Lever }
60325a0866cSChuck Lever 
6044fdc17b2STrond Myklebust /*
60525a0866cSChuck Lever  * 2.2.7.  readargs
60625a0866cSChuck Lever  *
60725a0866cSChuck Lever  *	struct readargs {
60825a0866cSChuck Lever  *		fhandle file;
60925a0866cSChuck Lever  *		unsigned offset;
61025a0866cSChuck Lever  *		unsigned count;
61125a0866cSChuck Lever  *		unsigned totalcount;
61225a0866cSChuck Lever  *	};
61325a0866cSChuck Lever  */
61425a0866cSChuck Lever static void encode_readargs(struct xdr_stream *xdr,
6153c6b899cSAnna Schumaker 			    const struct nfs_pgio_args *args)
61625a0866cSChuck Lever {
61725a0866cSChuck Lever 	u32 offset = args->offset;
61825a0866cSChuck Lever 	u32 count = args->count;
61925a0866cSChuck Lever 	__be32 *p;
62025a0866cSChuck Lever 
62125a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
62225a0866cSChuck Lever 
62325a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4 + 4);
62425a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
62525a0866cSChuck Lever 	*p++ = cpu_to_be32(count);
62625a0866cSChuck Lever 	*p = cpu_to_be32(count);
62725a0866cSChuck Lever }
62825a0866cSChuck Lever 
6299f06c719SChuck Lever static void nfs2_xdr_enc_readargs(struct rpc_rqst *req,
6309f06c719SChuck Lever 				  struct xdr_stream *xdr,
631fcc85819SChristoph Hellwig 				  const void *data)
63225a0866cSChuck Lever {
633fcc85819SChristoph Hellwig 	const struct nfs_pgio_args *args = data;
634fcc85819SChristoph Hellwig 
6359f06c719SChuck Lever 	encode_readargs(xdr, args);
636cf500bacSChuck Lever 	rpc_prepare_reply_pages(req, args->pages, args->pgbase,
63725a0866cSChuck Lever 				args->count, NFS_readres_sz);
63825a0866cSChuck Lever 	req->rq_rcv_buf.flags |= XDRBUF_READ;
63925a0866cSChuck Lever }
64025a0866cSChuck Lever 
64125a0866cSChuck Lever /*
64225a0866cSChuck Lever  * 2.2.9.  writeargs
64325a0866cSChuck Lever  *
64425a0866cSChuck Lever  *	struct writeargs {
64525a0866cSChuck Lever  *		fhandle file;
64625a0866cSChuck Lever  *		unsigned beginoffset;
64725a0866cSChuck Lever  *		unsigned offset;
64825a0866cSChuck Lever  *		unsigned totalcount;
64925a0866cSChuck Lever  *		nfsdata data;
65025a0866cSChuck Lever  *	};
65125a0866cSChuck Lever  */
65225a0866cSChuck Lever static void encode_writeargs(struct xdr_stream *xdr,
6533c6b899cSAnna Schumaker 			     const struct nfs_pgio_args *args)
65425a0866cSChuck Lever {
65525a0866cSChuck Lever 	u32 offset = args->offset;
65625a0866cSChuck Lever 	u32 count = args->count;
65725a0866cSChuck Lever 	__be32 *p;
65825a0866cSChuck Lever 
65925a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
66025a0866cSChuck Lever 
66125a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4);
66225a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
66325a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
66425a0866cSChuck Lever 	*p++ = cpu_to_be32(count);
66525a0866cSChuck Lever 
66625a0866cSChuck Lever 	/* nfsdata */
66725a0866cSChuck Lever 	*p = cpu_to_be32(count);
66825a0866cSChuck Lever 	xdr_write_pages(xdr, args->pages, args->pgbase, count);
66925a0866cSChuck Lever }
67025a0866cSChuck Lever 
6719f06c719SChuck Lever static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req,
6729f06c719SChuck Lever 				   struct xdr_stream *xdr,
673fcc85819SChristoph Hellwig 				   const void *data)
67425a0866cSChuck Lever {
675fcc85819SChristoph Hellwig 	const struct nfs_pgio_args *args = data;
676fcc85819SChristoph Hellwig 
6779f06c719SChuck Lever 	encode_writeargs(xdr, args);
6789f06c719SChuck Lever 	xdr->buf->flags |= XDRBUF_WRITE;
67925a0866cSChuck Lever }
68025a0866cSChuck Lever 
68125a0866cSChuck Lever /*
68225a0866cSChuck Lever  * 2.2.10.  createargs
68325a0866cSChuck Lever  *
68425a0866cSChuck Lever  *	struct createargs {
68525a0866cSChuck Lever  *		diropargs where;
68625a0866cSChuck Lever  *		sattr attributes;
68725a0866cSChuck Lever  *	};
68825a0866cSChuck Lever  */
6899f06c719SChuck Lever static void nfs2_xdr_enc_createargs(struct rpc_rqst *req,
6909f06c719SChuck Lever 				    struct xdr_stream *xdr,
691fcc85819SChristoph Hellwig 				    const void *data)
69225a0866cSChuck Lever {
693fcc85819SChristoph Hellwig 	const struct nfs_createargs *args = data;
694fcc85819SChristoph Hellwig 
6959f06c719SChuck Lever 	encode_diropargs(xdr, args->fh, args->name, args->len);
696c207db2fSTrond Myklebust 	encode_sattr(xdr, args->sattr, rpc_rqst_userns(req));
69725a0866cSChuck Lever }
69825a0866cSChuck Lever 
6999f06c719SChuck Lever static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req,
7009f06c719SChuck Lever 				    struct xdr_stream *xdr,
701fcc85819SChristoph Hellwig 				    const void *data)
70225a0866cSChuck Lever {
703fcc85819SChristoph Hellwig 	const struct nfs_removeargs *args = data;
704fcc85819SChristoph Hellwig 
7059f06c719SChuck Lever 	encode_diropargs(xdr, args->fh, args->name.name, args->name.len);
70625a0866cSChuck Lever }
70725a0866cSChuck Lever 
70825a0866cSChuck Lever /*
70925a0866cSChuck Lever  * 2.2.12.  renameargs
71025a0866cSChuck Lever  *
71125a0866cSChuck Lever  *	struct renameargs {
71225a0866cSChuck Lever  *		diropargs from;
71325a0866cSChuck Lever  *		diropargs to;
71425a0866cSChuck Lever  *	};
71525a0866cSChuck Lever  */
7169f06c719SChuck Lever static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req,
7179f06c719SChuck Lever 				    struct xdr_stream *xdr,
718fcc85819SChristoph Hellwig 				    const void *data)
71925a0866cSChuck Lever {
720fcc85819SChristoph Hellwig 	const struct nfs_renameargs *args = data;
72125a0866cSChuck Lever 	const struct qstr *old = args->old_name;
72225a0866cSChuck Lever 	const struct qstr *new = args->new_name;
72325a0866cSChuck Lever 
7249f06c719SChuck Lever 	encode_diropargs(xdr, args->old_dir, old->name, old->len);
7259f06c719SChuck Lever 	encode_diropargs(xdr, args->new_dir, new->name, new->len);
72625a0866cSChuck Lever }
72725a0866cSChuck Lever 
72825a0866cSChuck Lever /*
72925a0866cSChuck Lever  * 2.2.13.  linkargs
73025a0866cSChuck Lever  *
73125a0866cSChuck Lever  *	struct linkargs {
73225a0866cSChuck Lever  *		fhandle from;
73325a0866cSChuck Lever  *		diropargs to;
73425a0866cSChuck Lever  *	};
73525a0866cSChuck Lever  */
7369f06c719SChuck Lever static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req,
7379f06c719SChuck Lever 				  struct xdr_stream *xdr,
738fcc85819SChristoph Hellwig 				  const void *data)
73925a0866cSChuck Lever {
740fcc85819SChristoph Hellwig 	const struct nfs_linkargs *args = data;
741fcc85819SChristoph Hellwig 
7429f06c719SChuck Lever 	encode_fhandle(xdr, args->fromfh);
7439f06c719SChuck Lever 	encode_diropargs(xdr, args->tofh, args->toname, args->tolen);
74425a0866cSChuck Lever }
74525a0866cSChuck Lever 
74625a0866cSChuck Lever /*
74725a0866cSChuck Lever  * 2.2.14.  symlinkargs
74825a0866cSChuck Lever  *
74925a0866cSChuck Lever  *	struct symlinkargs {
75025a0866cSChuck Lever  *		diropargs from;
75125a0866cSChuck Lever  *		path to;
75225a0866cSChuck Lever  *		sattr attributes;
75325a0866cSChuck Lever  *	};
75425a0866cSChuck Lever  */
7559f06c719SChuck Lever static void nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req,
7569f06c719SChuck Lever 				     struct xdr_stream *xdr,
757fcc85819SChristoph Hellwig 				     const void *data)
75825a0866cSChuck Lever {
759fcc85819SChristoph Hellwig 	const struct nfs_symlinkargs *args = data;
760fcc85819SChristoph Hellwig 
7619f06c719SChuck Lever 	encode_diropargs(xdr, args->fromfh, args->fromname, args->fromlen);
7629f06c719SChuck Lever 	encode_path(xdr, args->pages, args->pathlen);
763c207db2fSTrond Myklebust 	encode_sattr(xdr, args->sattr, rpc_rqst_userns(req));
76425a0866cSChuck Lever }
76525a0866cSChuck Lever 
76625a0866cSChuck Lever /*
76725a0866cSChuck Lever  * 2.2.17.  readdirargs
76825a0866cSChuck Lever  *
76925a0866cSChuck Lever  *	struct readdirargs {
77025a0866cSChuck Lever  *		fhandle dir;
77125a0866cSChuck Lever  *		nfscookie cookie;
77225a0866cSChuck Lever  *		unsigned count;
77325a0866cSChuck Lever  *	};
77425a0866cSChuck Lever  */
77525a0866cSChuck Lever static void encode_readdirargs(struct xdr_stream *xdr,
77625a0866cSChuck Lever 			       const struct nfs_readdirargs *args)
77725a0866cSChuck Lever {
77825a0866cSChuck Lever 	__be32 *p;
77925a0866cSChuck Lever 
78025a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
78125a0866cSChuck Lever 
78225a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4);
78325a0866cSChuck Lever 	*p++ = cpu_to_be32(args->cookie);
78425a0866cSChuck Lever 	*p = cpu_to_be32(args->count);
78525a0866cSChuck Lever }
78625a0866cSChuck Lever 
7879f06c719SChuck Lever static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req,
7889f06c719SChuck Lever 				     struct xdr_stream *xdr,
789fcc85819SChristoph Hellwig 				     const void *data)
79025a0866cSChuck Lever {
791fcc85819SChristoph Hellwig 	const struct nfs_readdirargs *args = data;
792fcc85819SChristoph Hellwig 
7939f06c719SChuck Lever 	encode_readdirargs(xdr, args);
794cf500bacSChuck Lever 	rpc_prepare_reply_pages(req, args->pages, 0,
79525a0866cSChuck Lever 				args->count, NFS_readdirres_sz);
79625a0866cSChuck Lever }
79725a0866cSChuck Lever 
79825a0866cSChuck Lever /*
799661ad423SChuck Lever  * NFSv2 XDR decode functions
800661ad423SChuck Lever  *
801661ad423SChuck Lever  * NFSv2 result types are defined in section 2.2 of RFC 1094:
802661ad423SChuck Lever  * "NFS: Network File System Protocol Specification".
8031da177e4SLinus Torvalds  */
8041da177e4SLinus Torvalds 
805bf269551SChuck Lever static int nfs2_xdr_dec_stat(struct rpc_rqst *req, struct xdr_stream *xdr,
806f796f8b3SChuck Lever 			     void *__unused)
807f796f8b3SChuck Lever {
808f796f8b3SChuck Lever 	enum nfs_stat status;
809f796f8b3SChuck Lever 	int error;
810f796f8b3SChuck Lever 
811bf269551SChuck Lever 	error = decode_stat(xdr, &status);
812f796f8b3SChuck Lever 	if (unlikely(error))
813f796f8b3SChuck Lever 		goto out;
814f796f8b3SChuck Lever 	if (status != NFS_OK)
815f796f8b3SChuck Lever 		goto out_default;
816f796f8b3SChuck Lever out:
817f796f8b3SChuck Lever 	return error;
818f796f8b3SChuck Lever out_default:
819f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
820f796f8b3SChuck Lever }
821f796f8b3SChuck Lever 
822bf269551SChuck Lever static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, struct xdr_stream *xdr,
823fc016483SChristoph Hellwig 				 void *result)
824f796f8b3SChuck Lever {
825c207db2fSTrond Myklebust 	return decode_attrstat(xdr, result, NULL, rpc_rqst_userns(req));
826f796f8b3SChuck Lever }
827f796f8b3SChuck Lever 
828bf269551SChuck Lever static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, struct xdr_stream *xdr,
829fc016483SChristoph Hellwig 				 void *result)
830f796f8b3SChuck Lever {
831c207db2fSTrond Myklebust 	return decode_diropres(xdr, result, rpc_rqst_userns(req));
832f796f8b3SChuck Lever }
833f796f8b3SChuck Lever 
8341da177e4SLinus Torvalds /*
835f796f8b3SChuck Lever  * 2.2.6.  readlinkres
836f796f8b3SChuck Lever  *
837f796f8b3SChuck Lever  *	union readlinkres switch (stat status) {
838f796f8b3SChuck Lever  *	case NFS_OK:
839f796f8b3SChuck Lever  *		path data;
840f796f8b3SChuck Lever  *	default:
841f796f8b3SChuck Lever  *		void;
842f796f8b3SChuck Lever  *	};
843f796f8b3SChuck Lever  */
844bf269551SChuck Lever static int nfs2_xdr_dec_readlinkres(struct rpc_rqst *req,
845bf269551SChuck Lever 				    struct xdr_stream *xdr, void *__unused)
846f796f8b3SChuck Lever {
847f796f8b3SChuck Lever 	enum nfs_stat status;
848f796f8b3SChuck Lever 	int error;
849f796f8b3SChuck Lever 
850bf269551SChuck Lever 	error = decode_stat(xdr, &status);
851f796f8b3SChuck Lever 	if (unlikely(error))
852f796f8b3SChuck Lever 		goto out;
853f796f8b3SChuck Lever 	if (status != NFS_OK)
854f796f8b3SChuck Lever 		goto out_default;
855bf269551SChuck Lever 	error = decode_path(xdr);
856f796f8b3SChuck Lever out:
857f796f8b3SChuck Lever 	return error;
858f796f8b3SChuck Lever out_default:
859f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
860f796f8b3SChuck Lever }
861f796f8b3SChuck Lever 
862f796f8b3SChuck Lever /*
863f796f8b3SChuck Lever  * 2.2.7.  readres
864f796f8b3SChuck Lever  *
865f796f8b3SChuck Lever  *	union readres switch (stat status) {
866f796f8b3SChuck Lever  *	case NFS_OK:
867f796f8b3SChuck Lever  *		fattr attributes;
868f796f8b3SChuck Lever  *		nfsdata data;
869f796f8b3SChuck Lever  *	default:
870f796f8b3SChuck Lever  *		void;
871f796f8b3SChuck Lever  *	};
872f796f8b3SChuck Lever  */
873bf269551SChuck Lever static int nfs2_xdr_dec_readres(struct rpc_rqst *req, struct xdr_stream *xdr,
874fc016483SChristoph Hellwig 				void *data)
875f796f8b3SChuck Lever {
876fc016483SChristoph Hellwig 	struct nfs_pgio_res *result = data;
877f796f8b3SChuck Lever 	enum nfs_stat status;
878f796f8b3SChuck Lever 	int error;
879f796f8b3SChuck Lever 
880bf269551SChuck Lever 	error = decode_stat(xdr, &status);
881f796f8b3SChuck Lever 	if (unlikely(error))
882f796f8b3SChuck Lever 		goto out;
883aabff4ddSPeng Tao 	result->op_status = status;
884f796f8b3SChuck Lever 	if (status != NFS_OK)
885f796f8b3SChuck Lever 		goto out_default;
886c207db2fSTrond Myklebust 	error = decode_fattr(xdr, result->fattr, rpc_rqst_userns(req));
887f796f8b3SChuck Lever 	if (unlikely(error))
888f796f8b3SChuck Lever 		goto out;
889bf269551SChuck Lever 	error = decode_nfsdata(xdr, result);
890f796f8b3SChuck Lever out:
891f796f8b3SChuck Lever 	return error;
892f796f8b3SChuck Lever out_default:
893f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
894f796f8b3SChuck Lever }
895f796f8b3SChuck Lever 
896bf269551SChuck Lever static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, struct xdr_stream *xdr,
897fc016483SChristoph Hellwig 				 void *data)
898f796f8b3SChuck Lever {
899fc016483SChristoph Hellwig 	struct nfs_pgio_res *result = data;
900fc016483SChristoph Hellwig 
901f796f8b3SChuck Lever 	/* All NFSv2 writes are "file sync" writes */
902f796f8b3SChuck Lever 	result->verf->committed = NFS_FILE_SYNC;
903c207db2fSTrond Myklebust 	return decode_attrstat(xdr, result->fattr, &result->op_status,
904c207db2fSTrond Myklebust 			rpc_rqst_userns(req));
905f796f8b3SChuck Lever }
906f796f8b3SChuck Lever 
907f796f8b3SChuck Lever /**
908f796f8b3SChuck Lever  * nfs2_decode_dirent - Decode a single NFSv2 directory entry stored in
909f796f8b3SChuck Lever  *                      the local page cache.
910f796f8b3SChuck Lever  * @xdr: XDR stream where entry resides
911f796f8b3SChuck Lever  * @entry: buffer to fill in with entry data
912f796f8b3SChuck Lever  * @plus: boolean indicating whether this should be a readdirplus entry
913f796f8b3SChuck Lever  *
914573c4e1eSChuck Lever  * Returns zero if successful, otherwise a negative errno value is
915573c4e1eSChuck Lever  * returned.
916f796f8b3SChuck Lever  *
917f796f8b3SChuck Lever  * This function is not invoked during READDIR reply decoding, but
918f796f8b3SChuck Lever  * rather whenever an application invokes the getdents(2) system call
919f796f8b3SChuck Lever  * on a directory already in our cache.
920f796f8b3SChuck Lever  *
921f796f8b3SChuck Lever  * 2.2.17.  entry
922f796f8b3SChuck Lever  *
923f796f8b3SChuck Lever  *	struct entry {
924f796f8b3SChuck Lever  *		unsigned	fileid;
925f796f8b3SChuck Lever  *		filename	name;
926f796f8b3SChuck Lever  *		nfscookie	cookie;
927f796f8b3SChuck Lever  *		entry		*nextentry;
928f796f8b3SChuck Lever  *	};
929f796f8b3SChuck Lever  */
930573c4e1eSChuck Lever int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
931a7a3b1e9SBenjamin Coddington 		       bool plus)
932f796f8b3SChuck Lever {
933f796f8b3SChuck Lever 	__be32 *p;
934f796f8b3SChuck Lever 	int error;
935f796f8b3SChuck Lever 
936f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
937eb72f484SChuck Lever 	if (unlikely(!p))
938eb72f484SChuck Lever 		return -EAGAIN;
939f796f8b3SChuck Lever 	if (*p++ == xdr_zero) {
940f796f8b3SChuck Lever 		p = xdr_inline_decode(xdr, 4);
941eb72f484SChuck Lever 		if (unlikely(!p))
942eb72f484SChuck Lever 			return -EAGAIN;
943f796f8b3SChuck Lever 		if (*p++ == xdr_zero)
944573c4e1eSChuck Lever 			return -EAGAIN;
945f796f8b3SChuck Lever 		entry->eof = 1;
946573c4e1eSChuck Lever 		return -EBADCOOKIE;
947f796f8b3SChuck Lever 	}
948f796f8b3SChuck Lever 
949f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
950eb72f484SChuck Lever 	if (unlikely(!p))
951eb72f484SChuck Lever 		return -EAGAIN;
952f796f8b3SChuck Lever 	entry->ino = be32_to_cpup(p);
953f796f8b3SChuck Lever 
954f796f8b3SChuck Lever 	error = decode_filename_inline(xdr, &entry->name, &entry->len);
955f796f8b3SChuck Lever 	if (unlikely(error))
956573c4e1eSChuck Lever 		return error;
957f796f8b3SChuck Lever 
958f796f8b3SChuck Lever 	/*
959f796f8b3SChuck Lever 	 * The type (size and byte order) of nfscookie isn't defined in
960f796f8b3SChuck Lever 	 * RFC 1094.  This implementation assumes that it's an XDR uint32.
961f796f8b3SChuck Lever 	 */
962f796f8b3SChuck Lever 	entry->prev_cookie = entry->cookie;
963f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
964eb72f484SChuck Lever 	if (unlikely(!p))
965eb72f484SChuck Lever 		return -EAGAIN;
966f796f8b3SChuck Lever 	entry->cookie = be32_to_cpup(p);
967f796f8b3SChuck Lever 
968f796f8b3SChuck Lever 	entry->d_type = DT_UNKNOWN;
969f796f8b3SChuck Lever 
970573c4e1eSChuck Lever 	return 0;
971f796f8b3SChuck Lever }
972f796f8b3SChuck Lever 
973f796f8b3SChuck Lever /*
974f796f8b3SChuck Lever  * 2.2.17.  readdirres
975f796f8b3SChuck Lever  *
976f796f8b3SChuck Lever  *	union readdirres switch (stat status) {
977f796f8b3SChuck Lever  *	case NFS_OK:
978f796f8b3SChuck Lever  *		struct {
979f796f8b3SChuck Lever  *			entry *entries;
980f796f8b3SChuck Lever  *			bool eof;
981f796f8b3SChuck Lever  *		} readdirok;
982f796f8b3SChuck Lever  *	default:
983f796f8b3SChuck Lever  *		void;
984f796f8b3SChuck Lever  *	};
985f796f8b3SChuck Lever  *
986f796f8b3SChuck Lever  * Read the directory contents into the page cache, but don't
987f796f8b3SChuck Lever  * touch them.  The actual decoding is done by nfs2_decode_dirent()
988f796f8b3SChuck Lever  * during subsequent nfs_readdir() calls.
989f796f8b3SChuck Lever  */
990f796f8b3SChuck Lever static int decode_readdirok(struct xdr_stream *xdr)
991f796f8b3SChuck Lever {
99264bd577eSTrond Myklebust 	return xdr_read_pages(xdr, xdr->buf->page_len);
993f796f8b3SChuck Lever }
994f796f8b3SChuck Lever 
995bf269551SChuck Lever static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req,
996bf269551SChuck Lever 				   struct xdr_stream *xdr, void *__unused)
997f796f8b3SChuck Lever {
998f796f8b3SChuck Lever 	enum nfs_stat status;
999f796f8b3SChuck Lever 	int error;
1000f796f8b3SChuck Lever 
1001bf269551SChuck Lever 	error = decode_stat(xdr, &status);
1002f796f8b3SChuck Lever 	if (unlikely(error))
1003f796f8b3SChuck Lever 		goto out;
1004f796f8b3SChuck Lever 	if (status != NFS_OK)
1005f796f8b3SChuck Lever 		goto out_default;
1006bf269551SChuck Lever 	error = decode_readdirok(xdr);
1007f796f8b3SChuck Lever out:
1008f796f8b3SChuck Lever 	return error;
1009f796f8b3SChuck Lever out_default:
1010f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
1011f796f8b3SChuck Lever }
1012f796f8b3SChuck Lever 
10131da177e4SLinus Torvalds /*
1014f796f8b3SChuck Lever  * 2.2.18.  statfsres
1015f796f8b3SChuck Lever  *
1016f796f8b3SChuck Lever  *	union statfsres (stat status) {
1017f796f8b3SChuck Lever  *	case NFS_OK:
1018f796f8b3SChuck Lever  *		struct {
1019f796f8b3SChuck Lever  *			unsigned tsize;
1020f796f8b3SChuck Lever  *			unsigned bsize;
1021f796f8b3SChuck Lever  *			unsigned blocks;
1022f796f8b3SChuck Lever  *			unsigned bfree;
1023f796f8b3SChuck Lever  *			unsigned bavail;
1024f796f8b3SChuck Lever  *		} info;
1025f796f8b3SChuck Lever  *	default:
1026f796f8b3SChuck Lever  *		void;
1027f796f8b3SChuck Lever  *	};
1028f796f8b3SChuck Lever  */
1029f796f8b3SChuck Lever static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result)
1030f796f8b3SChuck Lever {
1031f796f8b3SChuck Lever 	__be32 *p;
1032f796f8b3SChuck Lever 
1033f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS_info_sz << 2);
1034eb72f484SChuck Lever 	if (unlikely(!p))
1035eb72f484SChuck Lever 		return -EIO;
1036f796f8b3SChuck Lever 	result->tsize  = be32_to_cpup(p++);
1037f796f8b3SChuck Lever 	result->bsize  = be32_to_cpup(p++);
1038f796f8b3SChuck Lever 	result->blocks = be32_to_cpup(p++);
1039f796f8b3SChuck Lever 	result->bfree  = be32_to_cpup(p++);
1040f796f8b3SChuck Lever 	result->bavail = be32_to_cpup(p);
1041f796f8b3SChuck Lever 	return 0;
1042f796f8b3SChuck Lever }
1043f796f8b3SChuck Lever 
1044bf269551SChuck Lever static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr,
1045fc016483SChristoph Hellwig 				  void *result)
1046f796f8b3SChuck Lever {
1047f796f8b3SChuck Lever 	enum nfs_stat status;
1048f796f8b3SChuck Lever 	int error;
1049f796f8b3SChuck Lever 
1050bf269551SChuck Lever 	error = decode_stat(xdr, &status);
1051f796f8b3SChuck Lever 	if (unlikely(error))
1052f796f8b3SChuck Lever 		goto out;
1053f796f8b3SChuck Lever 	if (status != NFS_OK)
1054f796f8b3SChuck Lever 		goto out_default;
1055bf269551SChuck Lever 	error = decode_info(xdr, result);
1056f796f8b3SChuck Lever out:
1057f796f8b3SChuck Lever 	return error;
1058f796f8b3SChuck Lever out_default:
1059f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
1060f796f8b3SChuck Lever }
1061f796f8b3SChuck Lever 
1062f796f8b3SChuck Lever 
1063f796f8b3SChuck Lever /*
10641da177e4SLinus Torvalds  * We need to translate between nfs status return values and
10651da177e4SLinus Torvalds  * the local errno values which may not be the same.
10661da177e4SLinus Torvalds  */
106785828493SChuck Lever static const struct {
10681da177e4SLinus Torvalds 	int stat;
10691da177e4SLinus Torvalds 	int errno;
10701da177e4SLinus Torvalds } nfs_errtbl[] = {
10711da177e4SLinus Torvalds 	{ NFS_OK,		0		},
1072856dff3dSBenny Halevy 	{ NFSERR_PERM,		-EPERM		},
1073856dff3dSBenny Halevy 	{ NFSERR_NOENT,		-ENOENT		},
1074856dff3dSBenny Halevy 	{ NFSERR_IO,		-errno_NFSERR_IO},
1075856dff3dSBenny Halevy 	{ NFSERR_NXIO,		-ENXIO		},
1076856dff3dSBenny Halevy /*	{ NFSERR_EAGAIN,	-EAGAIN		}, */
1077856dff3dSBenny Halevy 	{ NFSERR_ACCES,		-EACCES		},
1078856dff3dSBenny Halevy 	{ NFSERR_EXIST,		-EEXIST		},
1079856dff3dSBenny Halevy 	{ NFSERR_XDEV,		-EXDEV		},
1080856dff3dSBenny Halevy 	{ NFSERR_NODEV,		-ENODEV		},
1081856dff3dSBenny Halevy 	{ NFSERR_NOTDIR,	-ENOTDIR	},
1082856dff3dSBenny Halevy 	{ NFSERR_ISDIR,		-EISDIR		},
1083856dff3dSBenny Halevy 	{ NFSERR_INVAL,		-EINVAL		},
1084856dff3dSBenny Halevy 	{ NFSERR_FBIG,		-EFBIG		},
1085856dff3dSBenny Halevy 	{ NFSERR_NOSPC,		-ENOSPC		},
1086856dff3dSBenny Halevy 	{ NFSERR_ROFS,		-EROFS		},
1087856dff3dSBenny Halevy 	{ NFSERR_MLINK,		-EMLINK		},
1088856dff3dSBenny Halevy 	{ NFSERR_NAMETOOLONG,	-ENAMETOOLONG	},
1089856dff3dSBenny Halevy 	{ NFSERR_NOTEMPTY,	-ENOTEMPTY	},
1090856dff3dSBenny Halevy 	{ NFSERR_DQUOT,		-EDQUOT		},
1091856dff3dSBenny Halevy 	{ NFSERR_STALE,		-ESTALE		},
1092856dff3dSBenny Halevy 	{ NFSERR_REMOTE,	-EREMOTE	},
10931da177e4SLinus Torvalds #ifdef EWFLUSH
1094856dff3dSBenny Halevy 	{ NFSERR_WFLUSH,	-EWFLUSH	},
10951da177e4SLinus Torvalds #endif
1096856dff3dSBenny Halevy 	{ NFSERR_BADHANDLE,	-EBADHANDLE	},
1097856dff3dSBenny Halevy 	{ NFSERR_NOT_SYNC,	-ENOTSYNC	},
1098856dff3dSBenny Halevy 	{ NFSERR_BAD_COOKIE,	-EBADCOOKIE	},
1099856dff3dSBenny Halevy 	{ NFSERR_NOTSUPP,	-ENOTSUPP	},
1100856dff3dSBenny Halevy 	{ NFSERR_TOOSMALL,	-ETOOSMALL	},
1101fdcb4577STrond Myklebust 	{ NFSERR_SERVERFAULT,	-EREMOTEIO	},
1102856dff3dSBenny Halevy 	{ NFSERR_BADTYPE,	-EBADTYPE	},
1103856dff3dSBenny Halevy 	{ NFSERR_JUKEBOX,	-EJUKEBOX	},
1104856dff3dSBenny Halevy 	{ -1,			-EIO		}
11051da177e4SLinus Torvalds };
11061da177e4SLinus Torvalds 
110785828493SChuck Lever /**
110885828493SChuck Lever  * nfs_stat_to_errno - convert an NFS status code to a local errno
110985828493SChuck Lever  * @status: NFS status code to convert
111085828493SChuck Lever  *
111185828493SChuck Lever  * Returns a local errno value, or -EIO if the NFS status code is
111285828493SChuck Lever  * not recognized.  This function is used jointly by NFSv2 and NFSv3.
11131da177e4SLinus Torvalds  */
11145e7e5a0dSBryan Schumaker static int nfs_stat_to_errno(enum nfs_stat status)
11151da177e4SLinus Torvalds {
11161da177e4SLinus Torvalds 	int i;
11171da177e4SLinus Torvalds 
11181da177e4SLinus Torvalds 	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
111985828493SChuck Lever 		if (nfs_errtbl[i].stat == (int)status)
11201da177e4SLinus Torvalds 			return nfs_errtbl[i].errno;
11211da177e4SLinus Torvalds 	}
112285828493SChuck Lever 	dprintk("NFS: Unrecognized nfs status value: %u\n", status);
11231da177e4SLinus Torvalds 	return nfs_errtbl[i].errno;
11241da177e4SLinus Torvalds }
11251da177e4SLinus Torvalds 
11261da177e4SLinus Torvalds #define PROC(proc, argtype, restype, timer)				\
11271da177e4SLinus Torvalds [NFSPROC_##proc] = {							\
11281da177e4SLinus Torvalds 	.p_proc	    =  NFSPROC_##proc,					\
1129fcc85819SChristoph Hellwig 	.p_encode   =  nfs2_xdr_enc_##argtype,				\
1130fc016483SChristoph Hellwig 	.p_decode   =  nfs2_xdr_dec_##restype,				\
11312bea90d4SChuck Lever 	.p_arglen   =  NFS_##argtype##_sz,				\
11322bea90d4SChuck Lever 	.p_replen   =  NFS_##restype##_sz,				\
1133cc0175c1SChuck Lever 	.p_timer    =  timer,						\
1134cc0175c1SChuck Lever 	.p_statidx  =  NFSPROC_##proc,					\
1135cc0175c1SChuck Lever 	.p_name     =  #proc,						\
11361da177e4SLinus Torvalds 	}
1137511e936bSChristoph Hellwig const struct rpc_procinfo nfs_procedures[] = {
11381da177e4SLinus Torvalds 	PROC(GETATTR,	fhandle,	attrstat,	1),
11391da177e4SLinus Torvalds 	PROC(SETATTR,	sattrargs,	attrstat,	0),
11401da177e4SLinus Torvalds 	PROC(LOOKUP,	diropargs,	diropres,	2),
11411da177e4SLinus Torvalds 	PROC(READLINK,	readlinkargs,	readlinkres,	3),
11421da177e4SLinus Torvalds 	PROC(READ,	readargs,	readres,	3),
11431da177e4SLinus Torvalds 	PROC(WRITE,	writeargs,	writeres,	4),
11441da177e4SLinus Torvalds 	PROC(CREATE,	createargs,	diropres,	0),
11454fdc17b2STrond Myklebust 	PROC(REMOVE,	removeargs,	stat,		0),
11461da177e4SLinus Torvalds 	PROC(RENAME,	renameargs,	stat,		0),
11471da177e4SLinus Torvalds 	PROC(LINK,	linkargs,	stat,		0),
11481da177e4SLinus Torvalds 	PROC(SYMLINK,	symlinkargs,	stat,		0),
11491da177e4SLinus Torvalds 	PROC(MKDIR,	createargs,	diropres,	0),
11501da177e4SLinus Torvalds 	PROC(RMDIR,	diropargs,	stat,		0),
11511da177e4SLinus Torvalds 	PROC(READDIR,	readdirargs,	readdirres,	3),
11521da177e4SLinus Torvalds 	PROC(STATFS,	fhandle,	statfsres,	0),
11531da177e4SLinus Torvalds };
11541da177e4SLinus Torvalds 
1155c551858aSChristoph Hellwig static unsigned int nfs_version2_counts[ARRAY_SIZE(nfs_procedures)];
1156a613fa16STrond Myklebust const struct rpc_version nfs_version2 = {
11571da177e4SLinus Torvalds 	.number			= 2,
1158e8c96f8cSTobias Klauser 	.nrprocs		= ARRAY_SIZE(nfs_procedures),
1159c551858aSChristoph Hellwig 	.procs			= nfs_procedures,
1160c551858aSChristoph Hellwig 	.counts			= nfs_version2_counts,
11611da177e4SLinus Torvalds };
1162