xref: /openbmc/linux/fs/nfs/nfs2xdr.c (revision c551858a)
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  */
1069137bdf3SAnna Schumaker static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_pgio_res *result)
107f796f8b3SChuck Lever {
108f796f8b3SChuck Lever 	u32 recvd, count;
109f796f8b3SChuck Lever 	__be32 *p;
110f796f8b3SChuck Lever 
111f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
112f796f8b3SChuck Lever 	if (unlikely(p == NULL))
113f796f8b3SChuck Lever 		goto out_overflow;
114f796f8b3SChuck Lever 	count = be32_to_cpup(p);
11564bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, count);
116f796f8b3SChuck Lever 	if (unlikely(count > recvd))
117f796f8b3SChuck Lever 		goto out_cheating;
118f796f8b3SChuck Lever out:
119f796f8b3SChuck Lever 	result->eof = 0;	/* NFSv2 does not pass EOF flag on the wire. */
120f796f8b3SChuck Lever 	result->count = count;
121f796f8b3SChuck Lever 	return count;
122f796f8b3SChuck Lever out_cheating:
123f796f8b3SChuck Lever 	dprintk("NFS: server cheating in read result: "
124f796f8b3SChuck Lever 		"count %u > recvd %u\n", count, recvd);
125f796f8b3SChuck Lever 	count = recvd;
126f796f8b3SChuck Lever 	goto out;
127f796f8b3SChuck Lever out_overflow:
128f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
129f796f8b3SChuck Lever 	return -EIO;
130f796f8b3SChuck Lever }
131f796f8b3SChuck Lever 
132f796f8b3SChuck Lever /*
133f796f8b3SChuck Lever  *	enum stat {
134f796f8b3SChuck Lever  *		NFS_OK = 0,
135f796f8b3SChuck Lever  *		NFSERR_PERM = 1,
136f796f8b3SChuck Lever  *		NFSERR_NOENT = 2,
137f796f8b3SChuck Lever  *		NFSERR_IO = 5,
138f796f8b3SChuck Lever  *		NFSERR_NXIO = 6,
139f796f8b3SChuck Lever  *		NFSERR_ACCES = 13,
140f796f8b3SChuck Lever  *		NFSERR_EXIST = 17,
141f796f8b3SChuck Lever  *		NFSERR_NODEV = 19,
142f796f8b3SChuck Lever  *		NFSERR_NOTDIR = 20,
143f796f8b3SChuck Lever  *		NFSERR_ISDIR = 21,
144f796f8b3SChuck Lever  *		NFSERR_FBIG = 27,
145f796f8b3SChuck Lever  *		NFSERR_NOSPC = 28,
146f796f8b3SChuck Lever  *		NFSERR_ROFS = 30,
147f796f8b3SChuck Lever  *		NFSERR_NAMETOOLONG = 63,
148f796f8b3SChuck Lever  *		NFSERR_NOTEMPTY = 66,
149f796f8b3SChuck Lever  *		NFSERR_DQUOT = 69,
150f796f8b3SChuck Lever  *		NFSERR_STALE = 70,
151f796f8b3SChuck Lever  *		NFSERR_WFLUSH = 99
152f796f8b3SChuck Lever  *	};
153f796f8b3SChuck Lever  */
154f796f8b3SChuck Lever static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status)
155f796f8b3SChuck Lever {
156f796f8b3SChuck Lever 	__be32 *p;
157f796f8b3SChuck Lever 
158f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
159f796f8b3SChuck Lever 	if (unlikely(p == NULL))
160f796f8b3SChuck Lever 		goto out_overflow;
161f796f8b3SChuck Lever 	*status = be32_to_cpup(p);
162f796f8b3SChuck Lever 	return 0;
163f796f8b3SChuck Lever out_overflow:
164f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
165f796f8b3SChuck Lever 	return -EIO;
166f796f8b3SChuck Lever }
167f796f8b3SChuck Lever 
168f796f8b3SChuck Lever /*
1695f96e5e3SChuck Lever  * 2.3.2.  ftype
1705f96e5e3SChuck Lever  *
1715f96e5e3SChuck Lever  *	enum ftype {
1725f96e5e3SChuck Lever  *		NFNON = 0,
1735f96e5e3SChuck Lever  *		NFREG = 1,
1745f96e5e3SChuck Lever  *		NFDIR = 2,
1755f96e5e3SChuck Lever  *		NFBLK = 3,
1765f96e5e3SChuck Lever  *		NFCHR = 4,
1775f96e5e3SChuck Lever  *		NFLNK = 5
1785f96e5e3SChuck Lever  *	};
1795f96e5e3SChuck Lever  *
1805f96e5e3SChuck Lever  */
1815f96e5e3SChuck Lever static __be32 *xdr_decode_ftype(__be32 *p, u32 *type)
1825f96e5e3SChuck Lever {
1835f96e5e3SChuck Lever 	*type = be32_to_cpup(p++);
1845f96e5e3SChuck Lever 	if (unlikely(*type > NF2FIFO))
1855f96e5e3SChuck Lever 		*type = NFBAD;
1865f96e5e3SChuck Lever 	return p;
1875f96e5e3SChuck Lever }
1885f96e5e3SChuck Lever 
1895f96e5e3SChuck Lever /*
19025a0866cSChuck Lever  * 2.3.3.  fhandle
19125a0866cSChuck Lever  *
19225a0866cSChuck Lever  *	typedef opaque fhandle[FHSIZE];
19325a0866cSChuck Lever  */
19425a0866cSChuck Lever static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh)
19525a0866cSChuck Lever {
19625a0866cSChuck Lever 	__be32 *p;
19725a0866cSChuck Lever 
19825a0866cSChuck Lever 	p = xdr_reserve_space(xdr, NFS2_FHSIZE);
19925a0866cSChuck Lever 	memcpy(p, fh->data, NFS2_FHSIZE);
20025a0866cSChuck Lever }
20125a0866cSChuck Lever 
202f796f8b3SChuck Lever static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh)
203f796f8b3SChuck Lever {
204f796f8b3SChuck Lever 	__be32 *p;
205f796f8b3SChuck Lever 
206f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS2_FHSIZE);
207f796f8b3SChuck Lever 	if (unlikely(p == NULL))
208f796f8b3SChuck Lever 		goto out_overflow;
209f796f8b3SChuck Lever 	fh->size = NFS2_FHSIZE;
210f796f8b3SChuck Lever 	memcpy(fh->data, p, NFS2_FHSIZE);
211f796f8b3SChuck Lever 	return 0;
212f796f8b3SChuck Lever out_overflow:
213f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
214f796f8b3SChuck Lever 	return -EIO;
215f796f8b3SChuck Lever }
216f796f8b3SChuck Lever 
21725a0866cSChuck Lever /*
218282ac2a5SChuck Lever  * 2.3.4.  timeval
219282ac2a5SChuck Lever  *
220282ac2a5SChuck Lever  *	struct timeval {
221282ac2a5SChuck Lever  *		unsigned int seconds;
222282ac2a5SChuck Lever  *		unsigned int useconds;
223282ac2a5SChuck Lever  *	};
224282ac2a5SChuck Lever  */
225282ac2a5SChuck Lever static __be32 *xdr_encode_time(__be32 *p, const struct timespec *timep)
226282ac2a5SChuck Lever {
227282ac2a5SChuck Lever 	*p++ = cpu_to_be32(timep->tv_sec);
228282ac2a5SChuck Lever 	if (timep->tv_nsec != 0)
229282ac2a5SChuck Lever 		*p++ = cpu_to_be32(timep->tv_nsec / NSEC_PER_USEC);
230282ac2a5SChuck Lever 	else
231282ac2a5SChuck Lever 		*p++ = cpu_to_be32(0);
232282ac2a5SChuck Lever 	return p;
233282ac2a5SChuck Lever }
234282ac2a5SChuck Lever 
235282ac2a5SChuck Lever /*
236282ac2a5SChuck Lever  * Passing the invalid value useconds=1000000 is a Sun convention for
237282ac2a5SChuck Lever  * "set to current server time".  It's needed to make permissions checks
238282ac2a5SChuck Lever  * for the "touch" program across v2 mounts to Solaris and Irix servers
239282ac2a5SChuck Lever  * work correctly.  See description of sattr in section 6.1 of "NFS
240282ac2a5SChuck Lever  * Illustrated" by Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5.
241282ac2a5SChuck Lever  */
242282ac2a5SChuck Lever static __be32 *xdr_encode_current_server_time(__be32 *p,
243282ac2a5SChuck Lever 					      const struct timespec *timep)
244282ac2a5SChuck Lever {
245282ac2a5SChuck Lever 	*p++ = cpu_to_be32(timep->tv_sec);
246282ac2a5SChuck Lever 	*p++ = cpu_to_be32(1000000);
247282ac2a5SChuck Lever 	return p;
248282ac2a5SChuck Lever }
249282ac2a5SChuck Lever 
2505f96e5e3SChuck Lever static __be32 *xdr_decode_time(__be32 *p, struct timespec *timep)
2515f96e5e3SChuck Lever {
2525f96e5e3SChuck Lever 	timep->tv_sec = be32_to_cpup(p++);
2535f96e5e3SChuck Lever 	timep->tv_nsec = be32_to_cpup(p++) * NSEC_PER_USEC;
2545f96e5e3SChuck Lever 	return p;
2555f96e5e3SChuck Lever }
2565f96e5e3SChuck Lever 
257282ac2a5SChuck Lever /*
258f796f8b3SChuck Lever  * 2.3.5.  fattr
259f796f8b3SChuck Lever  *
260f796f8b3SChuck Lever  *	struct fattr {
261f796f8b3SChuck Lever  *		ftype		type;
262f796f8b3SChuck Lever  *		unsigned int	mode;
263f796f8b3SChuck Lever  *		unsigned int	nlink;
264f796f8b3SChuck Lever  *		unsigned int	uid;
265f796f8b3SChuck Lever  *		unsigned int	gid;
266f796f8b3SChuck Lever  *		unsigned int	size;
267f796f8b3SChuck Lever  *		unsigned int	blocksize;
268f796f8b3SChuck Lever  *		unsigned int	rdev;
269f796f8b3SChuck Lever  *		unsigned int	blocks;
270f796f8b3SChuck Lever  *		unsigned int	fsid;
271f796f8b3SChuck Lever  *		unsigned int	fileid;
272f796f8b3SChuck Lever  *		timeval		atime;
273f796f8b3SChuck Lever  *		timeval		mtime;
274f796f8b3SChuck Lever  *		timeval		ctime;
275f796f8b3SChuck Lever  *	};
276f796f8b3SChuck Lever  *
277f796f8b3SChuck Lever  */
278f796f8b3SChuck Lever static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
279f796f8b3SChuck Lever {
2805f96e5e3SChuck Lever 	u32 rdev, type;
281f796f8b3SChuck Lever 	__be32 *p;
282f796f8b3SChuck Lever 
283f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS_fattr_sz << 2);
284f796f8b3SChuck Lever 	if (unlikely(p == NULL))
285f796f8b3SChuck Lever 		goto out_overflow;
2865f96e5e3SChuck Lever 
2875f96e5e3SChuck Lever 	fattr->valid |= NFS_ATTR_FATTR_V2;
2885f96e5e3SChuck Lever 
2895f96e5e3SChuck Lever 	p = xdr_decode_ftype(p, &type);
2905f96e5e3SChuck Lever 
2915f96e5e3SChuck Lever 	fattr->mode = be32_to_cpup(p++);
2925f96e5e3SChuck Lever 	fattr->nlink = be32_to_cpup(p++);
293cfa0898dSEric W. Biederman 	fattr->uid = make_kuid(&init_user_ns, be32_to_cpup(p++));
294cfa0898dSEric W. Biederman 	if (!uid_valid(fattr->uid))
295cfa0898dSEric W. Biederman 		goto out_uid;
296cfa0898dSEric W. Biederman 	fattr->gid = make_kgid(&init_user_ns, be32_to_cpup(p++));
297cfa0898dSEric W. Biederman 	if (!gid_valid(fattr->gid))
298cfa0898dSEric W. Biederman 		goto out_gid;
299cfa0898dSEric W. Biederman 
3005f96e5e3SChuck Lever 	fattr->size = be32_to_cpup(p++);
3015f96e5e3SChuck Lever 	fattr->du.nfs2.blocksize = be32_to_cpup(p++);
3025f96e5e3SChuck Lever 
3035f96e5e3SChuck Lever 	rdev = be32_to_cpup(p++);
3045f96e5e3SChuck Lever 	fattr->rdev = new_decode_dev(rdev);
3055f96e5e3SChuck Lever 	if (type == (u32)NFCHR && rdev == (u32)NFS2_FIFO_DEV) {
3065f96e5e3SChuck Lever 		fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
3075f96e5e3SChuck Lever 		fattr->rdev = 0;
3085f96e5e3SChuck Lever 	}
3095f96e5e3SChuck Lever 
3105f96e5e3SChuck Lever 	fattr->du.nfs2.blocks = be32_to_cpup(p++);
3115f96e5e3SChuck Lever 	fattr->fsid.major = be32_to_cpup(p++);
3125f96e5e3SChuck Lever 	fattr->fsid.minor = 0;
3135f96e5e3SChuck Lever 	fattr->fileid = be32_to_cpup(p++);
3145f96e5e3SChuck Lever 
3155f96e5e3SChuck Lever 	p = xdr_decode_time(p, &fattr->atime);
3165f96e5e3SChuck Lever 	p = xdr_decode_time(p, &fattr->mtime);
3175f96e5e3SChuck Lever 	xdr_decode_time(p, &fattr->ctime);
3183a1556e8STrond Myklebust 	fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime);
3193a1556e8STrond Myklebust 
320f796f8b3SChuck Lever 	return 0;
321cfa0898dSEric W. Biederman out_uid:
322cfa0898dSEric W. Biederman 	dprintk("NFS: returned invalid uid\n");
323cfa0898dSEric W. Biederman 	return -EINVAL;
324cfa0898dSEric W. Biederman out_gid:
325cfa0898dSEric W. Biederman 	dprintk("NFS: returned invalid gid\n");
326cfa0898dSEric W. Biederman 	return -EINVAL;
327f796f8b3SChuck Lever out_overflow:
328f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
329f796f8b3SChuck Lever 	return -EIO;
330f796f8b3SChuck Lever }
331f796f8b3SChuck Lever 
332f796f8b3SChuck Lever /*
33325a0866cSChuck Lever  * 2.3.6.  sattr
33425a0866cSChuck Lever  *
33525a0866cSChuck Lever  *	struct sattr {
33625a0866cSChuck Lever  *		unsigned int	mode;
33725a0866cSChuck Lever  *		unsigned int	uid;
33825a0866cSChuck Lever  *		unsigned int	gid;
33925a0866cSChuck Lever  *		unsigned int	size;
34025a0866cSChuck Lever  *		timeval		atime;
34125a0866cSChuck Lever  *		timeval		mtime;
34225a0866cSChuck Lever  *	};
34325a0866cSChuck Lever  */
34425a0866cSChuck Lever 
34525a0866cSChuck Lever #define NFS2_SATTR_NOT_SET	(0xffffffff)
34625a0866cSChuck Lever 
34725a0866cSChuck Lever static __be32 *xdr_time_not_set(__be32 *p)
34825a0866cSChuck Lever {
34925a0866cSChuck Lever 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
35025a0866cSChuck Lever 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
35125a0866cSChuck Lever 	return p;
35225a0866cSChuck Lever }
35325a0866cSChuck Lever 
35425a0866cSChuck Lever static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr)
35525a0866cSChuck Lever {
35625a0866cSChuck Lever 	__be32 *p;
35725a0866cSChuck Lever 
35825a0866cSChuck Lever 	p = xdr_reserve_space(xdr, NFS_sattr_sz << 2);
35925a0866cSChuck Lever 
36025a0866cSChuck Lever 	if (attr->ia_valid & ATTR_MODE)
36125a0866cSChuck Lever 		*p++ = cpu_to_be32(attr->ia_mode);
36225a0866cSChuck Lever 	else
36325a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
36425a0866cSChuck Lever 	if (attr->ia_valid & ATTR_UID)
365cfa0898dSEric W. Biederman 		*p++ = cpu_to_be32(from_kuid(&init_user_ns, attr->ia_uid));
36625a0866cSChuck Lever 	else
36725a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
36825a0866cSChuck Lever 	if (attr->ia_valid & ATTR_GID)
369cfa0898dSEric W. Biederman 		*p++ = cpu_to_be32(from_kgid(&init_user_ns, attr->ia_gid));
37025a0866cSChuck Lever 	else
37125a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
37225a0866cSChuck Lever 	if (attr->ia_valid & ATTR_SIZE)
37325a0866cSChuck Lever 		*p++ = cpu_to_be32((u32)attr->ia_size);
37425a0866cSChuck Lever 	else
37525a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
37625a0866cSChuck Lever 
37725a0866cSChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET)
37825a0866cSChuck Lever 		p = xdr_encode_time(p, &attr->ia_atime);
37925a0866cSChuck Lever 	else if (attr->ia_valid & ATTR_ATIME)
38025a0866cSChuck Lever 		p = xdr_encode_current_server_time(p, &attr->ia_atime);
38125a0866cSChuck Lever 	else
38225a0866cSChuck Lever 		p = xdr_time_not_set(p);
38325a0866cSChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET)
38425a0866cSChuck Lever 		xdr_encode_time(p, &attr->ia_mtime);
38525a0866cSChuck Lever 	else if (attr->ia_valid & ATTR_MTIME)
38625a0866cSChuck Lever 		xdr_encode_current_server_time(p, &attr->ia_mtime);
38725a0866cSChuck Lever 	else
38825a0866cSChuck Lever 		xdr_time_not_set(p);
38925a0866cSChuck Lever }
39025a0866cSChuck Lever 
39125a0866cSChuck Lever /*
39225a0866cSChuck Lever  * 2.3.7.  filename
39325a0866cSChuck Lever  *
39425a0866cSChuck Lever  *	typedef string filename<MAXNAMLEN>;
39525a0866cSChuck Lever  */
39625a0866cSChuck Lever static void encode_filename(struct xdr_stream *xdr,
39725a0866cSChuck Lever 			    const char *name, u32 length)
39825a0866cSChuck Lever {
39925a0866cSChuck Lever 	__be32 *p;
40025a0866cSChuck Lever 
4017fc38846STrond Myklebust 	WARN_ON_ONCE(length > NFS2_MAXNAMLEN);
40225a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + length);
40325a0866cSChuck Lever 	xdr_encode_opaque(p, name, length);
40425a0866cSChuck Lever }
40525a0866cSChuck Lever 
406f796f8b3SChuck Lever static int decode_filename_inline(struct xdr_stream *xdr,
407f796f8b3SChuck Lever 				  const char **name, u32 *length)
408f796f8b3SChuck Lever {
409f796f8b3SChuck Lever 	__be32 *p;
410f796f8b3SChuck Lever 	u32 count;
411f796f8b3SChuck Lever 
412f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
413f796f8b3SChuck Lever 	if (unlikely(p == NULL))
414f796f8b3SChuck Lever 		goto out_overflow;
415f796f8b3SChuck Lever 	count = be32_to_cpup(p);
416f796f8b3SChuck Lever 	if (count > NFS3_MAXNAMLEN)
417f796f8b3SChuck Lever 		goto out_nametoolong;
418f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, count);
419f796f8b3SChuck Lever 	if (unlikely(p == NULL))
420f796f8b3SChuck Lever 		goto out_overflow;
421f796f8b3SChuck Lever 	*name = (const char *)p;
422f796f8b3SChuck Lever 	*length = count;
423f796f8b3SChuck Lever 	return 0;
424f796f8b3SChuck Lever out_nametoolong:
425f796f8b3SChuck Lever 	dprintk("NFS: returned filename too long: %u\n", count);
426f796f8b3SChuck Lever 	return -ENAMETOOLONG;
427f796f8b3SChuck Lever out_overflow:
428f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
429f796f8b3SChuck Lever 	return -EIO;
430f796f8b3SChuck Lever }
431f796f8b3SChuck Lever 
43225a0866cSChuck Lever /*
43325a0866cSChuck Lever  * 2.3.8.  path
43425a0866cSChuck Lever  *
43525a0866cSChuck Lever  *	typedef string path<MAXPATHLEN>;
43625a0866cSChuck Lever  */
43725a0866cSChuck Lever static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length)
43825a0866cSChuck Lever {
43925a0866cSChuck Lever 	__be32 *p;
44025a0866cSChuck Lever 
44125a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4);
44225a0866cSChuck Lever 	*p = cpu_to_be32(length);
44325a0866cSChuck Lever 	xdr_write_pages(xdr, pages, 0, length);
44425a0866cSChuck Lever }
44525a0866cSChuck Lever 
446f796f8b3SChuck Lever static int decode_path(struct xdr_stream *xdr)
447f796f8b3SChuck Lever {
448f796f8b3SChuck Lever 	u32 length, recvd;
449f796f8b3SChuck Lever 	__be32 *p;
450f796f8b3SChuck Lever 
451f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
452f796f8b3SChuck Lever 	if (unlikely(p == NULL))
453f796f8b3SChuck Lever 		goto out_overflow;
454f796f8b3SChuck Lever 	length = be32_to_cpup(p);
455f796f8b3SChuck Lever 	if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN))
456f796f8b3SChuck Lever 		goto out_size;
45764bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, length);
458f796f8b3SChuck Lever 	if (unlikely(length > recvd))
459f796f8b3SChuck Lever 		goto out_cheating;
460f796f8b3SChuck Lever 	xdr_terminate_string(xdr->buf, length);
461f796f8b3SChuck Lever 	return 0;
462f796f8b3SChuck Lever out_size:
463f796f8b3SChuck Lever 	dprintk("NFS: returned pathname too long: %u\n", length);
464f796f8b3SChuck Lever 	return -ENAMETOOLONG;
465f796f8b3SChuck Lever out_cheating:
466f796f8b3SChuck Lever 	dprintk("NFS: server cheating in pathname result: "
467f796f8b3SChuck Lever 		"length %u > received %u\n", length, recvd);
468f796f8b3SChuck Lever 	return -EIO;
469f796f8b3SChuck Lever out_overflow:
470f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
471f796f8b3SChuck Lever 	return -EIO;
472f796f8b3SChuck Lever }
473f796f8b3SChuck Lever 
474f796f8b3SChuck Lever /*
475f796f8b3SChuck Lever  * 2.3.9.  attrstat
476f796f8b3SChuck Lever  *
477f796f8b3SChuck Lever  *	union attrstat switch (stat status) {
478f796f8b3SChuck Lever  *	case NFS_OK:
479f796f8b3SChuck Lever  *		fattr attributes;
480f796f8b3SChuck Lever  *	default:
481f796f8b3SChuck Lever  *		void;
482f796f8b3SChuck Lever  *	};
483f796f8b3SChuck Lever  */
484aabff4ddSPeng Tao static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result,
485aabff4ddSPeng Tao 			   __u32 *op_status)
486f796f8b3SChuck Lever {
487f796f8b3SChuck Lever 	enum nfs_stat status;
488f796f8b3SChuck Lever 	int error;
489f796f8b3SChuck Lever 
490f796f8b3SChuck Lever 	error = decode_stat(xdr, &status);
491f796f8b3SChuck Lever 	if (unlikely(error))
492f796f8b3SChuck Lever 		goto out;
493aabff4ddSPeng Tao 	if (op_status)
494aabff4ddSPeng Tao 		*op_status = status;
495f796f8b3SChuck Lever 	if (status != NFS_OK)
496f796f8b3SChuck Lever 		goto out_default;
497f796f8b3SChuck Lever 	error = decode_fattr(xdr, result);
498f796f8b3SChuck Lever out:
499f796f8b3SChuck Lever 	return error;
500f796f8b3SChuck Lever out_default:
501f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
502f796f8b3SChuck Lever }
503f796f8b3SChuck Lever 
50425a0866cSChuck Lever /*
50525a0866cSChuck Lever  * 2.3.10.  diropargs
50625a0866cSChuck Lever  *
50725a0866cSChuck Lever  *	struct diropargs {
50825a0866cSChuck Lever  *		fhandle  dir;
50925a0866cSChuck Lever  *		filename name;
51025a0866cSChuck Lever  *	};
51125a0866cSChuck Lever  */
51225a0866cSChuck Lever static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh,
51325a0866cSChuck Lever 			     const char *name, u32 length)
51425a0866cSChuck Lever {
51525a0866cSChuck Lever 	encode_fhandle(xdr, fh);
51625a0866cSChuck Lever 	encode_filename(xdr, name, length);
51725a0866cSChuck Lever }
51825a0866cSChuck Lever 
519f796f8b3SChuck Lever /*
520f796f8b3SChuck Lever  * 2.3.11.  diropres
521f796f8b3SChuck Lever  *
522f796f8b3SChuck Lever  *	union diropres switch (stat status) {
523f796f8b3SChuck Lever  *	case NFS_OK:
524f796f8b3SChuck Lever  *		struct {
525f796f8b3SChuck Lever  *			fhandle file;
526f796f8b3SChuck Lever  *			fattr   attributes;
527f796f8b3SChuck Lever  *		} diropok;
528f796f8b3SChuck Lever  *	default:
529f796f8b3SChuck Lever  *		void;
530f796f8b3SChuck Lever  *	};
531f796f8b3SChuck Lever  */
532f796f8b3SChuck Lever static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result)
533f796f8b3SChuck Lever {
534f796f8b3SChuck Lever 	int error;
535f796f8b3SChuck Lever 
536f796f8b3SChuck Lever 	error = decode_fhandle(xdr, result->fh);
537f796f8b3SChuck Lever 	if (unlikely(error))
538f796f8b3SChuck Lever 		goto out;
539f796f8b3SChuck Lever 	error = decode_fattr(xdr, result->fattr);
540f796f8b3SChuck Lever out:
541f796f8b3SChuck Lever 	return error;
542f796f8b3SChuck Lever }
543f796f8b3SChuck Lever 
544f796f8b3SChuck Lever static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result)
545f796f8b3SChuck Lever {
546f796f8b3SChuck Lever 	enum nfs_stat status;
547f796f8b3SChuck Lever 	int error;
548f796f8b3SChuck Lever 
549f796f8b3SChuck Lever 	error = decode_stat(xdr, &status);
550f796f8b3SChuck Lever 	if (unlikely(error))
551f796f8b3SChuck Lever 		goto out;
552f796f8b3SChuck Lever 	if (status != NFS_OK)
553f796f8b3SChuck Lever 		goto out_default;
554f796f8b3SChuck Lever 	error = decode_diropok(xdr, result);
555f796f8b3SChuck Lever out:
556f796f8b3SChuck Lever 	return error;
557f796f8b3SChuck Lever out_default:
558f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
559f796f8b3SChuck Lever }
560f796f8b3SChuck Lever 
56125a0866cSChuck Lever 
56225a0866cSChuck Lever /*
5632d70f533SChuck Lever  * NFSv2 XDR encode functions
5642d70f533SChuck Lever  *
5652d70f533SChuck Lever  * NFSv2 argument types are defined in section 2.2 of RFC 1094:
5662d70f533SChuck Lever  * "NFS: Network File System Protocol Specification".
5671da177e4SLinus Torvalds  */
5681da177e4SLinus Torvalds 
5699f06c719SChuck Lever static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req,
5709f06c719SChuck Lever 				 struct xdr_stream *xdr,
571fcc85819SChristoph Hellwig 				 const void *data)
57225a0866cSChuck Lever {
573fcc85819SChristoph Hellwig 	const struct nfs_fh *fh = data;
574fcc85819SChristoph Hellwig 
5759f06c719SChuck Lever 	encode_fhandle(xdr, fh);
57625a0866cSChuck Lever }
57725a0866cSChuck Lever 
5781da177e4SLinus Torvalds /*
57925a0866cSChuck Lever  * 2.2.3.  sattrargs
58025a0866cSChuck Lever  *
58125a0866cSChuck Lever  *	struct sattrargs {
58225a0866cSChuck Lever  *		fhandle file;
58325a0866cSChuck Lever  *		sattr attributes;
58425a0866cSChuck Lever  *	};
58525a0866cSChuck Lever  */
5869f06c719SChuck Lever static void nfs2_xdr_enc_sattrargs(struct rpc_rqst *req,
5879f06c719SChuck Lever 				   struct xdr_stream *xdr,
588fcc85819SChristoph Hellwig 				   const void *data)
58925a0866cSChuck Lever {
590fcc85819SChristoph Hellwig 	const struct nfs_sattrargs *args = data;
591fcc85819SChristoph Hellwig 
5929f06c719SChuck Lever 	encode_fhandle(xdr, args->fh);
5939f06c719SChuck Lever 	encode_sattr(xdr, args->sattr);
59425a0866cSChuck Lever }
59525a0866cSChuck Lever 
5969f06c719SChuck Lever static void nfs2_xdr_enc_diropargs(struct rpc_rqst *req,
5979f06c719SChuck Lever 				   struct xdr_stream *xdr,
598fcc85819SChristoph Hellwig 				   const void *data)
59925a0866cSChuck Lever {
600fcc85819SChristoph Hellwig 	const struct nfs_diropargs *args = data;
601fcc85819SChristoph Hellwig 
6029f06c719SChuck Lever 	encode_diropargs(xdr, args->fh, args->name, args->len);
60325a0866cSChuck Lever }
60425a0866cSChuck Lever 
6059f06c719SChuck Lever static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req,
6069f06c719SChuck Lever 				      struct xdr_stream *xdr,
607fcc85819SChristoph Hellwig 				      const void *data)
60825a0866cSChuck Lever {
609fcc85819SChristoph Hellwig 	const struct nfs_readlinkargs *args = data;
610fcc85819SChristoph Hellwig 
6119f06c719SChuck Lever 	encode_fhandle(xdr, args->fh);
61225a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
61325a0866cSChuck Lever 					args->pglen, NFS_readlinkres_sz);
61425a0866cSChuck Lever }
61525a0866cSChuck Lever 
6164fdc17b2STrond Myklebust /*
61725a0866cSChuck Lever  * 2.2.7.  readargs
61825a0866cSChuck Lever  *
61925a0866cSChuck Lever  *	struct readargs {
62025a0866cSChuck Lever  *		fhandle file;
62125a0866cSChuck Lever  *		unsigned offset;
62225a0866cSChuck Lever  *		unsigned count;
62325a0866cSChuck Lever  *		unsigned totalcount;
62425a0866cSChuck Lever  *	};
62525a0866cSChuck Lever  */
62625a0866cSChuck Lever static void encode_readargs(struct xdr_stream *xdr,
6273c6b899cSAnna Schumaker 			    const struct nfs_pgio_args *args)
62825a0866cSChuck Lever {
62925a0866cSChuck Lever 	u32 offset = args->offset;
63025a0866cSChuck Lever 	u32 count = args->count;
63125a0866cSChuck Lever 	__be32 *p;
63225a0866cSChuck Lever 
63325a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
63425a0866cSChuck Lever 
63525a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4 + 4);
63625a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
63725a0866cSChuck Lever 	*p++ = cpu_to_be32(count);
63825a0866cSChuck Lever 	*p = cpu_to_be32(count);
63925a0866cSChuck Lever }
64025a0866cSChuck Lever 
6419f06c719SChuck Lever static void nfs2_xdr_enc_readargs(struct rpc_rqst *req,
6429f06c719SChuck Lever 				  struct xdr_stream *xdr,
643fcc85819SChristoph Hellwig 				  const void *data)
64425a0866cSChuck Lever {
645fcc85819SChristoph Hellwig 	const struct nfs_pgio_args *args = data;
646fcc85819SChristoph Hellwig 
6479f06c719SChuck Lever 	encode_readargs(xdr, args);
64825a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
64925a0866cSChuck Lever 					args->count, NFS_readres_sz);
65025a0866cSChuck Lever 	req->rq_rcv_buf.flags |= XDRBUF_READ;
65125a0866cSChuck Lever }
65225a0866cSChuck Lever 
65325a0866cSChuck Lever /*
65425a0866cSChuck Lever  * 2.2.9.  writeargs
65525a0866cSChuck Lever  *
65625a0866cSChuck Lever  *	struct writeargs {
65725a0866cSChuck Lever  *		fhandle file;
65825a0866cSChuck Lever  *		unsigned beginoffset;
65925a0866cSChuck Lever  *		unsigned offset;
66025a0866cSChuck Lever  *		unsigned totalcount;
66125a0866cSChuck Lever  *		nfsdata data;
66225a0866cSChuck Lever  *	};
66325a0866cSChuck Lever  */
66425a0866cSChuck Lever static void encode_writeargs(struct xdr_stream *xdr,
6653c6b899cSAnna Schumaker 			     const struct nfs_pgio_args *args)
66625a0866cSChuck Lever {
66725a0866cSChuck Lever 	u32 offset = args->offset;
66825a0866cSChuck Lever 	u32 count = args->count;
66925a0866cSChuck Lever 	__be32 *p;
67025a0866cSChuck Lever 
67125a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
67225a0866cSChuck Lever 
67325a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4);
67425a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
67525a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
67625a0866cSChuck Lever 	*p++ = cpu_to_be32(count);
67725a0866cSChuck Lever 
67825a0866cSChuck Lever 	/* nfsdata */
67925a0866cSChuck Lever 	*p = cpu_to_be32(count);
68025a0866cSChuck Lever 	xdr_write_pages(xdr, args->pages, args->pgbase, count);
68125a0866cSChuck Lever }
68225a0866cSChuck Lever 
6839f06c719SChuck Lever static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req,
6849f06c719SChuck Lever 				   struct xdr_stream *xdr,
685fcc85819SChristoph Hellwig 				   const void *data)
68625a0866cSChuck Lever {
687fcc85819SChristoph Hellwig 	const struct nfs_pgio_args *args = data;
688fcc85819SChristoph Hellwig 
6899f06c719SChuck Lever 	encode_writeargs(xdr, args);
6909f06c719SChuck Lever 	xdr->buf->flags |= XDRBUF_WRITE;
69125a0866cSChuck Lever }
69225a0866cSChuck Lever 
69325a0866cSChuck Lever /*
69425a0866cSChuck Lever  * 2.2.10.  createargs
69525a0866cSChuck Lever  *
69625a0866cSChuck Lever  *	struct createargs {
69725a0866cSChuck Lever  *		diropargs where;
69825a0866cSChuck Lever  *		sattr attributes;
69925a0866cSChuck Lever  *	};
70025a0866cSChuck Lever  */
7019f06c719SChuck Lever static void nfs2_xdr_enc_createargs(struct rpc_rqst *req,
7029f06c719SChuck Lever 				    struct xdr_stream *xdr,
703fcc85819SChristoph Hellwig 				    const void *data)
70425a0866cSChuck Lever {
705fcc85819SChristoph Hellwig 	const struct nfs_createargs *args = data;
706fcc85819SChristoph Hellwig 
7079f06c719SChuck Lever 	encode_diropargs(xdr, args->fh, args->name, args->len);
7089f06c719SChuck Lever 	encode_sattr(xdr, args->sattr);
70925a0866cSChuck Lever }
71025a0866cSChuck Lever 
7119f06c719SChuck Lever static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req,
7129f06c719SChuck Lever 				    struct xdr_stream *xdr,
713fcc85819SChristoph Hellwig 				    const void *data)
71425a0866cSChuck Lever {
715fcc85819SChristoph Hellwig 	const struct nfs_removeargs *args = data;
716fcc85819SChristoph Hellwig 
7179f06c719SChuck Lever 	encode_diropargs(xdr, args->fh, args->name.name, args->name.len);
71825a0866cSChuck Lever }
71925a0866cSChuck Lever 
72025a0866cSChuck Lever /*
72125a0866cSChuck Lever  * 2.2.12.  renameargs
72225a0866cSChuck Lever  *
72325a0866cSChuck Lever  *	struct renameargs {
72425a0866cSChuck Lever  *		diropargs from;
72525a0866cSChuck Lever  *		diropargs to;
72625a0866cSChuck Lever  *	};
72725a0866cSChuck Lever  */
7289f06c719SChuck Lever static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req,
7299f06c719SChuck Lever 				    struct xdr_stream *xdr,
730fcc85819SChristoph Hellwig 				    const void *data)
73125a0866cSChuck Lever {
732fcc85819SChristoph Hellwig 	const struct nfs_renameargs *args = data;
73325a0866cSChuck Lever 	const struct qstr *old = args->old_name;
73425a0866cSChuck Lever 	const struct qstr *new = args->new_name;
73525a0866cSChuck Lever 
7369f06c719SChuck Lever 	encode_diropargs(xdr, args->old_dir, old->name, old->len);
7379f06c719SChuck Lever 	encode_diropargs(xdr, args->new_dir, new->name, new->len);
73825a0866cSChuck Lever }
73925a0866cSChuck Lever 
74025a0866cSChuck Lever /*
74125a0866cSChuck Lever  * 2.2.13.  linkargs
74225a0866cSChuck Lever  *
74325a0866cSChuck Lever  *	struct linkargs {
74425a0866cSChuck Lever  *		fhandle from;
74525a0866cSChuck Lever  *		diropargs to;
74625a0866cSChuck Lever  *	};
74725a0866cSChuck Lever  */
7489f06c719SChuck Lever static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req,
7499f06c719SChuck Lever 				  struct xdr_stream *xdr,
750fcc85819SChristoph Hellwig 				  const void *data)
75125a0866cSChuck Lever {
752fcc85819SChristoph Hellwig 	const struct nfs_linkargs *args = data;
753fcc85819SChristoph Hellwig 
7549f06c719SChuck Lever 	encode_fhandle(xdr, args->fromfh);
7559f06c719SChuck Lever 	encode_diropargs(xdr, args->tofh, args->toname, args->tolen);
75625a0866cSChuck Lever }
75725a0866cSChuck Lever 
75825a0866cSChuck Lever /*
75925a0866cSChuck Lever  * 2.2.14.  symlinkargs
76025a0866cSChuck Lever  *
76125a0866cSChuck Lever  *	struct symlinkargs {
76225a0866cSChuck Lever  *		diropargs from;
76325a0866cSChuck Lever  *		path to;
76425a0866cSChuck Lever  *		sattr attributes;
76525a0866cSChuck Lever  *	};
76625a0866cSChuck Lever  */
7679f06c719SChuck Lever static void nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req,
7689f06c719SChuck Lever 				     struct xdr_stream *xdr,
769fcc85819SChristoph Hellwig 				     const void *data)
77025a0866cSChuck Lever {
771fcc85819SChristoph Hellwig 	const struct nfs_symlinkargs *args = data;
772fcc85819SChristoph Hellwig 
7739f06c719SChuck Lever 	encode_diropargs(xdr, args->fromfh, args->fromname, args->fromlen);
7749f06c719SChuck Lever 	encode_path(xdr, args->pages, args->pathlen);
7759f06c719SChuck Lever 	encode_sattr(xdr, args->sattr);
77625a0866cSChuck Lever }
77725a0866cSChuck Lever 
77825a0866cSChuck Lever /*
77925a0866cSChuck Lever  * 2.2.17.  readdirargs
78025a0866cSChuck Lever  *
78125a0866cSChuck Lever  *	struct readdirargs {
78225a0866cSChuck Lever  *		fhandle dir;
78325a0866cSChuck Lever  *		nfscookie cookie;
78425a0866cSChuck Lever  *		unsigned count;
78525a0866cSChuck Lever  *	};
78625a0866cSChuck Lever  */
78725a0866cSChuck Lever static void encode_readdirargs(struct xdr_stream *xdr,
78825a0866cSChuck Lever 			       const struct nfs_readdirargs *args)
78925a0866cSChuck Lever {
79025a0866cSChuck Lever 	__be32 *p;
79125a0866cSChuck Lever 
79225a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
79325a0866cSChuck Lever 
79425a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4);
79525a0866cSChuck Lever 	*p++ = cpu_to_be32(args->cookie);
79625a0866cSChuck Lever 	*p = cpu_to_be32(args->count);
79725a0866cSChuck Lever }
79825a0866cSChuck Lever 
7999f06c719SChuck Lever static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req,
8009f06c719SChuck Lever 				     struct xdr_stream *xdr,
801fcc85819SChristoph Hellwig 				     const void *data)
80225a0866cSChuck Lever {
803fcc85819SChristoph Hellwig 	const struct nfs_readdirargs *args = data;
804fcc85819SChristoph Hellwig 
8059f06c719SChuck Lever 	encode_readdirargs(xdr, args);
80625a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, 0,
80725a0866cSChuck Lever 					args->count, NFS_readdirres_sz);
80825a0866cSChuck Lever }
80925a0866cSChuck Lever 
81025a0866cSChuck Lever /*
811661ad423SChuck Lever  * NFSv2 XDR decode functions
812661ad423SChuck Lever  *
813661ad423SChuck Lever  * NFSv2 result types are defined in section 2.2 of RFC 1094:
814661ad423SChuck Lever  * "NFS: Network File System Protocol Specification".
8151da177e4SLinus Torvalds  */
8161da177e4SLinus Torvalds 
817bf269551SChuck Lever static int nfs2_xdr_dec_stat(struct rpc_rqst *req, struct xdr_stream *xdr,
818f796f8b3SChuck Lever 			     void *__unused)
819f796f8b3SChuck Lever {
820f796f8b3SChuck Lever 	enum nfs_stat status;
821f796f8b3SChuck Lever 	int error;
822f796f8b3SChuck Lever 
823bf269551SChuck Lever 	error = decode_stat(xdr, &status);
824f796f8b3SChuck Lever 	if (unlikely(error))
825f796f8b3SChuck Lever 		goto out;
826f796f8b3SChuck Lever 	if (status != NFS_OK)
827f796f8b3SChuck Lever 		goto out_default;
828f796f8b3SChuck Lever out:
829f796f8b3SChuck Lever 	return error;
830f796f8b3SChuck Lever out_default:
831f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
832f796f8b3SChuck Lever }
833f796f8b3SChuck Lever 
834bf269551SChuck Lever static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, struct xdr_stream *xdr,
835fc016483SChristoph Hellwig 				 void *result)
836f796f8b3SChuck Lever {
837aabff4ddSPeng Tao 	return decode_attrstat(xdr, result, NULL);
838f796f8b3SChuck Lever }
839f796f8b3SChuck Lever 
840bf269551SChuck Lever static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, struct xdr_stream *xdr,
841fc016483SChristoph Hellwig 				 void *result)
842f796f8b3SChuck Lever {
843bf269551SChuck Lever 	return decode_diropres(xdr, result);
844f796f8b3SChuck Lever }
845f796f8b3SChuck Lever 
8461da177e4SLinus Torvalds /*
847f796f8b3SChuck Lever  * 2.2.6.  readlinkres
848f796f8b3SChuck Lever  *
849f796f8b3SChuck Lever  *	union readlinkres switch (stat status) {
850f796f8b3SChuck Lever  *	case NFS_OK:
851f796f8b3SChuck Lever  *		path data;
852f796f8b3SChuck Lever  *	default:
853f796f8b3SChuck Lever  *		void;
854f796f8b3SChuck Lever  *	};
855f796f8b3SChuck Lever  */
856bf269551SChuck Lever static int nfs2_xdr_dec_readlinkres(struct rpc_rqst *req,
857bf269551SChuck Lever 				    struct xdr_stream *xdr, void *__unused)
858f796f8b3SChuck Lever {
859f796f8b3SChuck Lever 	enum nfs_stat status;
860f796f8b3SChuck Lever 	int error;
861f796f8b3SChuck Lever 
862bf269551SChuck Lever 	error = decode_stat(xdr, &status);
863f796f8b3SChuck Lever 	if (unlikely(error))
864f796f8b3SChuck Lever 		goto out;
865f796f8b3SChuck Lever 	if (status != NFS_OK)
866f796f8b3SChuck Lever 		goto out_default;
867bf269551SChuck Lever 	error = decode_path(xdr);
868f796f8b3SChuck Lever out:
869f796f8b3SChuck Lever 	return error;
870f796f8b3SChuck Lever out_default:
871f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
872f796f8b3SChuck Lever }
873f796f8b3SChuck Lever 
874f796f8b3SChuck Lever /*
875f796f8b3SChuck Lever  * 2.2.7.  readres
876f796f8b3SChuck Lever  *
877f796f8b3SChuck Lever  *	union readres switch (stat status) {
878f796f8b3SChuck Lever  *	case NFS_OK:
879f796f8b3SChuck Lever  *		fattr attributes;
880f796f8b3SChuck Lever  *		nfsdata data;
881f796f8b3SChuck Lever  *	default:
882f796f8b3SChuck Lever  *		void;
883f796f8b3SChuck Lever  *	};
884f796f8b3SChuck Lever  */
885bf269551SChuck Lever static int nfs2_xdr_dec_readres(struct rpc_rqst *req, struct xdr_stream *xdr,
886fc016483SChristoph Hellwig 				void *data)
887f796f8b3SChuck Lever {
888fc016483SChristoph Hellwig 	struct nfs_pgio_res *result = data;
889f796f8b3SChuck Lever 	enum nfs_stat status;
890f796f8b3SChuck Lever 	int error;
891f796f8b3SChuck Lever 
892bf269551SChuck Lever 	error = decode_stat(xdr, &status);
893f796f8b3SChuck Lever 	if (unlikely(error))
894f796f8b3SChuck Lever 		goto out;
895aabff4ddSPeng Tao 	result->op_status = status;
896f796f8b3SChuck Lever 	if (status != NFS_OK)
897f796f8b3SChuck Lever 		goto out_default;
898bf269551SChuck Lever 	error = decode_fattr(xdr, result->fattr);
899f796f8b3SChuck Lever 	if (unlikely(error))
900f796f8b3SChuck Lever 		goto out;
901bf269551SChuck Lever 	error = decode_nfsdata(xdr, result);
902f796f8b3SChuck Lever out:
903f796f8b3SChuck Lever 	return error;
904f796f8b3SChuck Lever out_default:
905f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
906f796f8b3SChuck Lever }
907f796f8b3SChuck Lever 
908bf269551SChuck Lever static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, struct xdr_stream *xdr,
909fc016483SChristoph Hellwig 				 void *data)
910f796f8b3SChuck Lever {
911fc016483SChristoph Hellwig 	struct nfs_pgio_res *result = data;
912fc016483SChristoph Hellwig 
913f796f8b3SChuck Lever 	/* All NFSv2 writes are "file sync" writes */
914f796f8b3SChuck Lever 	result->verf->committed = NFS_FILE_SYNC;
915aabff4ddSPeng Tao 	return decode_attrstat(xdr, result->fattr, &result->op_status);
916f796f8b3SChuck Lever }
917f796f8b3SChuck Lever 
918f796f8b3SChuck Lever /**
919f796f8b3SChuck Lever  * nfs2_decode_dirent - Decode a single NFSv2 directory entry stored in
920f796f8b3SChuck Lever  *                      the local page cache.
921f796f8b3SChuck Lever  * @xdr: XDR stream where entry resides
922f796f8b3SChuck Lever  * @entry: buffer to fill in with entry data
923f796f8b3SChuck Lever  * @plus: boolean indicating whether this should be a readdirplus entry
924f796f8b3SChuck Lever  *
925573c4e1eSChuck Lever  * Returns zero if successful, otherwise a negative errno value is
926573c4e1eSChuck Lever  * returned.
927f796f8b3SChuck Lever  *
928f796f8b3SChuck Lever  * This function is not invoked during READDIR reply decoding, but
929f796f8b3SChuck Lever  * rather whenever an application invokes the getdents(2) system call
930f796f8b3SChuck Lever  * on a directory already in our cache.
931f796f8b3SChuck Lever  *
932f796f8b3SChuck Lever  * 2.2.17.  entry
933f796f8b3SChuck Lever  *
934f796f8b3SChuck Lever  *	struct entry {
935f796f8b3SChuck Lever  *		unsigned	fileid;
936f796f8b3SChuck Lever  *		filename	name;
937f796f8b3SChuck Lever  *		nfscookie	cookie;
938f796f8b3SChuck Lever  *		entry		*nextentry;
939f796f8b3SChuck Lever  *	};
940f796f8b3SChuck Lever  */
941573c4e1eSChuck Lever int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
942573c4e1eSChuck Lever 		       int plus)
943f796f8b3SChuck Lever {
944f796f8b3SChuck Lever 	__be32 *p;
945f796f8b3SChuck Lever 	int error;
946f796f8b3SChuck Lever 
947f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
948f796f8b3SChuck Lever 	if (unlikely(p == NULL))
949f796f8b3SChuck Lever 		goto out_overflow;
950f796f8b3SChuck Lever 	if (*p++ == xdr_zero) {
951f796f8b3SChuck Lever 		p = xdr_inline_decode(xdr, 4);
952f796f8b3SChuck Lever 		if (unlikely(p == NULL))
953f796f8b3SChuck Lever 			goto out_overflow;
954f796f8b3SChuck Lever 		if (*p++ == xdr_zero)
955573c4e1eSChuck Lever 			return -EAGAIN;
956f796f8b3SChuck Lever 		entry->eof = 1;
957573c4e1eSChuck Lever 		return -EBADCOOKIE;
958f796f8b3SChuck Lever 	}
959f796f8b3SChuck Lever 
960f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
961f796f8b3SChuck Lever 	if (unlikely(p == NULL))
962f796f8b3SChuck Lever 		goto out_overflow;
963f796f8b3SChuck Lever 	entry->ino = be32_to_cpup(p);
964f796f8b3SChuck Lever 
965f796f8b3SChuck Lever 	error = decode_filename_inline(xdr, &entry->name, &entry->len);
966f796f8b3SChuck Lever 	if (unlikely(error))
967573c4e1eSChuck Lever 		return error;
968f796f8b3SChuck Lever 
969f796f8b3SChuck Lever 	/*
970f796f8b3SChuck Lever 	 * The type (size and byte order) of nfscookie isn't defined in
971f796f8b3SChuck Lever 	 * RFC 1094.  This implementation assumes that it's an XDR uint32.
972f796f8b3SChuck Lever 	 */
973f796f8b3SChuck Lever 	entry->prev_cookie = entry->cookie;
974f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
975f796f8b3SChuck Lever 	if (unlikely(p == NULL))
976f796f8b3SChuck Lever 		goto out_overflow;
977f796f8b3SChuck Lever 	entry->cookie = be32_to_cpup(p);
978f796f8b3SChuck Lever 
979f796f8b3SChuck Lever 	entry->d_type = DT_UNKNOWN;
980f796f8b3SChuck Lever 
981573c4e1eSChuck Lever 	return 0;
982f796f8b3SChuck Lever 
983f796f8b3SChuck Lever out_overflow:
984f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
985573c4e1eSChuck Lever 	return -EAGAIN;
986f796f8b3SChuck Lever }
987f796f8b3SChuck Lever 
988f796f8b3SChuck Lever /*
989f796f8b3SChuck Lever  * 2.2.17.  readdirres
990f796f8b3SChuck Lever  *
991f796f8b3SChuck Lever  *	union readdirres switch (stat status) {
992f796f8b3SChuck Lever  *	case NFS_OK:
993f796f8b3SChuck Lever  *		struct {
994f796f8b3SChuck Lever  *			entry *entries;
995f796f8b3SChuck Lever  *			bool eof;
996f796f8b3SChuck Lever  *		} readdirok;
997f796f8b3SChuck Lever  *	default:
998f796f8b3SChuck Lever  *		void;
999f796f8b3SChuck Lever  *	};
1000f796f8b3SChuck Lever  *
1001f796f8b3SChuck Lever  * Read the directory contents into the page cache, but don't
1002f796f8b3SChuck Lever  * touch them.  The actual decoding is done by nfs2_decode_dirent()
1003f796f8b3SChuck Lever  * during subsequent nfs_readdir() calls.
1004f796f8b3SChuck Lever  */
1005f796f8b3SChuck Lever static int decode_readdirok(struct xdr_stream *xdr)
1006f796f8b3SChuck Lever {
100764bd577eSTrond Myklebust 	return xdr_read_pages(xdr, xdr->buf->page_len);
1008f796f8b3SChuck Lever }
1009f796f8b3SChuck Lever 
1010bf269551SChuck Lever static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req,
1011bf269551SChuck Lever 				   struct xdr_stream *xdr, void *__unused)
1012f796f8b3SChuck Lever {
1013f796f8b3SChuck Lever 	enum nfs_stat status;
1014f796f8b3SChuck Lever 	int error;
1015f796f8b3SChuck Lever 
1016bf269551SChuck Lever 	error = decode_stat(xdr, &status);
1017f796f8b3SChuck Lever 	if (unlikely(error))
1018f796f8b3SChuck Lever 		goto out;
1019f796f8b3SChuck Lever 	if (status != NFS_OK)
1020f796f8b3SChuck Lever 		goto out_default;
1021bf269551SChuck Lever 	error = decode_readdirok(xdr);
1022f796f8b3SChuck Lever out:
1023f796f8b3SChuck Lever 	return error;
1024f796f8b3SChuck Lever out_default:
1025f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
1026f796f8b3SChuck Lever }
1027f796f8b3SChuck Lever 
10281da177e4SLinus Torvalds /*
1029f796f8b3SChuck Lever  * 2.2.18.  statfsres
1030f796f8b3SChuck Lever  *
1031f796f8b3SChuck Lever  *	union statfsres (stat status) {
1032f796f8b3SChuck Lever  *	case NFS_OK:
1033f796f8b3SChuck Lever  *		struct {
1034f796f8b3SChuck Lever  *			unsigned tsize;
1035f796f8b3SChuck Lever  *			unsigned bsize;
1036f796f8b3SChuck Lever  *			unsigned blocks;
1037f796f8b3SChuck Lever  *			unsigned bfree;
1038f796f8b3SChuck Lever  *			unsigned bavail;
1039f796f8b3SChuck Lever  *		} info;
1040f796f8b3SChuck Lever  *	default:
1041f796f8b3SChuck Lever  *		void;
1042f796f8b3SChuck Lever  *	};
1043f796f8b3SChuck Lever  */
1044f796f8b3SChuck Lever static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result)
1045f796f8b3SChuck Lever {
1046f796f8b3SChuck Lever 	__be32 *p;
1047f796f8b3SChuck Lever 
1048f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS_info_sz << 2);
1049f796f8b3SChuck Lever 	if (unlikely(p == NULL))
1050f796f8b3SChuck Lever 		goto out_overflow;
1051f796f8b3SChuck Lever 	result->tsize  = be32_to_cpup(p++);
1052f796f8b3SChuck Lever 	result->bsize  = be32_to_cpup(p++);
1053f796f8b3SChuck Lever 	result->blocks = be32_to_cpup(p++);
1054f796f8b3SChuck Lever 	result->bfree  = be32_to_cpup(p++);
1055f796f8b3SChuck Lever 	result->bavail = be32_to_cpup(p);
1056f796f8b3SChuck Lever 	return 0;
1057f796f8b3SChuck Lever out_overflow:
1058f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
1059f796f8b3SChuck Lever 	return -EIO;
1060f796f8b3SChuck Lever }
1061f796f8b3SChuck Lever 
1062bf269551SChuck Lever static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr,
1063fc016483SChristoph Hellwig 				  void *result)
1064f796f8b3SChuck Lever {
1065f796f8b3SChuck Lever 	enum nfs_stat status;
1066f796f8b3SChuck Lever 	int error;
1067f796f8b3SChuck Lever 
1068bf269551SChuck Lever 	error = decode_stat(xdr, &status);
1069f796f8b3SChuck Lever 	if (unlikely(error))
1070f796f8b3SChuck Lever 		goto out;
1071f796f8b3SChuck Lever 	if (status != NFS_OK)
1072f796f8b3SChuck Lever 		goto out_default;
1073bf269551SChuck Lever 	error = decode_info(xdr, result);
1074f796f8b3SChuck Lever out:
1075f796f8b3SChuck Lever 	return error;
1076f796f8b3SChuck Lever out_default:
1077f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
1078f796f8b3SChuck Lever }
1079f796f8b3SChuck Lever 
1080f796f8b3SChuck Lever 
1081f796f8b3SChuck Lever /*
10821da177e4SLinus Torvalds  * We need to translate between nfs status return values and
10831da177e4SLinus Torvalds  * the local errno values which may not be the same.
10841da177e4SLinus Torvalds  */
108585828493SChuck Lever static const struct {
10861da177e4SLinus Torvalds 	int stat;
10871da177e4SLinus Torvalds 	int errno;
10881da177e4SLinus Torvalds } nfs_errtbl[] = {
10891da177e4SLinus Torvalds 	{ NFS_OK,		0		},
1090856dff3dSBenny Halevy 	{ NFSERR_PERM,		-EPERM		},
1091856dff3dSBenny Halevy 	{ NFSERR_NOENT,		-ENOENT		},
1092856dff3dSBenny Halevy 	{ NFSERR_IO,		-errno_NFSERR_IO},
1093856dff3dSBenny Halevy 	{ NFSERR_NXIO,		-ENXIO		},
1094856dff3dSBenny Halevy /*	{ NFSERR_EAGAIN,	-EAGAIN		}, */
1095856dff3dSBenny Halevy 	{ NFSERR_ACCES,		-EACCES		},
1096856dff3dSBenny Halevy 	{ NFSERR_EXIST,		-EEXIST		},
1097856dff3dSBenny Halevy 	{ NFSERR_XDEV,		-EXDEV		},
1098856dff3dSBenny Halevy 	{ NFSERR_NODEV,		-ENODEV		},
1099856dff3dSBenny Halevy 	{ NFSERR_NOTDIR,	-ENOTDIR	},
1100856dff3dSBenny Halevy 	{ NFSERR_ISDIR,		-EISDIR		},
1101856dff3dSBenny Halevy 	{ NFSERR_INVAL,		-EINVAL		},
1102856dff3dSBenny Halevy 	{ NFSERR_FBIG,		-EFBIG		},
1103856dff3dSBenny Halevy 	{ NFSERR_NOSPC,		-ENOSPC		},
1104856dff3dSBenny Halevy 	{ NFSERR_ROFS,		-EROFS		},
1105856dff3dSBenny Halevy 	{ NFSERR_MLINK,		-EMLINK		},
1106856dff3dSBenny Halevy 	{ NFSERR_NAMETOOLONG,	-ENAMETOOLONG	},
1107856dff3dSBenny Halevy 	{ NFSERR_NOTEMPTY,	-ENOTEMPTY	},
1108856dff3dSBenny Halevy 	{ NFSERR_DQUOT,		-EDQUOT		},
1109856dff3dSBenny Halevy 	{ NFSERR_STALE,		-ESTALE		},
1110856dff3dSBenny Halevy 	{ NFSERR_REMOTE,	-EREMOTE	},
11111da177e4SLinus Torvalds #ifdef EWFLUSH
1112856dff3dSBenny Halevy 	{ NFSERR_WFLUSH,	-EWFLUSH	},
11131da177e4SLinus Torvalds #endif
1114856dff3dSBenny Halevy 	{ NFSERR_BADHANDLE,	-EBADHANDLE	},
1115856dff3dSBenny Halevy 	{ NFSERR_NOT_SYNC,	-ENOTSYNC	},
1116856dff3dSBenny Halevy 	{ NFSERR_BAD_COOKIE,	-EBADCOOKIE	},
1117856dff3dSBenny Halevy 	{ NFSERR_NOTSUPP,	-ENOTSUPP	},
1118856dff3dSBenny Halevy 	{ NFSERR_TOOSMALL,	-ETOOSMALL	},
1119fdcb4577STrond Myklebust 	{ NFSERR_SERVERFAULT,	-EREMOTEIO	},
1120856dff3dSBenny Halevy 	{ NFSERR_BADTYPE,	-EBADTYPE	},
1121856dff3dSBenny Halevy 	{ NFSERR_JUKEBOX,	-EJUKEBOX	},
1122856dff3dSBenny Halevy 	{ -1,			-EIO		}
11231da177e4SLinus Torvalds };
11241da177e4SLinus Torvalds 
112585828493SChuck Lever /**
112685828493SChuck Lever  * nfs_stat_to_errno - convert an NFS status code to a local errno
112785828493SChuck Lever  * @status: NFS status code to convert
112885828493SChuck Lever  *
112985828493SChuck Lever  * Returns a local errno value, or -EIO if the NFS status code is
113085828493SChuck Lever  * not recognized.  This function is used jointly by NFSv2 and NFSv3.
11311da177e4SLinus Torvalds  */
11325e7e5a0dSBryan Schumaker static int nfs_stat_to_errno(enum nfs_stat status)
11331da177e4SLinus Torvalds {
11341da177e4SLinus Torvalds 	int i;
11351da177e4SLinus Torvalds 
11361da177e4SLinus Torvalds 	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
113785828493SChuck Lever 		if (nfs_errtbl[i].stat == (int)status)
11381da177e4SLinus Torvalds 			return nfs_errtbl[i].errno;
11391da177e4SLinus Torvalds 	}
114085828493SChuck Lever 	dprintk("NFS: Unrecognized nfs status value: %u\n", status);
11411da177e4SLinus Torvalds 	return nfs_errtbl[i].errno;
11421da177e4SLinus Torvalds }
11431da177e4SLinus Torvalds 
11441da177e4SLinus Torvalds #define PROC(proc, argtype, restype, timer)				\
11451da177e4SLinus Torvalds [NFSPROC_##proc] = {							\
11461da177e4SLinus Torvalds 	.p_proc	    =  NFSPROC_##proc,					\
1147fcc85819SChristoph Hellwig 	.p_encode   =  nfs2_xdr_enc_##argtype,				\
1148fc016483SChristoph Hellwig 	.p_decode   =  nfs2_xdr_dec_##restype,				\
11492bea90d4SChuck Lever 	.p_arglen   =  NFS_##argtype##_sz,				\
11502bea90d4SChuck Lever 	.p_replen   =  NFS_##restype##_sz,				\
1151cc0175c1SChuck Lever 	.p_timer    =  timer,						\
1152cc0175c1SChuck Lever 	.p_statidx  =  NFSPROC_##proc,					\
1153cc0175c1SChuck Lever 	.p_name     =  #proc,						\
11541da177e4SLinus Torvalds 	}
11551da177e4SLinus Torvalds struct rpc_procinfo	nfs_procedures[] = {
11561da177e4SLinus Torvalds 	PROC(GETATTR,	fhandle,	attrstat,	1),
11571da177e4SLinus Torvalds 	PROC(SETATTR,	sattrargs,	attrstat,	0),
11581da177e4SLinus Torvalds 	PROC(LOOKUP,	diropargs,	diropres,	2),
11591da177e4SLinus Torvalds 	PROC(READLINK,	readlinkargs,	readlinkres,	3),
11601da177e4SLinus Torvalds 	PROC(READ,	readargs,	readres,	3),
11611da177e4SLinus Torvalds 	PROC(WRITE,	writeargs,	writeres,	4),
11621da177e4SLinus Torvalds 	PROC(CREATE,	createargs,	diropres,	0),
11634fdc17b2STrond Myklebust 	PROC(REMOVE,	removeargs,	stat,		0),
11641da177e4SLinus Torvalds 	PROC(RENAME,	renameargs,	stat,		0),
11651da177e4SLinus Torvalds 	PROC(LINK,	linkargs,	stat,		0),
11661da177e4SLinus Torvalds 	PROC(SYMLINK,	symlinkargs,	stat,		0),
11671da177e4SLinus Torvalds 	PROC(MKDIR,	createargs,	diropres,	0),
11681da177e4SLinus Torvalds 	PROC(RMDIR,	diropargs,	stat,		0),
11691da177e4SLinus Torvalds 	PROC(READDIR,	readdirargs,	readdirres,	3),
11701da177e4SLinus Torvalds 	PROC(STATFS,	fhandle,	statfsres,	0),
11711da177e4SLinus Torvalds };
11721da177e4SLinus Torvalds 
1173c551858aSChristoph Hellwig static unsigned int nfs_version2_counts[ARRAY_SIZE(nfs_procedures)];
1174a613fa16STrond Myklebust const struct rpc_version nfs_version2 = {
11751da177e4SLinus Torvalds 	.number			= 2,
1176e8c96f8cSTobias Klauser 	.nrprocs		= ARRAY_SIZE(nfs_procedures),
1177c551858aSChristoph Hellwig 	.procs			= nfs_procedures,
1178c551858aSChristoph Hellwig 	.counts			= nfs_version2_counts,
11791da177e4SLinus Torvalds };
1180