xref: /openbmc/linux/fs/nfs/nfs2xdr.c (revision 5e7e5a0d)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/fs/nfs/nfs2xdr.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * XDR functions to encode/decode NFS RPC arguments and results.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Copyright (C) 1992, 1993, 1994  Rick Sladkey
71da177e4SLinus Torvalds  * Copyright (C) 1996 Olaf Kirch
81da177e4SLinus Torvalds  * 04 Aug 1998  Ion Badulescu <ionut@cs.columbia.edu>
91da177e4SLinus Torvalds  * 		FIFO's need special handling in NFSv2
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/param.h>
131da177e4SLinus Torvalds #include <linux/time.h>
141da177e4SLinus Torvalds #include <linux/mm.h>
151da177e4SLinus Torvalds #include <linux/errno.h>
161da177e4SLinus Torvalds #include <linux/string.h>
171da177e4SLinus Torvalds #include <linux/in.h>
181da177e4SLinus Torvalds #include <linux/pagemap.h>
191da177e4SLinus Torvalds #include <linux/proc_fs.h>
201da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
211da177e4SLinus Torvalds #include <linux/nfs.h>
221da177e4SLinus Torvalds #include <linux/nfs2.h>
231da177e4SLinus Torvalds #include <linux/nfs_fs.h>
24816724e6STrond Myklebust #include "internal.h"
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds #define NFSDBG_FACILITY		NFSDBG_XDR
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds /* Mapping from NFS error code to "errno" error code. */
291da177e4SLinus Torvalds #define errno_NFSERR_IO		EIO
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds /*
321da177e4SLinus Torvalds  * Declare the space requirements for NFS arguments and replies as
331da177e4SLinus Torvalds  * number of 32bit-words
341da177e4SLinus Torvalds  */
351da177e4SLinus Torvalds #define NFS_fhandle_sz		(8)
361da177e4SLinus Torvalds #define NFS_sattr_sz		(8)
371da177e4SLinus Torvalds #define NFS_filename_sz		(1+(NFS2_MAXNAMLEN>>2))
381da177e4SLinus Torvalds #define NFS_path_sz		(1+(NFS2_MAXPATHLEN>>2))
391da177e4SLinus Torvalds #define NFS_fattr_sz		(17)
401da177e4SLinus Torvalds #define NFS_info_sz		(5)
411da177e4SLinus Torvalds #define NFS_entry_sz		(NFS_filename_sz+3)
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds #define NFS_diropargs_sz	(NFS_fhandle_sz+NFS_filename_sz)
444fdc17b2STrond Myklebust #define NFS_removeargs_sz	(NFS_fhandle_sz+NFS_filename_sz)
451da177e4SLinus Torvalds #define NFS_sattrargs_sz	(NFS_fhandle_sz+NFS_sattr_sz)
461da177e4SLinus Torvalds #define NFS_readlinkargs_sz	(NFS_fhandle_sz)
471da177e4SLinus Torvalds #define NFS_readargs_sz		(NFS_fhandle_sz+3)
481da177e4SLinus Torvalds #define NFS_writeargs_sz	(NFS_fhandle_sz+4)
491da177e4SLinus Torvalds #define NFS_createargs_sz	(NFS_diropargs_sz+NFS_sattr_sz)
501da177e4SLinus Torvalds #define NFS_renameargs_sz	(NFS_diropargs_sz+NFS_diropargs_sz)
511da177e4SLinus Torvalds #define NFS_linkargs_sz		(NFS_fhandle_sz+NFS_diropargs_sz)
5294a6d753SChuck Lever #define NFS_symlinkargs_sz	(NFS_diropargs_sz+1+NFS_sattr_sz)
531da177e4SLinus Torvalds #define NFS_readdirargs_sz	(NFS_fhandle_sz+2)
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds #define NFS_attrstat_sz		(1+NFS_fattr_sz)
561da177e4SLinus Torvalds #define NFS_diropres_sz		(1+NFS_fhandle_sz+NFS_fattr_sz)
571da177e4SLinus Torvalds #define NFS_readlinkres_sz	(2)
581da177e4SLinus Torvalds #define NFS_readres_sz		(1+NFS_fattr_sz+1)
591da177e4SLinus Torvalds #define NFS_writeres_sz         (NFS_attrstat_sz)
601da177e4SLinus Torvalds #define NFS_stat_sz		(1)
611da177e4SLinus Torvalds #define NFS_readdirres_sz	(1)
621da177e4SLinus Torvalds #define NFS_statfsres_sz	(1+NFS_info_sz)
631da177e4SLinus Torvalds 
645e7e5a0dSBryan Schumaker static int nfs_stat_to_errno(enum nfs_stat);
6525a0866cSChuck Lever 
6625a0866cSChuck Lever /*
6725a0866cSChuck Lever  * While encoding arguments, set up the reply buffer in advance to
6825a0866cSChuck Lever  * receive reply data directly into the page cache.
6925a0866cSChuck Lever  */
7025a0866cSChuck Lever static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages,
7125a0866cSChuck Lever 				 unsigned int base, unsigned int len,
7225a0866cSChuck Lever 				 unsigned int bufsize)
7325a0866cSChuck Lever {
7425a0866cSChuck Lever 	struct rpc_auth	*auth = req->rq_cred->cr_auth;
7525a0866cSChuck Lever 	unsigned int replen;
7625a0866cSChuck Lever 
7725a0866cSChuck Lever 	replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize;
7825a0866cSChuck Lever 	xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len);
7925a0866cSChuck Lever }
8025a0866cSChuck Lever 
81f796f8b3SChuck Lever /*
82f796f8b3SChuck Lever  * Handle decode buffer overflows out-of-line.
83f796f8b3SChuck Lever  */
84f796f8b3SChuck Lever static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
85f796f8b3SChuck Lever {
86f796f8b3SChuck Lever 	dprintk("NFS: %s prematurely hit the end of our receive buffer. "
87f796f8b3SChuck Lever 		"Remaining buffer length is %tu words.\n",
88f796f8b3SChuck Lever 		func, xdr->end - xdr->p);
89f796f8b3SChuck Lever }
90f796f8b3SChuck Lever 
9125a0866cSChuck Lever 
921da177e4SLinus Torvalds /*
9325a0866cSChuck Lever  * Encode/decode NFSv2 basic data types
9425a0866cSChuck Lever  *
9525a0866cSChuck Lever  * Basic NFSv2 data types are defined in section 2.3 of RFC 1094:
9625a0866cSChuck Lever  * "NFS: Network File System Protocol Specification".
9725a0866cSChuck Lever  *
9825a0866cSChuck Lever  * Not all basic data types have their own encoding and decoding
9925a0866cSChuck Lever  * functions.  For run-time efficiency, some data types are encoded
10025a0866cSChuck Lever  * or decoded inline.
10125a0866cSChuck Lever  */
10225a0866cSChuck Lever 
10325a0866cSChuck Lever /*
104f796f8b3SChuck Lever  *	typedef opaque	nfsdata<>;
105f796f8b3SChuck Lever  */
106f796f8b3SChuck Lever static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result)
107f796f8b3SChuck Lever {
108f796f8b3SChuck Lever 	u32 recvd, count;
109f796f8b3SChuck Lever 	size_t hdrlen;
110f796f8b3SChuck Lever 	__be32 *p;
111f796f8b3SChuck Lever 
112f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
113f796f8b3SChuck Lever 	if (unlikely(p == NULL))
114f796f8b3SChuck Lever 		goto out_overflow;
115f796f8b3SChuck Lever 	count = be32_to_cpup(p);
116f796f8b3SChuck Lever 	hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
117f796f8b3SChuck Lever 	recvd = xdr->buf->len - hdrlen;
118f796f8b3SChuck Lever 	if (unlikely(count > recvd))
119f796f8b3SChuck Lever 		goto out_cheating;
120f796f8b3SChuck Lever out:
121f796f8b3SChuck Lever 	xdr_read_pages(xdr, count);
122f796f8b3SChuck Lever 	result->eof = 0;	/* NFSv2 does not pass EOF flag on the wire. */
123f796f8b3SChuck Lever 	result->count = count;
124f796f8b3SChuck Lever 	return count;
125f796f8b3SChuck Lever out_cheating:
126f796f8b3SChuck Lever 	dprintk("NFS: server cheating in read result: "
127f796f8b3SChuck Lever 		"count %u > recvd %u\n", count, recvd);
128f796f8b3SChuck Lever 	count = recvd;
129f796f8b3SChuck Lever 	goto out;
130f796f8b3SChuck Lever out_overflow:
131f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
132f796f8b3SChuck Lever 	return -EIO;
133f796f8b3SChuck Lever }
134f796f8b3SChuck Lever 
135f796f8b3SChuck Lever /*
136f796f8b3SChuck Lever  *	enum stat {
137f796f8b3SChuck Lever  *		NFS_OK = 0,
138f796f8b3SChuck Lever  *		NFSERR_PERM = 1,
139f796f8b3SChuck Lever  *		NFSERR_NOENT = 2,
140f796f8b3SChuck Lever  *		NFSERR_IO = 5,
141f796f8b3SChuck Lever  *		NFSERR_NXIO = 6,
142f796f8b3SChuck Lever  *		NFSERR_ACCES = 13,
143f796f8b3SChuck Lever  *		NFSERR_EXIST = 17,
144f796f8b3SChuck Lever  *		NFSERR_NODEV = 19,
145f796f8b3SChuck Lever  *		NFSERR_NOTDIR = 20,
146f796f8b3SChuck Lever  *		NFSERR_ISDIR = 21,
147f796f8b3SChuck Lever  *		NFSERR_FBIG = 27,
148f796f8b3SChuck Lever  *		NFSERR_NOSPC = 28,
149f796f8b3SChuck Lever  *		NFSERR_ROFS = 30,
150f796f8b3SChuck Lever  *		NFSERR_NAMETOOLONG = 63,
151f796f8b3SChuck Lever  *		NFSERR_NOTEMPTY = 66,
152f796f8b3SChuck Lever  *		NFSERR_DQUOT = 69,
153f796f8b3SChuck Lever  *		NFSERR_STALE = 70,
154f796f8b3SChuck Lever  *		NFSERR_WFLUSH = 99
155f796f8b3SChuck Lever  *	};
156f796f8b3SChuck Lever  */
157f796f8b3SChuck Lever static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status)
158f796f8b3SChuck Lever {
159f796f8b3SChuck Lever 	__be32 *p;
160f796f8b3SChuck Lever 
161f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
162f796f8b3SChuck Lever 	if (unlikely(p == NULL))
163f796f8b3SChuck Lever 		goto out_overflow;
164f796f8b3SChuck Lever 	*status = be32_to_cpup(p);
165f796f8b3SChuck Lever 	return 0;
166f796f8b3SChuck Lever out_overflow:
167f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
168f796f8b3SChuck Lever 	return -EIO;
169f796f8b3SChuck Lever }
170f796f8b3SChuck Lever 
171f796f8b3SChuck Lever /*
1725f96e5e3SChuck Lever  * 2.3.2.  ftype
1735f96e5e3SChuck Lever  *
1745f96e5e3SChuck Lever  *	enum ftype {
1755f96e5e3SChuck Lever  *		NFNON = 0,
1765f96e5e3SChuck Lever  *		NFREG = 1,
1775f96e5e3SChuck Lever  *		NFDIR = 2,
1785f96e5e3SChuck Lever  *		NFBLK = 3,
1795f96e5e3SChuck Lever  *		NFCHR = 4,
1805f96e5e3SChuck Lever  *		NFLNK = 5
1815f96e5e3SChuck Lever  *	};
1825f96e5e3SChuck Lever  *
1835f96e5e3SChuck Lever  */
1845f96e5e3SChuck Lever static __be32 *xdr_decode_ftype(__be32 *p, u32 *type)
1855f96e5e3SChuck Lever {
1865f96e5e3SChuck Lever 	*type = be32_to_cpup(p++);
1875f96e5e3SChuck Lever 	if (unlikely(*type > NF2FIFO))
1885f96e5e3SChuck Lever 		*type = NFBAD;
1895f96e5e3SChuck Lever 	return p;
1905f96e5e3SChuck Lever }
1915f96e5e3SChuck Lever 
1925f96e5e3SChuck Lever /*
19325a0866cSChuck Lever  * 2.3.3.  fhandle
19425a0866cSChuck Lever  *
19525a0866cSChuck Lever  *	typedef opaque fhandle[FHSIZE];
19625a0866cSChuck Lever  */
19725a0866cSChuck Lever static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh)
19825a0866cSChuck Lever {
19925a0866cSChuck Lever 	__be32 *p;
20025a0866cSChuck Lever 
20125a0866cSChuck Lever 	BUG_ON(fh->size != NFS2_FHSIZE);
20225a0866cSChuck Lever 	p = xdr_reserve_space(xdr, NFS2_FHSIZE);
20325a0866cSChuck Lever 	memcpy(p, fh->data, NFS2_FHSIZE);
20425a0866cSChuck Lever }
20525a0866cSChuck Lever 
206f796f8b3SChuck Lever static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh)
207f796f8b3SChuck Lever {
208f796f8b3SChuck Lever 	__be32 *p;
209f796f8b3SChuck Lever 
210f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS2_FHSIZE);
211f796f8b3SChuck Lever 	if (unlikely(p == NULL))
212f796f8b3SChuck Lever 		goto out_overflow;
213f796f8b3SChuck Lever 	fh->size = NFS2_FHSIZE;
214f796f8b3SChuck Lever 	memcpy(fh->data, p, NFS2_FHSIZE);
215f796f8b3SChuck Lever 	return 0;
216f796f8b3SChuck Lever out_overflow:
217f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
218f796f8b3SChuck Lever 	return -EIO;
219f796f8b3SChuck Lever }
220f796f8b3SChuck Lever 
22125a0866cSChuck Lever /*
222282ac2a5SChuck Lever  * 2.3.4.  timeval
223282ac2a5SChuck Lever  *
224282ac2a5SChuck Lever  *	struct timeval {
225282ac2a5SChuck Lever  *		unsigned int seconds;
226282ac2a5SChuck Lever  *		unsigned int useconds;
227282ac2a5SChuck Lever  *	};
228282ac2a5SChuck Lever  */
229282ac2a5SChuck Lever static __be32 *xdr_encode_time(__be32 *p, const struct timespec *timep)
230282ac2a5SChuck Lever {
231282ac2a5SChuck Lever 	*p++ = cpu_to_be32(timep->tv_sec);
232282ac2a5SChuck Lever 	if (timep->tv_nsec != 0)
233282ac2a5SChuck Lever 		*p++ = cpu_to_be32(timep->tv_nsec / NSEC_PER_USEC);
234282ac2a5SChuck Lever 	else
235282ac2a5SChuck Lever 		*p++ = cpu_to_be32(0);
236282ac2a5SChuck Lever 	return p;
237282ac2a5SChuck Lever }
238282ac2a5SChuck Lever 
239282ac2a5SChuck Lever /*
240282ac2a5SChuck Lever  * Passing the invalid value useconds=1000000 is a Sun convention for
241282ac2a5SChuck Lever  * "set to current server time".  It's needed to make permissions checks
242282ac2a5SChuck Lever  * for the "touch" program across v2 mounts to Solaris and Irix servers
243282ac2a5SChuck Lever  * work correctly.  See description of sattr in section 6.1 of "NFS
244282ac2a5SChuck Lever  * Illustrated" by Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5.
245282ac2a5SChuck Lever  */
246282ac2a5SChuck Lever static __be32 *xdr_encode_current_server_time(__be32 *p,
247282ac2a5SChuck Lever 					      const struct timespec *timep)
248282ac2a5SChuck Lever {
249282ac2a5SChuck Lever 	*p++ = cpu_to_be32(timep->tv_sec);
250282ac2a5SChuck Lever 	*p++ = cpu_to_be32(1000000);
251282ac2a5SChuck Lever 	return p;
252282ac2a5SChuck Lever }
253282ac2a5SChuck Lever 
2545f96e5e3SChuck Lever static __be32 *xdr_decode_time(__be32 *p, struct timespec *timep)
2555f96e5e3SChuck Lever {
2565f96e5e3SChuck Lever 	timep->tv_sec = be32_to_cpup(p++);
2575f96e5e3SChuck Lever 	timep->tv_nsec = be32_to_cpup(p++) * NSEC_PER_USEC;
2585f96e5e3SChuck Lever 	return p;
2595f96e5e3SChuck Lever }
2605f96e5e3SChuck Lever 
261282ac2a5SChuck Lever /*
262f796f8b3SChuck Lever  * 2.3.5.  fattr
263f796f8b3SChuck Lever  *
264f796f8b3SChuck Lever  *	struct fattr {
265f796f8b3SChuck Lever  *		ftype		type;
266f796f8b3SChuck Lever  *		unsigned int	mode;
267f796f8b3SChuck Lever  *		unsigned int	nlink;
268f796f8b3SChuck Lever  *		unsigned int	uid;
269f796f8b3SChuck Lever  *		unsigned int	gid;
270f796f8b3SChuck Lever  *		unsigned int	size;
271f796f8b3SChuck Lever  *		unsigned int	blocksize;
272f796f8b3SChuck Lever  *		unsigned int	rdev;
273f796f8b3SChuck Lever  *		unsigned int	blocks;
274f796f8b3SChuck Lever  *		unsigned int	fsid;
275f796f8b3SChuck Lever  *		unsigned int	fileid;
276f796f8b3SChuck Lever  *		timeval		atime;
277f796f8b3SChuck Lever  *		timeval		mtime;
278f796f8b3SChuck Lever  *		timeval		ctime;
279f796f8b3SChuck Lever  *	};
280f796f8b3SChuck Lever  *
281f796f8b3SChuck Lever  */
282f796f8b3SChuck Lever static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
283f796f8b3SChuck Lever {
2845f96e5e3SChuck Lever 	u32 rdev, type;
285f796f8b3SChuck Lever 	__be32 *p;
286f796f8b3SChuck Lever 
287f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS_fattr_sz << 2);
288f796f8b3SChuck Lever 	if (unlikely(p == NULL))
289f796f8b3SChuck Lever 		goto out_overflow;
2905f96e5e3SChuck Lever 
2915f96e5e3SChuck Lever 	fattr->valid |= NFS_ATTR_FATTR_V2;
2925f96e5e3SChuck Lever 
2935f96e5e3SChuck Lever 	p = xdr_decode_ftype(p, &type);
2945f96e5e3SChuck Lever 
2955f96e5e3SChuck Lever 	fattr->mode = be32_to_cpup(p++);
2965f96e5e3SChuck Lever 	fattr->nlink = be32_to_cpup(p++);
2975f96e5e3SChuck Lever 	fattr->uid = be32_to_cpup(p++);
2985f96e5e3SChuck Lever 	fattr->gid = be32_to_cpup(p++);
2995f96e5e3SChuck Lever 	fattr->size = be32_to_cpup(p++);
3005f96e5e3SChuck Lever 	fattr->du.nfs2.blocksize = be32_to_cpup(p++);
3015f96e5e3SChuck Lever 
3025f96e5e3SChuck Lever 	rdev = be32_to_cpup(p++);
3035f96e5e3SChuck Lever 	fattr->rdev = new_decode_dev(rdev);
3045f96e5e3SChuck Lever 	if (type == (u32)NFCHR && rdev == (u32)NFS2_FIFO_DEV) {
3055f96e5e3SChuck Lever 		fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
3065f96e5e3SChuck Lever 		fattr->rdev = 0;
3075f96e5e3SChuck Lever 	}
3085f96e5e3SChuck Lever 
3095f96e5e3SChuck Lever 	fattr->du.nfs2.blocks = be32_to_cpup(p++);
3105f96e5e3SChuck Lever 	fattr->fsid.major = be32_to_cpup(p++);
3115f96e5e3SChuck Lever 	fattr->fsid.minor = 0;
3125f96e5e3SChuck Lever 	fattr->fileid = be32_to_cpup(p++);
3135f96e5e3SChuck Lever 
3145f96e5e3SChuck Lever 	p = xdr_decode_time(p, &fattr->atime);
3155f96e5e3SChuck Lever 	p = xdr_decode_time(p, &fattr->mtime);
3165f96e5e3SChuck Lever 	xdr_decode_time(p, &fattr->ctime);
3173a1556e8STrond Myklebust 	fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime);
3183a1556e8STrond Myklebust 
319f796f8b3SChuck Lever 	return 0;
320f796f8b3SChuck Lever out_overflow:
321f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
322f796f8b3SChuck Lever 	return -EIO;
323f796f8b3SChuck Lever }
324f796f8b3SChuck Lever 
325f796f8b3SChuck Lever /*
32625a0866cSChuck Lever  * 2.3.6.  sattr
32725a0866cSChuck Lever  *
32825a0866cSChuck Lever  *	struct sattr {
32925a0866cSChuck Lever  *		unsigned int	mode;
33025a0866cSChuck Lever  *		unsigned int	uid;
33125a0866cSChuck Lever  *		unsigned int	gid;
33225a0866cSChuck Lever  *		unsigned int	size;
33325a0866cSChuck Lever  *		timeval		atime;
33425a0866cSChuck Lever  *		timeval		mtime;
33525a0866cSChuck Lever  *	};
33625a0866cSChuck Lever  */
33725a0866cSChuck Lever 
33825a0866cSChuck Lever #define NFS2_SATTR_NOT_SET	(0xffffffff)
33925a0866cSChuck Lever 
34025a0866cSChuck Lever static __be32 *xdr_time_not_set(__be32 *p)
34125a0866cSChuck Lever {
34225a0866cSChuck Lever 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
34325a0866cSChuck Lever 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
34425a0866cSChuck Lever 	return p;
34525a0866cSChuck Lever }
34625a0866cSChuck Lever 
34725a0866cSChuck Lever static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr)
34825a0866cSChuck Lever {
34925a0866cSChuck Lever 	__be32 *p;
35025a0866cSChuck Lever 
35125a0866cSChuck Lever 	p = xdr_reserve_space(xdr, NFS_sattr_sz << 2);
35225a0866cSChuck Lever 
35325a0866cSChuck Lever 	if (attr->ia_valid & ATTR_MODE)
35425a0866cSChuck Lever 		*p++ = cpu_to_be32(attr->ia_mode);
35525a0866cSChuck Lever 	else
35625a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
35725a0866cSChuck Lever 	if (attr->ia_valid & ATTR_UID)
35825a0866cSChuck Lever 		*p++ = cpu_to_be32(attr->ia_uid);
35925a0866cSChuck Lever 	else
36025a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
36125a0866cSChuck Lever 	if (attr->ia_valid & ATTR_GID)
36225a0866cSChuck Lever 		*p++ = cpu_to_be32(attr->ia_gid);
36325a0866cSChuck Lever 	else
36425a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
36525a0866cSChuck Lever 	if (attr->ia_valid & ATTR_SIZE)
36625a0866cSChuck Lever 		*p++ = cpu_to_be32((u32)attr->ia_size);
36725a0866cSChuck Lever 	else
36825a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
36925a0866cSChuck Lever 
37025a0866cSChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET)
37125a0866cSChuck Lever 		p = xdr_encode_time(p, &attr->ia_atime);
37225a0866cSChuck Lever 	else if (attr->ia_valid & ATTR_ATIME)
37325a0866cSChuck Lever 		p = xdr_encode_current_server_time(p, &attr->ia_atime);
37425a0866cSChuck Lever 	else
37525a0866cSChuck Lever 		p = xdr_time_not_set(p);
37625a0866cSChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET)
37725a0866cSChuck Lever 		xdr_encode_time(p, &attr->ia_mtime);
37825a0866cSChuck Lever 	else if (attr->ia_valid & ATTR_MTIME)
37925a0866cSChuck Lever 		xdr_encode_current_server_time(p, &attr->ia_mtime);
38025a0866cSChuck Lever 	else
38125a0866cSChuck Lever 		xdr_time_not_set(p);
38225a0866cSChuck Lever }
38325a0866cSChuck Lever 
38425a0866cSChuck Lever /*
38525a0866cSChuck Lever  * 2.3.7.  filename
38625a0866cSChuck Lever  *
38725a0866cSChuck Lever  *	typedef string filename<MAXNAMLEN>;
38825a0866cSChuck Lever  */
38925a0866cSChuck Lever static void encode_filename(struct xdr_stream *xdr,
39025a0866cSChuck Lever 			    const char *name, u32 length)
39125a0866cSChuck Lever {
39225a0866cSChuck Lever 	__be32 *p;
39325a0866cSChuck Lever 
39425a0866cSChuck Lever 	BUG_ON(length > NFS2_MAXNAMLEN);
39525a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + length);
39625a0866cSChuck Lever 	xdr_encode_opaque(p, name, length);
39725a0866cSChuck Lever }
39825a0866cSChuck Lever 
399f796f8b3SChuck Lever static int decode_filename_inline(struct xdr_stream *xdr,
400f796f8b3SChuck Lever 				  const char **name, u32 *length)
401f796f8b3SChuck Lever {
402f796f8b3SChuck Lever 	__be32 *p;
403f796f8b3SChuck Lever 	u32 count;
404f796f8b3SChuck Lever 
405f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
406f796f8b3SChuck Lever 	if (unlikely(p == NULL))
407f796f8b3SChuck Lever 		goto out_overflow;
408f796f8b3SChuck Lever 	count = be32_to_cpup(p);
409f796f8b3SChuck Lever 	if (count > NFS3_MAXNAMLEN)
410f796f8b3SChuck Lever 		goto out_nametoolong;
411f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, count);
412f796f8b3SChuck Lever 	if (unlikely(p == NULL))
413f796f8b3SChuck Lever 		goto out_overflow;
414f796f8b3SChuck Lever 	*name = (const char *)p;
415f796f8b3SChuck Lever 	*length = count;
416f796f8b3SChuck Lever 	return 0;
417f796f8b3SChuck Lever out_nametoolong:
418f796f8b3SChuck Lever 	dprintk("NFS: returned filename too long: %u\n", count);
419f796f8b3SChuck Lever 	return -ENAMETOOLONG;
420f796f8b3SChuck Lever out_overflow:
421f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
422f796f8b3SChuck Lever 	return -EIO;
423f796f8b3SChuck Lever }
424f796f8b3SChuck Lever 
42525a0866cSChuck Lever /*
42625a0866cSChuck Lever  * 2.3.8.  path
42725a0866cSChuck Lever  *
42825a0866cSChuck Lever  *	typedef string path<MAXPATHLEN>;
42925a0866cSChuck Lever  */
43025a0866cSChuck Lever static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length)
43125a0866cSChuck Lever {
43225a0866cSChuck Lever 	__be32 *p;
43325a0866cSChuck Lever 
43425a0866cSChuck Lever 	BUG_ON(length > NFS2_MAXPATHLEN);
43525a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4);
43625a0866cSChuck Lever 	*p = cpu_to_be32(length);
43725a0866cSChuck Lever 	xdr_write_pages(xdr, pages, 0, length);
43825a0866cSChuck Lever }
43925a0866cSChuck Lever 
440f796f8b3SChuck Lever static int decode_path(struct xdr_stream *xdr)
441f796f8b3SChuck Lever {
442f796f8b3SChuck Lever 	u32 length, recvd;
443f796f8b3SChuck Lever 	size_t hdrlen;
444f796f8b3SChuck Lever 	__be32 *p;
445f796f8b3SChuck Lever 
446f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
447f796f8b3SChuck Lever 	if (unlikely(p == NULL))
448f796f8b3SChuck Lever 		goto out_overflow;
449f796f8b3SChuck Lever 	length = be32_to_cpup(p);
450f796f8b3SChuck Lever 	if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN))
451f796f8b3SChuck Lever 		goto out_size;
452f796f8b3SChuck Lever 	hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
453f796f8b3SChuck Lever 	recvd = xdr->buf->len - hdrlen;
454f796f8b3SChuck Lever 	if (unlikely(length > recvd))
455f796f8b3SChuck Lever 		goto out_cheating;
456f796f8b3SChuck Lever 
457f796f8b3SChuck Lever 	xdr_read_pages(xdr, length);
458f796f8b3SChuck Lever 	xdr_terminate_string(xdr->buf, length);
459f796f8b3SChuck Lever 	return 0;
460f796f8b3SChuck Lever out_size:
461f796f8b3SChuck Lever 	dprintk("NFS: returned pathname too long: %u\n", length);
462f796f8b3SChuck Lever 	return -ENAMETOOLONG;
463f796f8b3SChuck Lever out_cheating:
464f796f8b3SChuck Lever 	dprintk("NFS: server cheating in pathname result: "
465f796f8b3SChuck Lever 		"length %u > received %u\n", length, recvd);
466f796f8b3SChuck Lever 	return -EIO;
467f796f8b3SChuck Lever out_overflow:
468f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
469f796f8b3SChuck Lever 	return -EIO;
470f796f8b3SChuck Lever }
471f796f8b3SChuck Lever 
472f796f8b3SChuck Lever /*
473f796f8b3SChuck Lever  * 2.3.9.  attrstat
474f796f8b3SChuck Lever  *
475f796f8b3SChuck Lever  *	union attrstat switch (stat status) {
476f796f8b3SChuck Lever  *	case NFS_OK:
477f796f8b3SChuck Lever  *		fattr attributes;
478f796f8b3SChuck Lever  *	default:
479f796f8b3SChuck Lever  *		void;
480f796f8b3SChuck Lever  *	};
481f796f8b3SChuck Lever  */
482f796f8b3SChuck Lever static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result)
483f796f8b3SChuck Lever {
484f796f8b3SChuck Lever 	enum nfs_stat status;
485f796f8b3SChuck Lever 	int error;
486f796f8b3SChuck Lever 
487f796f8b3SChuck Lever 	error = decode_stat(xdr, &status);
488f796f8b3SChuck Lever 	if (unlikely(error))
489f796f8b3SChuck Lever 		goto out;
490f796f8b3SChuck Lever 	if (status != NFS_OK)
491f796f8b3SChuck Lever 		goto out_default;
492f796f8b3SChuck Lever 	error = decode_fattr(xdr, result);
493f796f8b3SChuck Lever out:
494f796f8b3SChuck Lever 	return error;
495f796f8b3SChuck Lever out_default:
496f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
497f796f8b3SChuck Lever }
498f796f8b3SChuck Lever 
49925a0866cSChuck Lever /*
50025a0866cSChuck Lever  * 2.3.10.  diropargs
50125a0866cSChuck Lever  *
50225a0866cSChuck Lever  *	struct diropargs {
50325a0866cSChuck Lever  *		fhandle  dir;
50425a0866cSChuck Lever  *		filename name;
50525a0866cSChuck Lever  *	};
50625a0866cSChuck Lever  */
50725a0866cSChuck Lever static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh,
50825a0866cSChuck Lever 			     const char *name, u32 length)
50925a0866cSChuck Lever {
51025a0866cSChuck Lever 	encode_fhandle(xdr, fh);
51125a0866cSChuck Lever 	encode_filename(xdr, name, length);
51225a0866cSChuck Lever }
51325a0866cSChuck Lever 
514f796f8b3SChuck Lever /*
515f796f8b3SChuck Lever  * 2.3.11.  diropres
516f796f8b3SChuck Lever  *
517f796f8b3SChuck Lever  *	union diropres switch (stat status) {
518f796f8b3SChuck Lever  *	case NFS_OK:
519f796f8b3SChuck Lever  *		struct {
520f796f8b3SChuck Lever  *			fhandle file;
521f796f8b3SChuck Lever  *			fattr   attributes;
522f796f8b3SChuck Lever  *		} diropok;
523f796f8b3SChuck Lever  *	default:
524f796f8b3SChuck Lever  *		void;
525f796f8b3SChuck Lever  *	};
526f796f8b3SChuck Lever  */
527f796f8b3SChuck Lever static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result)
528f796f8b3SChuck Lever {
529f796f8b3SChuck Lever 	int error;
530f796f8b3SChuck Lever 
531f796f8b3SChuck Lever 	error = decode_fhandle(xdr, result->fh);
532f796f8b3SChuck Lever 	if (unlikely(error))
533f796f8b3SChuck Lever 		goto out;
534f796f8b3SChuck Lever 	error = decode_fattr(xdr, result->fattr);
535f796f8b3SChuck Lever out:
536f796f8b3SChuck Lever 	return error;
537f796f8b3SChuck Lever }
538f796f8b3SChuck Lever 
539f796f8b3SChuck Lever static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result)
540f796f8b3SChuck Lever {
541f796f8b3SChuck Lever 	enum nfs_stat status;
542f796f8b3SChuck Lever 	int error;
543f796f8b3SChuck Lever 
544f796f8b3SChuck Lever 	error = decode_stat(xdr, &status);
545f796f8b3SChuck Lever 	if (unlikely(error))
546f796f8b3SChuck Lever 		goto out;
547f796f8b3SChuck Lever 	if (status != NFS_OK)
548f796f8b3SChuck Lever 		goto out_default;
549f796f8b3SChuck Lever 	error = decode_diropok(xdr, result);
550f796f8b3SChuck Lever out:
551f796f8b3SChuck Lever 	return error;
552f796f8b3SChuck Lever out_default:
553f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
554f796f8b3SChuck Lever }
555f796f8b3SChuck Lever 
55625a0866cSChuck Lever 
55725a0866cSChuck Lever /*
5582d70f533SChuck Lever  * NFSv2 XDR encode functions
5592d70f533SChuck Lever  *
5602d70f533SChuck Lever  * NFSv2 argument types are defined in section 2.2 of RFC 1094:
5612d70f533SChuck Lever  * "NFS: Network File System Protocol Specification".
5621da177e4SLinus Torvalds  */
5631da177e4SLinus Torvalds 
5649f06c719SChuck Lever static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req,
5659f06c719SChuck Lever 				 struct xdr_stream *xdr,
56625a0866cSChuck Lever 				 const struct nfs_fh *fh)
56725a0866cSChuck Lever {
5689f06c719SChuck Lever 	encode_fhandle(xdr, fh);
56925a0866cSChuck Lever }
57025a0866cSChuck Lever 
5711da177e4SLinus Torvalds /*
57225a0866cSChuck Lever  * 2.2.3.  sattrargs
57325a0866cSChuck Lever  *
57425a0866cSChuck Lever  *	struct sattrargs {
57525a0866cSChuck Lever  *		fhandle file;
57625a0866cSChuck Lever  *		sattr attributes;
57725a0866cSChuck Lever  *	};
57825a0866cSChuck Lever  */
5799f06c719SChuck Lever static void nfs2_xdr_enc_sattrargs(struct rpc_rqst *req,
5809f06c719SChuck Lever 				   struct xdr_stream *xdr,
58125a0866cSChuck Lever 				   const struct nfs_sattrargs *args)
58225a0866cSChuck Lever {
5839f06c719SChuck Lever 	encode_fhandle(xdr, args->fh);
5849f06c719SChuck Lever 	encode_sattr(xdr, args->sattr);
58525a0866cSChuck Lever }
58625a0866cSChuck Lever 
5879f06c719SChuck Lever static void nfs2_xdr_enc_diropargs(struct rpc_rqst *req,
5889f06c719SChuck Lever 				   struct xdr_stream *xdr,
58925a0866cSChuck Lever 				   const struct nfs_diropargs *args)
59025a0866cSChuck Lever {
5919f06c719SChuck Lever 	encode_diropargs(xdr, args->fh, args->name, args->len);
59225a0866cSChuck Lever }
59325a0866cSChuck Lever 
5949f06c719SChuck Lever static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req,
5959f06c719SChuck Lever 				      struct xdr_stream *xdr,
59625a0866cSChuck Lever 				      const struct nfs_readlinkargs *args)
59725a0866cSChuck Lever {
5989f06c719SChuck Lever 	encode_fhandle(xdr, args->fh);
59925a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
60025a0866cSChuck Lever 					args->pglen, NFS_readlinkres_sz);
60125a0866cSChuck Lever }
60225a0866cSChuck Lever 
6034fdc17b2STrond Myklebust /*
60425a0866cSChuck Lever  * 2.2.7.  readargs
60525a0866cSChuck Lever  *
60625a0866cSChuck Lever  *	struct readargs {
60725a0866cSChuck Lever  *		fhandle file;
60825a0866cSChuck Lever  *		unsigned offset;
60925a0866cSChuck Lever  *		unsigned count;
61025a0866cSChuck Lever  *		unsigned totalcount;
61125a0866cSChuck Lever  *	};
61225a0866cSChuck Lever  */
61325a0866cSChuck Lever static void encode_readargs(struct xdr_stream *xdr,
61425a0866cSChuck Lever 			    const struct nfs_readargs *args)
61525a0866cSChuck Lever {
61625a0866cSChuck Lever 	u32 offset = args->offset;
61725a0866cSChuck Lever 	u32 count = args->count;
61825a0866cSChuck Lever 	__be32 *p;
61925a0866cSChuck Lever 
62025a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
62125a0866cSChuck Lever 
62225a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4 + 4);
62325a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
62425a0866cSChuck Lever 	*p++ = cpu_to_be32(count);
62525a0866cSChuck Lever 	*p = cpu_to_be32(count);
62625a0866cSChuck Lever }
62725a0866cSChuck Lever 
6289f06c719SChuck Lever static void nfs2_xdr_enc_readargs(struct rpc_rqst *req,
6299f06c719SChuck Lever 				  struct xdr_stream *xdr,
63025a0866cSChuck Lever 				  const struct nfs_readargs *args)
63125a0866cSChuck Lever {
6329f06c719SChuck Lever 	encode_readargs(xdr, args);
63325a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
63425a0866cSChuck Lever 					args->count, NFS_readres_sz);
63525a0866cSChuck Lever 	req->rq_rcv_buf.flags |= XDRBUF_READ;
63625a0866cSChuck Lever }
63725a0866cSChuck Lever 
63825a0866cSChuck Lever /*
63925a0866cSChuck Lever  * 2.2.9.  writeargs
64025a0866cSChuck Lever  *
64125a0866cSChuck Lever  *	struct writeargs {
64225a0866cSChuck Lever  *		fhandle file;
64325a0866cSChuck Lever  *		unsigned beginoffset;
64425a0866cSChuck Lever  *		unsigned offset;
64525a0866cSChuck Lever  *		unsigned totalcount;
64625a0866cSChuck Lever  *		nfsdata data;
64725a0866cSChuck Lever  *	};
64825a0866cSChuck Lever  */
64925a0866cSChuck Lever static void encode_writeargs(struct xdr_stream *xdr,
65025a0866cSChuck Lever 			     const struct nfs_writeargs *args)
65125a0866cSChuck Lever {
65225a0866cSChuck Lever 	u32 offset = args->offset;
65325a0866cSChuck Lever 	u32 count = args->count;
65425a0866cSChuck Lever 	__be32 *p;
65525a0866cSChuck Lever 
65625a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
65725a0866cSChuck Lever 
65825a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4);
65925a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
66025a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
66125a0866cSChuck Lever 	*p++ = cpu_to_be32(count);
66225a0866cSChuck Lever 
66325a0866cSChuck Lever 	/* nfsdata */
66425a0866cSChuck Lever 	*p = cpu_to_be32(count);
66525a0866cSChuck Lever 	xdr_write_pages(xdr, args->pages, args->pgbase, count);
66625a0866cSChuck Lever }
66725a0866cSChuck Lever 
6689f06c719SChuck Lever static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req,
6699f06c719SChuck Lever 				   struct xdr_stream *xdr,
67025a0866cSChuck Lever 				   const struct nfs_writeargs *args)
67125a0866cSChuck Lever {
6729f06c719SChuck Lever 	encode_writeargs(xdr, args);
6739f06c719SChuck Lever 	xdr->buf->flags |= XDRBUF_WRITE;
67425a0866cSChuck Lever }
67525a0866cSChuck Lever 
67625a0866cSChuck Lever /*
67725a0866cSChuck Lever  * 2.2.10.  createargs
67825a0866cSChuck Lever  *
67925a0866cSChuck Lever  *	struct createargs {
68025a0866cSChuck Lever  *		diropargs where;
68125a0866cSChuck Lever  *		sattr attributes;
68225a0866cSChuck Lever  *	};
68325a0866cSChuck Lever  */
6849f06c719SChuck Lever static void nfs2_xdr_enc_createargs(struct rpc_rqst *req,
6859f06c719SChuck Lever 				    struct xdr_stream *xdr,
68625a0866cSChuck Lever 				    const struct nfs_createargs *args)
68725a0866cSChuck Lever {
6889f06c719SChuck Lever 	encode_diropargs(xdr, args->fh, args->name, args->len);
6899f06c719SChuck Lever 	encode_sattr(xdr, args->sattr);
69025a0866cSChuck Lever }
69125a0866cSChuck Lever 
6929f06c719SChuck Lever static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req,
6939f06c719SChuck Lever 				    struct xdr_stream *xdr,
69425a0866cSChuck Lever 				    const struct nfs_removeargs *args)
69525a0866cSChuck Lever {
6969f06c719SChuck Lever 	encode_diropargs(xdr, args->fh, args->name.name, args->name.len);
69725a0866cSChuck Lever }
69825a0866cSChuck Lever 
69925a0866cSChuck Lever /*
70025a0866cSChuck Lever  * 2.2.12.  renameargs
70125a0866cSChuck Lever  *
70225a0866cSChuck Lever  *	struct renameargs {
70325a0866cSChuck Lever  *		diropargs from;
70425a0866cSChuck Lever  *		diropargs to;
70525a0866cSChuck Lever  *	};
70625a0866cSChuck Lever  */
7079f06c719SChuck Lever static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req,
7089f06c719SChuck Lever 				    struct xdr_stream *xdr,
70925a0866cSChuck Lever 				    const struct nfs_renameargs *args)
71025a0866cSChuck Lever {
71125a0866cSChuck Lever 	const struct qstr *old = args->old_name;
71225a0866cSChuck Lever 	const struct qstr *new = args->new_name;
71325a0866cSChuck Lever 
7149f06c719SChuck Lever 	encode_diropargs(xdr, args->old_dir, old->name, old->len);
7159f06c719SChuck Lever 	encode_diropargs(xdr, args->new_dir, new->name, new->len);
71625a0866cSChuck Lever }
71725a0866cSChuck Lever 
71825a0866cSChuck Lever /*
71925a0866cSChuck Lever  * 2.2.13.  linkargs
72025a0866cSChuck Lever  *
72125a0866cSChuck Lever  *	struct linkargs {
72225a0866cSChuck Lever  *		fhandle from;
72325a0866cSChuck Lever  *		diropargs to;
72425a0866cSChuck Lever  *	};
72525a0866cSChuck Lever  */
7269f06c719SChuck Lever static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req,
7279f06c719SChuck Lever 				  struct xdr_stream *xdr,
72825a0866cSChuck Lever 				  const struct nfs_linkargs *args)
72925a0866cSChuck Lever {
7309f06c719SChuck Lever 	encode_fhandle(xdr, args->fromfh);
7319f06c719SChuck Lever 	encode_diropargs(xdr, args->tofh, args->toname, args->tolen);
73225a0866cSChuck Lever }
73325a0866cSChuck Lever 
73425a0866cSChuck Lever /*
73525a0866cSChuck Lever  * 2.2.14.  symlinkargs
73625a0866cSChuck Lever  *
73725a0866cSChuck Lever  *	struct symlinkargs {
73825a0866cSChuck Lever  *		diropargs from;
73925a0866cSChuck Lever  *		path to;
74025a0866cSChuck Lever  *		sattr attributes;
74125a0866cSChuck Lever  *	};
74225a0866cSChuck Lever  */
7439f06c719SChuck Lever static void nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req,
7449f06c719SChuck Lever 				     struct xdr_stream *xdr,
74525a0866cSChuck Lever 				     const struct nfs_symlinkargs *args)
74625a0866cSChuck Lever {
7479f06c719SChuck Lever 	encode_diropargs(xdr, args->fromfh, args->fromname, args->fromlen);
7489f06c719SChuck Lever 	encode_path(xdr, args->pages, args->pathlen);
7499f06c719SChuck Lever 	encode_sattr(xdr, args->sattr);
75025a0866cSChuck Lever }
75125a0866cSChuck Lever 
75225a0866cSChuck Lever /*
75325a0866cSChuck Lever  * 2.2.17.  readdirargs
75425a0866cSChuck Lever  *
75525a0866cSChuck Lever  *	struct readdirargs {
75625a0866cSChuck Lever  *		fhandle dir;
75725a0866cSChuck Lever  *		nfscookie cookie;
75825a0866cSChuck Lever  *		unsigned count;
75925a0866cSChuck Lever  *	};
76025a0866cSChuck Lever  */
76125a0866cSChuck Lever static void encode_readdirargs(struct xdr_stream *xdr,
76225a0866cSChuck Lever 			       const struct nfs_readdirargs *args)
76325a0866cSChuck Lever {
76425a0866cSChuck Lever 	__be32 *p;
76525a0866cSChuck Lever 
76625a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
76725a0866cSChuck Lever 
76825a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4);
76925a0866cSChuck Lever 	*p++ = cpu_to_be32(args->cookie);
77025a0866cSChuck Lever 	*p = cpu_to_be32(args->count);
77125a0866cSChuck Lever }
77225a0866cSChuck Lever 
7739f06c719SChuck Lever static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req,
7749f06c719SChuck Lever 				     struct xdr_stream *xdr,
77525a0866cSChuck Lever 				     const struct nfs_readdirargs *args)
77625a0866cSChuck Lever {
7779f06c719SChuck Lever 	encode_readdirargs(xdr, args);
77825a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, 0,
77925a0866cSChuck Lever 					args->count, NFS_readdirres_sz);
78025a0866cSChuck Lever }
78125a0866cSChuck Lever 
78225a0866cSChuck Lever /*
783661ad423SChuck Lever  * NFSv2 XDR decode functions
784661ad423SChuck Lever  *
785661ad423SChuck Lever  * NFSv2 result types are defined in section 2.2 of RFC 1094:
786661ad423SChuck Lever  * "NFS: Network File System Protocol Specification".
7871da177e4SLinus Torvalds  */
7881da177e4SLinus Torvalds 
789bf269551SChuck Lever static int nfs2_xdr_dec_stat(struct rpc_rqst *req, struct xdr_stream *xdr,
790f796f8b3SChuck Lever 			     void *__unused)
791f796f8b3SChuck Lever {
792f796f8b3SChuck Lever 	enum nfs_stat status;
793f796f8b3SChuck Lever 	int error;
794f796f8b3SChuck Lever 
795bf269551SChuck Lever 	error = decode_stat(xdr, &status);
796f796f8b3SChuck Lever 	if (unlikely(error))
797f796f8b3SChuck Lever 		goto out;
798f796f8b3SChuck Lever 	if (status != NFS_OK)
799f796f8b3SChuck Lever 		goto out_default;
800f796f8b3SChuck Lever out:
801f796f8b3SChuck Lever 	return error;
802f796f8b3SChuck Lever out_default:
803f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
804f796f8b3SChuck Lever }
805f796f8b3SChuck Lever 
806bf269551SChuck Lever static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, struct xdr_stream *xdr,
807f796f8b3SChuck Lever 				 struct nfs_fattr *result)
808f796f8b3SChuck Lever {
809bf269551SChuck Lever 	return decode_attrstat(xdr, result);
810f796f8b3SChuck Lever }
811f796f8b3SChuck Lever 
812bf269551SChuck Lever static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, struct xdr_stream *xdr,
813f796f8b3SChuck Lever 				 struct nfs_diropok *result)
814f796f8b3SChuck Lever {
815bf269551SChuck Lever 	return decode_diropres(xdr, result);
816f796f8b3SChuck Lever }
817f796f8b3SChuck Lever 
8181da177e4SLinus Torvalds /*
819f796f8b3SChuck Lever  * 2.2.6.  readlinkres
820f796f8b3SChuck Lever  *
821f796f8b3SChuck Lever  *	union readlinkres switch (stat status) {
822f796f8b3SChuck Lever  *	case NFS_OK:
823f796f8b3SChuck Lever  *		path data;
824f796f8b3SChuck Lever  *	default:
825f796f8b3SChuck Lever  *		void;
826f796f8b3SChuck Lever  *	};
827f796f8b3SChuck Lever  */
828bf269551SChuck Lever static int nfs2_xdr_dec_readlinkres(struct rpc_rqst *req,
829bf269551SChuck Lever 				    struct xdr_stream *xdr, void *__unused)
830f796f8b3SChuck Lever {
831f796f8b3SChuck Lever 	enum nfs_stat status;
832f796f8b3SChuck Lever 	int error;
833f796f8b3SChuck Lever 
834bf269551SChuck Lever 	error = decode_stat(xdr, &status);
835f796f8b3SChuck Lever 	if (unlikely(error))
836f796f8b3SChuck Lever 		goto out;
837f796f8b3SChuck Lever 	if (status != NFS_OK)
838f796f8b3SChuck Lever 		goto out_default;
839bf269551SChuck Lever 	error = decode_path(xdr);
840f796f8b3SChuck Lever out:
841f796f8b3SChuck Lever 	return error;
842f796f8b3SChuck Lever out_default:
843f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
844f796f8b3SChuck Lever }
845f796f8b3SChuck Lever 
846f796f8b3SChuck Lever /*
847f796f8b3SChuck Lever  * 2.2.7.  readres
848f796f8b3SChuck Lever  *
849f796f8b3SChuck Lever  *	union readres switch (stat status) {
850f796f8b3SChuck Lever  *	case NFS_OK:
851f796f8b3SChuck Lever  *		fattr attributes;
852f796f8b3SChuck Lever  *		nfsdata data;
853f796f8b3SChuck Lever  *	default:
854f796f8b3SChuck Lever  *		void;
855f796f8b3SChuck Lever  *	};
856f796f8b3SChuck Lever  */
857bf269551SChuck Lever static int nfs2_xdr_dec_readres(struct rpc_rqst *req, struct xdr_stream *xdr,
858f796f8b3SChuck Lever 				struct nfs_readres *result)
859f796f8b3SChuck Lever {
860f796f8b3SChuck Lever 	enum nfs_stat status;
861f796f8b3SChuck Lever 	int error;
862f796f8b3SChuck Lever 
863bf269551SChuck Lever 	error = decode_stat(xdr, &status);
864f796f8b3SChuck Lever 	if (unlikely(error))
865f796f8b3SChuck Lever 		goto out;
866f796f8b3SChuck Lever 	if (status != NFS_OK)
867f796f8b3SChuck Lever 		goto out_default;
868bf269551SChuck Lever 	error = decode_fattr(xdr, result->fattr);
869f796f8b3SChuck Lever 	if (unlikely(error))
870f796f8b3SChuck Lever 		goto out;
871bf269551SChuck Lever 	error = decode_nfsdata(xdr, result);
872f796f8b3SChuck Lever out:
873f796f8b3SChuck Lever 	return error;
874f796f8b3SChuck Lever out_default:
875f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
876f796f8b3SChuck Lever }
877f796f8b3SChuck Lever 
878bf269551SChuck Lever static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, struct xdr_stream *xdr,
879f796f8b3SChuck Lever 				 struct nfs_writeres *result)
880f796f8b3SChuck Lever {
881f796f8b3SChuck Lever 	/* All NFSv2 writes are "file sync" writes */
882f796f8b3SChuck Lever 	result->verf->committed = NFS_FILE_SYNC;
883bf269551SChuck Lever 	return decode_attrstat(xdr, result->fattr);
884f796f8b3SChuck Lever }
885f796f8b3SChuck Lever 
886f796f8b3SChuck Lever /**
887f796f8b3SChuck Lever  * nfs2_decode_dirent - Decode a single NFSv2 directory entry stored in
888f796f8b3SChuck Lever  *                      the local page cache.
889f796f8b3SChuck Lever  * @xdr: XDR stream where entry resides
890f796f8b3SChuck Lever  * @entry: buffer to fill in with entry data
891f796f8b3SChuck Lever  * @plus: boolean indicating whether this should be a readdirplus entry
892f796f8b3SChuck Lever  *
893573c4e1eSChuck Lever  * Returns zero if successful, otherwise a negative errno value is
894573c4e1eSChuck Lever  * returned.
895f796f8b3SChuck Lever  *
896f796f8b3SChuck Lever  * This function is not invoked during READDIR reply decoding, but
897f796f8b3SChuck Lever  * rather whenever an application invokes the getdents(2) system call
898f796f8b3SChuck Lever  * on a directory already in our cache.
899f796f8b3SChuck Lever  *
900f796f8b3SChuck Lever  * 2.2.17.  entry
901f796f8b3SChuck Lever  *
902f796f8b3SChuck Lever  *	struct entry {
903f796f8b3SChuck Lever  *		unsigned	fileid;
904f796f8b3SChuck Lever  *		filename	name;
905f796f8b3SChuck Lever  *		nfscookie	cookie;
906f796f8b3SChuck Lever  *		entry		*nextentry;
907f796f8b3SChuck Lever  *	};
908f796f8b3SChuck Lever  */
909573c4e1eSChuck Lever int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
910573c4e1eSChuck Lever 		       int plus)
911f796f8b3SChuck Lever {
912f796f8b3SChuck Lever 	__be32 *p;
913f796f8b3SChuck Lever 	int error;
914f796f8b3SChuck Lever 
915f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
916f796f8b3SChuck Lever 	if (unlikely(p == NULL))
917f796f8b3SChuck Lever 		goto out_overflow;
918f796f8b3SChuck Lever 	if (*p++ == xdr_zero) {
919f796f8b3SChuck Lever 		p = xdr_inline_decode(xdr, 4);
920f796f8b3SChuck Lever 		if (unlikely(p == NULL))
921f796f8b3SChuck Lever 			goto out_overflow;
922f796f8b3SChuck Lever 		if (*p++ == xdr_zero)
923573c4e1eSChuck Lever 			return -EAGAIN;
924f796f8b3SChuck Lever 		entry->eof = 1;
925573c4e1eSChuck Lever 		return -EBADCOOKIE;
926f796f8b3SChuck Lever 	}
927f796f8b3SChuck Lever 
928f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
929f796f8b3SChuck Lever 	if (unlikely(p == NULL))
930f796f8b3SChuck Lever 		goto out_overflow;
931f796f8b3SChuck Lever 	entry->ino = be32_to_cpup(p);
932f796f8b3SChuck Lever 
933f796f8b3SChuck Lever 	error = decode_filename_inline(xdr, &entry->name, &entry->len);
934f796f8b3SChuck Lever 	if (unlikely(error))
935573c4e1eSChuck Lever 		return error;
936f796f8b3SChuck Lever 
937f796f8b3SChuck Lever 	/*
938f796f8b3SChuck Lever 	 * The type (size and byte order) of nfscookie isn't defined in
939f796f8b3SChuck Lever 	 * RFC 1094.  This implementation assumes that it's an XDR uint32.
940f796f8b3SChuck Lever 	 */
941f796f8b3SChuck Lever 	entry->prev_cookie = entry->cookie;
942f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
943f796f8b3SChuck Lever 	if (unlikely(p == NULL))
944f796f8b3SChuck Lever 		goto out_overflow;
945f796f8b3SChuck Lever 	entry->cookie = be32_to_cpup(p);
946f796f8b3SChuck Lever 
947f796f8b3SChuck Lever 	entry->d_type = DT_UNKNOWN;
948f796f8b3SChuck Lever 
949573c4e1eSChuck Lever 	return 0;
950f796f8b3SChuck Lever 
951f796f8b3SChuck Lever out_overflow:
952f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
953573c4e1eSChuck Lever 	return -EAGAIN;
954f796f8b3SChuck Lever }
955f796f8b3SChuck Lever 
956f796f8b3SChuck Lever /*
957f796f8b3SChuck Lever  * 2.2.17.  readdirres
958f796f8b3SChuck Lever  *
959f796f8b3SChuck Lever  *	union readdirres switch (stat status) {
960f796f8b3SChuck Lever  *	case NFS_OK:
961f796f8b3SChuck Lever  *		struct {
962f796f8b3SChuck Lever  *			entry *entries;
963f796f8b3SChuck Lever  *			bool eof;
964f796f8b3SChuck Lever  *		} readdirok;
965f796f8b3SChuck Lever  *	default:
966f796f8b3SChuck Lever  *		void;
967f796f8b3SChuck Lever  *	};
968f796f8b3SChuck Lever  *
969f796f8b3SChuck Lever  * Read the directory contents into the page cache, but don't
970f796f8b3SChuck Lever  * touch them.  The actual decoding is done by nfs2_decode_dirent()
971f796f8b3SChuck Lever  * during subsequent nfs_readdir() calls.
972f796f8b3SChuck Lever  */
973f796f8b3SChuck Lever static int decode_readdirok(struct xdr_stream *xdr)
974f796f8b3SChuck Lever {
975f796f8b3SChuck Lever 	u32 recvd, pglen;
976f796f8b3SChuck Lever 	size_t hdrlen;
977f796f8b3SChuck Lever 
978f796f8b3SChuck Lever 	pglen = xdr->buf->page_len;
979f796f8b3SChuck Lever 	hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
980f796f8b3SChuck Lever 	recvd = xdr->buf->len - hdrlen;
981f796f8b3SChuck Lever 	if (unlikely(pglen > recvd))
982f796f8b3SChuck Lever 		goto out_cheating;
983f796f8b3SChuck Lever out:
984f796f8b3SChuck Lever 	xdr_read_pages(xdr, pglen);
985f796f8b3SChuck Lever 	return pglen;
986f796f8b3SChuck Lever out_cheating:
987f796f8b3SChuck Lever 	dprintk("NFS: server cheating in readdir result: "
988f796f8b3SChuck Lever 		"pglen %u > recvd %u\n", pglen, recvd);
989f796f8b3SChuck Lever 	pglen = recvd;
990f796f8b3SChuck Lever 	goto out;
991f796f8b3SChuck Lever }
992f796f8b3SChuck Lever 
993bf269551SChuck Lever static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req,
994bf269551SChuck Lever 				   struct xdr_stream *xdr, void *__unused)
995f796f8b3SChuck Lever {
996f796f8b3SChuck Lever 	enum nfs_stat status;
997f796f8b3SChuck Lever 	int error;
998f796f8b3SChuck Lever 
999bf269551SChuck Lever 	error = decode_stat(xdr, &status);
1000f796f8b3SChuck Lever 	if (unlikely(error))
1001f796f8b3SChuck Lever 		goto out;
1002f796f8b3SChuck Lever 	if (status != NFS_OK)
1003f796f8b3SChuck Lever 		goto out_default;
1004bf269551SChuck Lever 	error = decode_readdirok(xdr);
1005f796f8b3SChuck Lever out:
1006f796f8b3SChuck Lever 	return error;
1007f796f8b3SChuck Lever out_default:
1008f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
1009f796f8b3SChuck Lever }
1010f796f8b3SChuck Lever 
10111da177e4SLinus Torvalds /*
1012f796f8b3SChuck Lever  * 2.2.18.  statfsres
1013f796f8b3SChuck Lever  *
1014f796f8b3SChuck Lever  *	union statfsres (stat status) {
1015f796f8b3SChuck Lever  *	case NFS_OK:
1016f796f8b3SChuck Lever  *		struct {
1017f796f8b3SChuck Lever  *			unsigned tsize;
1018f796f8b3SChuck Lever  *			unsigned bsize;
1019f796f8b3SChuck Lever  *			unsigned blocks;
1020f796f8b3SChuck Lever  *			unsigned bfree;
1021f796f8b3SChuck Lever  *			unsigned bavail;
1022f796f8b3SChuck Lever  *		} info;
1023f796f8b3SChuck Lever  *	default:
1024f796f8b3SChuck Lever  *		void;
1025f796f8b3SChuck Lever  *	};
1026f796f8b3SChuck Lever  */
1027f796f8b3SChuck Lever static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result)
1028f796f8b3SChuck Lever {
1029f796f8b3SChuck Lever 	__be32 *p;
1030f796f8b3SChuck Lever 
1031f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS_info_sz << 2);
1032f796f8b3SChuck Lever 	if (unlikely(p == NULL))
1033f796f8b3SChuck Lever 		goto out_overflow;
1034f796f8b3SChuck Lever 	result->tsize  = be32_to_cpup(p++);
1035f796f8b3SChuck Lever 	result->bsize  = be32_to_cpup(p++);
1036f796f8b3SChuck Lever 	result->blocks = be32_to_cpup(p++);
1037f796f8b3SChuck Lever 	result->bfree  = be32_to_cpup(p++);
1038f796f8b3SChuck Lever 	result->bavail = be32_to_cpup(p);
1039f796f8b3SChuck Lever 	return 0;
1040f796f8b3SChuck Lever out_overflow:
1041f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
1042f796f8b3SChuck Lever 	return -EIO;
1043f796f8b3SChuck Lever }
1044f796f8b3SChuck Lever 
1045bf269551SChuck Lever static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr,
1046f796f8b3SChuck Lever 				  struct nfs2_fsstat *result)
1047f796f8b3SChuck Lever {
1048f796f8b3SChuck Lever 	enum nfs_stat status;
1049f796f8b3SChuck Lever 	int error;
1050f796f8b3SChuck Lever 
1051bf269551SChuck Lever 	error = decode_stat(xdr, &status);
1052f796f8b3SChuck Lever 	if (unlikely(error))
1053f796f8b3SChuck Lever 		goto out;
1054f796f8b3SChuck Lever 	if (status != NFS_OK)
1055f796f8b3SChuck Lever 		goto out_default;
1056bf269551SChuck Lever 	error = decode_info(xdr, result);
1057f796f8b3SChuck Lever out:
1058f796f8b3SChuck Lever 	return error;
1059f796f8b3SChuck Lever out_default:
1060f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
1061f796f8b3SChuck Lever }
1062f796f8b3SChuck Lever 
1063f796f8b3SChuck Lever 
1064f796f8b3SChuck Lever /*
10651da177e4SLinus Torvalds  * We need to translate between nfs status return values and
10661da177e4SLinus Torvalds  * the local errno values which may not be the same.
10671da177e4SLinus Torvalds  */
106885828493SChuck Lever static const struct {
10691da177e4SLinus Torvalds 	int stat;
10701da177e4SLinus Torvalds 	int errno;
10711da177e4SLinus Torvalds } nfs_errtbl[] = {
10721da177e4SLinus Torvalds 	{ NFS_OK,		0		},
1073856dff3dSBenny Halevy 	{ NFSERR_PERM,		-EPERM		},
1074856dff3dSBenny Halevy 	{ NFSERR_NOENT,		-ENOENT		},
1075856dff3dSBenny Halevy 	{ NFSERR_IO,		-errno_NFSERR_IO},
1076856dff3dSBenny Halevy 	{ NFSERR_NXIO,		-ENXIO		},
1077856dff3dSBenny Halevy /*	{ NFSERR_EAGAIN,	-EAGAIN		}, */
1078856dff3dSBenny Halevy 	{ NFSERR_ACCES,		-EACCES		},
1079856dff3dSBenny Halevy 	{ NFSERR_EXIST,		-EEXIST		},
1080856dff3dSBenny Halevy 	{ NFSERR_XDEV,		-EXDEV		},
1081856dff3dSBenny Halevy 	{ NFSERR_NODEV,		-ENODEV		},
1082856dff3dSBenny Halevy 	{ NFSERR_NOTDIR,	-ENOTDIR	},
1083856dff3dSBenny Halevy 	{ NFSERR_ISDIR,		-EISDIR		},
1084856dff3dSBenny Halevy 	{ NFSERR_INVAL,		-EINVAL		},
1085856dff3dSBenny Halevy 	{ NFSERR_FBIG,		-EFBIG		},
1086856dff3dSBenny Halevy 	{ NFSERR_NOSPC,		-ENOSPC		},
1087856dff3dSBenny Halevy 	{ NFSERR_ROFS,		-EROFS		},
1088856dff3dSBenny Halevy 	{ NFSERR_MLINK,		-EMLINK		},
1089856dff3dSBenny Halevy 	{ NFSERR_NAMETOOLONG,	-ENAMETOOLONG	},
1090856dff3dSBenny Halevy 	{ NFSERR_NOTEMPTY,	-ENOTEMPTY	},
1091856dff3dSBenny Halevy 	{ NFSERR_DQUOT,		-EDQUOT		},
1092856dff3dSBenny Halevy 	{ NFSERR_STALE,		-ESTALE		},
1093856dff3dSBenny Halevy 	{ NFSERR_REMOTE,	-EREMOTE	},
10941da177e4SLinus Torvalds #ifdef EWFLUSH
1095856dff3dSBenny Halevy 	{ NFSERR_WFLUSH,	-EWFLUSH	},
10961da177e4SLinus Torvalds #endif
1097856dff3dSBenny Halevy 	{ NFSERR_BADHANDLE,	-EBADHANDLE	},
1098856dff3dSBenny Halevy 	{ NFSERR_NOT_SYNC,	-ENOTSYNC	},
1099856dff3dSBenny Halevy 	{ NFSERR_BAD_COOKIE,	-EBADCOOKIE	},
1100856dff3dSBenny Halevy 	{ NFSERR_NOTSUPP,	-ENOTSUPP	},
1101856dff3dSBenny Halevy 	{ NFSERR_TOOSMALL,	-ETOOSMALL	},
1102fdcb4577STrond Myklebust 	{ NFSERR_SERVERFAULT,	-EREMOTEIO	},
1103856dff3dSBenny Halevy 	{ NFSERR_BADTYPE,	-EBADTYPE	},
1104856dff3dSBenny Halevy 	{ NFSERR_JUKEBOX,	-EJUKEBOX	},
1105856dff3dSBenny Halevy 	{ -1,			-EIO		}
11061da177e4SLinus Torvalds };
11071da177e4SLinus Torvalds 
110885828493SChuck Lever /**
110985828493SChuck Lever  * nfs_stat_to_errno - convert an NFS status code to a local errno
111085828493SChuck Lever  * @status: NFS status code to convert
111185828493SChuck Lever  *
111285828493SChuck Lever  * Returns a local errno value, or -EIO if the NFS status code is
111385828493SChuck Lever  * not recognized.  This function is used jointly by NFSv2 and NFSv3.
11141da177e4SLinus Torvalds  */
11155e7e5a0dSBryan Schumaker static int nfs_stat_to_errno(enum nfs_stat status)
11161da177e4SLinus Torvalds {
11171da177e4SLinus Torvalds 	int i;
11181da177e4SLinus Torvalds 
11191da177e4SLinus Torvalds 	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
112085828493SChuck Lever 		if (nfs_errtbl[i].stat == (int)status)
11211da177e4SLinus Torvalds 			return nfs_errtbl[i].errno;
11221da177e4SLinus Torvalds 	}
112385828493SChuck Lever 	dprintk("NFS: Unrecognized nfs status value: %u\n", status);
11241da177e4SLinus Torvalds 	return nfs_errtbl[i].errno;
11251da177e4SLinus Torvalds }
11261da177e4SLinus Torvalds 
11271da177e4SLinus Torvalds #define PROC(proc, argtype, restype, timer)				\
11281da177e4SLinus Torvalds [NFSPROC_##proc] = {							\
11291da177e4SLinus Torvalds 	.p_proc	    =  NFSPROC_##proc,					\
11309f06c719SChuck Lever 	.p_encode   =  (kxdreproc_t)nfs2_xdr_enc_##argtype,		\
1131bf269551SChuck Lever 	.p_decode   =  (kxdrdproc_t)nfs2_xdr_dec_##restype,		\
11322bea90d4SChuck Lever 	.p_arglen   =  NFS_##argtype##_sz,				\
11332bea90d4SChuck Lever 	.p_replen   =  NFS_##restype##_sz,				\
1134cc0175c1SChuck Lever 	.p_timer    =  timer,						\
1135cc0175c1SChuck Lever 	.p_statidx  =  NFSPROC_##proc,					\
1136cc0175c1SChuck Lever 	.p_name     =  #proc,						\
11371da177e4SLinus Torvalds 	}
11381da177e4SLinus Torvalds struct rpc_procinfo	nfs_procedures[] = {
11391da177e4SLinus Torvalds 	PROC(GETATTR,	fhandle,	attrstat,	1),
11401da177e4SLinus Torvalds 	PROC(SETATTR,	sattrargs,	attrstat,	0),
11411da177e4SLinus Torvalds 	PROC(LOOKUP,	diropargs,	diropres,	2),
11421da177e4SLinus Torvalds 	PROC(READLINK,	readlinkargs,	readlinkres,	3),
11431da177e4SLinus Torvalds 	PROC(READ,	readargs,	readres,	3),
11441da177e4SLinus Torvalds 	PROC(WRITE,	writeargs,	writeres,	4),
11451da177e4SLinus Torvalds 	PROC(CREATE,	createargs,	diropres,	0),
11464fdc17b2STrond Myklebust 	PROC(REMOVE,	removeargs,	stat,		0),
11471da177e4SLinus Torvalds 	PROC(RENAME,	renameargs,	stat,		0),
11481da177e4SLinus Torvalds 	PROC(LINK,	linkargs,	stat,		0),
11491da177e4SLinus Torvalds 	PROC(SYMLINK,	symlinkargs,	stat,		0),
11501da177e4SLinus Torvalds 	PROC(MKDIR,	createargs,	diropres,	0),
11511da177e4SLinus Torvalds 	PROC(RMDIR,	diropargs,	stat,		0),
11521da177e4SLinus Torvalds 	PROC(READDIR,	readdirargs,	readdirres,	3),
11531da177e4SLinus Torvalds 	PROC(STATFS,	fhandle,	statfsres,	0),
11541da177e4SLinus Torvalds };
11551da177e4SLinus Torvalds 
1156a613fa16STrond Myklebust const struct rpc_version nfs_version2 = {
11571da177e4SLinus Torvalds 	.number			= 2,
1158e8c96f8cSTobias Klauser 	.nrprocs		= ARRAY_SIZE(nfs_procedures),
11591da177e4SLinus Torvalds 	.procs			= nfs_procedures
11601da177e4SLinus Torvalds };
1161