xref: /openbmc/linux/fs/nfs/nfs2xdr.c (revision f796f8b3)
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 
6425a0866cSChuck Lever 
6525a0866cSChuck Lever /*
6625a0866cSChuck Lever  * While encoding arguments, set up the reply buffer in advance to
6725a0866cSChuck Lever  * receive reply data directly into the page cache.
6825a0866cSChuck Lever  */
6925a0866cSChuck Lever static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages,
7025a0866cSChuck Lever 				 unsigned int base, unsigned int len,
7125a0866cSChuck Lever 				 unsigned int bufsize)
7225a0866cSChuck Lever {
7325a0866cSChuck Lever 	struct rpc_auth	*auth = req->rq_cred->cr_auth;
7425a0866cSChuck Lever 	unsigned int replen;
7525a0866cSChuck Lever 
7625a0866cSChuck Lever 	replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize;
7725a0866cSChuck Lever 	xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len);
7825a0866cSChuck Lever }
7925a0866cSChuck Lever 
80f796f8b3SChuck Lever /*
81f796f8b3SChuck Lever  * Handle decode buffer overflows out-of-line.
82f796f8b3SChuck Lever  */
83f796f8b3SChuck Lever static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
84f796f8b3SChuck Lever {
85f796f8b3SChuck Lever 	dprintk("NFS: %s prematurely hit the end of our receive buffer. "
86f796f8b3SChuck Lever 		"Remaining buffer length is %tu words.\n",
87f796f8b3SChuck Lever 		func, xdr->end - xdr->p);
88f796f8b3SChuck Lever }
89f796f8b3SChuck Lever 
9025a0866cSChuck Lever 
911da177e4SLinus Torvalds /*
921da177e4SLinus Torvalds  * Common NFS XDR functions as inlines
931da177e4SLinus Torvalds  */
949d787a75SAl Viro static inline __be32 *
959d787a75SAl Viro xdr_decode_fhandle(__be32 *p, struct nfs_fh *fhandle)
961da177e4SLinus Torvalds {
971da177e4SLinus Torvalds 	/* NFSv2 handles have a fixed length */
981da177e4SLinus Torvalds 	fhandle->size = NFS2_FHSIZE;
991da177e4SLinus Torvalds 	memcpy(fhandle->data, p, NFS2_FHSIZE);
1001da177e4SLinus Torvalds 	return p + XDR_QUADLEN(NFS2_FHSIZE);
1011da177e4SLinus Torvalds }
1021da177e4SLinus Torvalds 
1039d787a75SAl Viro static inline __be32*
1049d787a75SAl Viro xdr_decode_time(__be32 *p, struct timespec *timep)
1051da177e4SLinus Torvalds {
1061da177e4SLinus Torvalds 	timep->tv_sec = ntohl(*p++);
1071da177e4SLinus Torvalds 	/* Convert microseconds into nanoseconds */
1081da177e4SLinus Torvalds 	timep->tv_nsec = ntohl(*p++) * 1000;
1091da177e4SLinus Torvalds 	return p;
1101da177e4SLinus Torvalds }
1111da177e4SLinus Torvalds 
1129d787a75SAl Viro static __be32 *
1139d787a75SAl Viro xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
1141da177e4SLinus Torvalds {
115bca79478STrond Myklebust 	u32 rdev, type;
116bca79478STrond Myklebust 	type = ntohl(*p++);
1171da177e4SLinus Torvalds 	fattr->mode = ntohl(*p++);
1181da177e4SLinus Torvalds 	fattr->nlink = ntohl(*p++);
1191da177e4SLinus Torvalds 	fattr->uid = ntohl(*p++);
1201da177e4SLinus Torvalds 	fattr->gid = ntohl(*p++);
1211da177e4SLinus Torvalds 	fattr->size = ntohl(*p++);
1221da177e4SLinus Torvalds 	fattr->du.nfs2.blocksize = ntohl(*p++);
1231da177e4SLinus Torvalds 	rdev = ntohl(*p++);
1241da177e4SLinus Torvalds 	fattr->du.nfs2.blocks = ntohl(*p++);
1258b4bdcf8STrond Myklebust 	fattr->fsid.major = ntohl(*p++);
1268b4bdcf8STrond Myklebust 	fattr->fsid.minor = 0;
1271da177e4SLinus Torvalds 	fattr->fileid = ntohl(*p++);
1281da177e4SLinus Torvalds 	p = xdr_decode_time(p, &fattr->atime);
1291da177e4SLinus Torvalds 	p = xdr_decode_time(p, &fattr->mtime);
1301da177e4SLinus Torvalds 	p = xdr_decode_time(p, &fattr->ctime);
1319e6e70f8STrond Myklebust 	fattr->valid |= NFS_ATTR_FATTR_V2;
1321da177e4SLinus Torvalds 	fattr->rdev = new_decode_dev(rdev);
133bca79478STrond Myklebust 	if (type == NFCHR && rdev == NFS2_FIFO_DEV) {
1341da177e4SLinus Torvalds 		fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
1351da177e4SLinus Torvalds 		fattr->rdev = 0;
1361da177e4SLinus Torvalds 	}
1371da177e4SLinus Torvalds 	return p;
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds /*
14125a0866cSChuck Lever  * Encode/decode NFSv2 basic data types
14225a0866cSChuck Lever  *
14325a0866cSChuck Lever  * Basic NFSv2 data types are defined in section 2.3 of RFC 1094:
14425a0866cSChuck Lever  * "NFS: Network File System Protocol Specification".
14525a0866cSChuck Lever  *
14625a0866cSChuck Lever  * Not all basic data types have their own encoding and decoding
14725a0866cSChuck Lever  * functions.  For run-time efficiency, some data types are encoded
14825a0866cSChuck Lever  * or decoded inline.
14925a0866cSChuck Lever  */
15025a0866cSChuck Lever 
15125a0866cSChuck Lever /*
152f796f8b3SChuck Lever  *	typedef opaque	nfsdata<>;
153f796f8b3SChuck Lever  */
154f796f8b3SChuck Lever static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result)
155f796f8b3SChuck Lever {
156f796f8b3SChuck Lever 	u32 recvd, count;
157f796f8b3SChuck Lever 	size_t hdrlen;
158f796f8b3SChuck Lever 	__be32 *p;
159f796f8b3SChuck Lever 
160f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
161f796f8b3SChuck Lever 	if (unlikely(p == NULL))
162f796f8b3SChuck Lever 		goto out_overflow;
163f796f8b3SChuck Lever 	count = be32_to_cpup(p);
164f796f8b3SChuck Lever 	hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
165f796f8b3SChuck Lever 	recvd = xdr->buf->len - hdrlen;
166f796f8b3SChuck Lever 	if (unlikely(count > recvd))
167f796f8b3SChuck Lever 		goto out_cheating;
168f796f8b3SChuck Lever out:
169f796f8b3SChuck Lever 	xdr_read_pages(xdr, count);
170f796f8b3SChuck Lever 	result->eof = 0;	/* NFSv2 does not pass EOF flag on the wire. */
171f796f8b3SChuck Lever 	result->count = count;
172f796f8b3SChuck Lever 	return count;
173f796f8b3SChuck Lever out_cheating:
174f796f8b3SChuck Lever 	dprintk("NFS: server cheating in read result: "
175f796f8b3SChuck Lever 		"count %u > recvd %u\n", count, recvd);
176f796f8b3SChuck Lever 	count = recvd;
177f796f8b3SChuck Lever 	goto out;
178f796f8b3SChuck Lever out_overflow:
179f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
180f796f8b3SChuck Lever 	return -EIO;
181f796f8b3SChuck Lever }
182f796f8b3SChuck Lever 
183f796f8b3SChuck Lever /*
184f796f8b3SChuck Lever  *	enum stat {
185f796f8b3SChuck Lever  *		NFS_OK = 0,
186f796f8b3SChuck Lever  *		NFSERR_PERM = 1,
187f796f8b3SChuck Lever  *		NFSERR_NOENT = 2,
188f796f8b3SChuck Lever  *		NFSERR_IO = 5,
189f796f8b3SChuck Lever  *		NFSERR_NXIO = 6,
190f796f8b3SChuck Lever  *		NFSERR_ACCES = 13,
191f796f8b3SChuck Lever  *		NFSERR_EXIST = 17,
192f796f8b3SChuck Lever  *		NFSERR_NODEV = 19,
193f796f8b3SChuck Lever  *		NFSERR_NOTDIR = 20,
194f796f8b3SChuck Lever  *		NFSERR_ISDIR = 21,
195f796f8b3SChuck Lever  *		NFSERR_FBIG = 27,
196f796f8b3SChuck Lever  *		NFSERR_NOSPC = 28,
197f796f8b3SChuck Lever  *		NFSERR_ROFS = 30,
198f796f8b3SChuck Lever  *		NFSERR_NAMETOOLONG = 63,
199f796f8b3SChuck Lever  *		NFSERR_NOTEMPTY = 66,
200f796f8b3SChuck Lever  *		NFSERR_DQUOT = 69,
201f796f8b3SChuck Lever  *		NFSERR_STALE = 70,
202f796f8b3SChuck Lever  *		NFSERR_WFLUSH = 99
203f796f8b3SChuck Lever  *	};
204f796f8b3SChuck Lever  */
205f796f8b3SChuck Lever static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status)
206f796f8b3SChuck Lever {
207f796f8b3SChuck Lever 	__be32 *p;
208f796f8b3SChuck Lever 
209f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
210f796f8b3SChuck Lever 	if (unlikely(p == NULL))
211f796f8b3SChuck Lever 		goto out_overflow;
212f796f8b3SChuck Lever 	*status = be32_to_cpup(p);
213f796f8b3SChuck Lever 	return 0;
214f796f8b3SChuck Lever out_overflow:
215f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
216f796f8b3SChuck Lever 	return -EIO;
217f796f8b3SChuck Lever }
218f796f8b3SChuck Lever 
219f796f8b3SChuck Lever /*
22025a0866cSChuck Lever  * 2.3.3.  fhandle
22125a0866cSChuck Lever  *
22225a0866cSChuck Lever  *	typedef opaque fhandle[FHSIZE];
22325a0866cSChuck Lever  */
22425a0866cSChuck Lever static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh)
22525a0866cSChuck Lever {
22625a0866cSChuck Lever 	__be32 *p;
22725a0866cSChuck Lever 
22825a0866cSChuck Lever 	BUG_ON(fh->size != NFS2_FHSIZE);
22925a0866cSChuck Lever 	p = xdr_reserve_space(xdr, NFS2_FHSIZE);
23025a0866cSChuck Lever 	memcpy(p, fh->data, NFS2_FHSIZE);
23125a0866cSChuck Lever }
23225a0866cSChuck Lever 
233f796f8b3SChuck Lever static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh)
234f796f8b3SChuck Lever {
235f796f8b3SChuck Lever 	__be32 *p;
236f796f8b3SChuck Lever 
237f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS2_FHSIZE);
238f796f8b3SChuck Lever 	if (unlikely(p == NULL))
239f796f8b3SChuck Lever 		goto out_overflow;
240f796f8b3SChuck Lever 	fh->size = NFS2_FHSIZE;
241f796f8b3SChuck Lever 	memcpy(fh->data, p, NFS2_FHSIZE);
242f796f8b3SChuck Lever 	return 0;
243f796f8b3SChuck Lever out_overflow:
244f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
245f796f8b3SChuck Lever 	return -EIO;
246f796f8b3SChuck Lever }
247f796f8b3SChuck Lever 
24825a0866cSChuck Lever /*
249282ac2a5SChuck Lever  * 2.3.4.  timeval
250282ac2a5SChuck Lever  *
251282ac2a5SChuck Lever  *	struct timeval {
252282ac2a5SChuck Lever  *		unsigned int seconds;
253282ac2a5SChuck Lever  *		unsigned int useconds;
254282ac2a5SChuck Lever  *	};
255282ac2a5SChuck Lever  */
256282ac2a5SChuck Lever static __be32 *xdr_encode_time(__be32 *p, const struct timespec *timep)
257282ac2a5SChuck Lever {
258282ac2a5SChuck Lever 	*p++ = cpu_to_be32(timep->tv_sec);
259282ac2a5SChuck Lever 	if (timep->tv_nsec != 0)
260282ac2a5SChuck Lever 		*p++ = cpu_to_be32(timep->tv_nsec / NSEC_PER_USEC);
261282ac2a5SChuck Lever 	else
262282ac2a5SChuck Lever 		*p++ = cpu_to_be32(0);
263282ac2a5SChuck Lever 	return p;
264282ac2a5SChuck Lever }
265282ac2a5SChuck Lever 
266282ac2a5SChuck Lever /*
267282ac2a5SChuck Lever  * Passing the invalid value useconds=1000000 is a Sun convention for
268282ac2a5SChuck Lever  * "set to current server time".  It's needed to make permissions checks
269282ac2a5SChuck Lever  * for the "touch" program across v2 mounts to Solaris and Irix servers
270282ac2a5SChuck Lever  * work correctly.  See description of sattr in section 6.1 of "NFS
271282ac2a5SChuck Lever  * Illustrated" by Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5.
272282ac2a5SChuck Lever  */
273282ac2a5SChuck Lever static __be32 *xdr_encode_current_server_time(__be32 *p,
274282ac2a5SChuck Lever 					      const struct timespec *timep)
275282ac2a5SChuck Lever {
276282ac2a5SChuck Lever 	*p++ = cpu_to_be32(timep->tv_sec);
277282ac2a5SChuck Lever 	*p++ = cpu_to_be32(1000000);
278282ac2a5SChuck Lever 	return p;
279282ac2a5SChuck Lever }
280282ac2a5SChuck Lever 
281282ac2a5SChuck Lever /*
282f796f8b3SChuck Lever  * 2.3.5.  fattr
283f796f8b3SChuck Lever  *
284f796f8b3SChuck Lever  *	struct fattr {
285f796f8b3SChuck Lever  *		ftype		type;
286f796f8b3SChuck Lever  *		unsigned int	mode;
287f796f8b3SChuck Lever  *		unsigned int	nlink;
288f796f8b3SChuck Lever  *		unsigned int	uid;
289f796f8b3SChuck Lever  *		unsigned int	gid;
290f796f8b3SChuck Lever  *		unsigned int	size;
291f796f8b3SChuck Lever  *		unsigned int	blocksize;
292f796f8b3SChuck Lever  *		unsigned int	rdev;
293f796f8b3SChuck Lever  *		unsigned int	blocks;
294f796f8b3SChuck Lever  *		unsigned int	fsid;
295f796f8b3SChuck Lever  *		unsigned int	fileid;
296f796f8b3SChuck Lever  *		timeval		atime;
297f796f8b3SChuck Lever  *		timeval		mtime;
298f796f8b3SChuck Lever  *		timeval		ctime;
299f796f8b3SChuck Lever  *	};
300f796f8b3SChuck Lever  *
301f796f8b3SChuck Lever  */
302f796f8b3SChuck Lever static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
303f796f8b3SChuck Lever {
304f796f8b3SChuck Lever 	__be32 *p;
305f796f8b3SChuck Lever 
306f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS_fattr_sz << 2);
307f796f8b3SChuck Lever 	if (unlikely(p == NULL))
308f796f8b3SChuck Lever 		goto out_overflow;
309f796f8b3SChuck Lever 	xdr_decode_fattr(p, fattr);
310f796f8b3SChuck Lever 	return 0;
311f796f8b3SChuck Lever out_overflow:
312f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
313f796f8b3SChuck Lever 	return -EIO;
314f796f8b3SChuck Lever }
315f796f8b3SChuck Lever 
316f796f8b3SChuck Lever /*
31725a0866cSChuck Lever  * 2.3.6.  sattr
31825a0866cSChuck Lever  *
31925a0866cSChuck Lever  *	struct sattr {
32025a0866cSChuck Lever  *		unsigned int	mode;
32125a0866cSChuck Lever  *		unsigned int	uid;
32225a0866cSChuck Lever  *		unsigned int	gid;
32325a0866cSChuck Lever  *		unsigned int	size;
32425a0866cSChuck Lever  *		timeval		atime;
32525a0866cSChuck Lever  *		timeval		mtime;
32625a0866cSChuck Lever  *	};
32725a0866cSChuck Lever  */
32825a0866cSChuck Lever 
32925a0866cSChuck Lever #define NFS2_SATTR_NOT_SET	(0xffffffff)
33025a0866cSChuck Lever 
33125a0866cSChuck Lever static __be32 *xdr_time_not_set(__be32 *p)
33225a0866cSChuck Lever {
33325a0866cSChuck Lever 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
33425a0866cSChuck Lever 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
33525a0866cSChuck Lever 	return p;
33625a0866cSChuck Lever }
33725a0866cSChuck Lever 
33825a0866cSChuck Lever static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr)
33925a0866cSChuck Lever {
34025a0866cSChuck Lever 	__be32 *p;
34125a0866cSChuck Lever 
34225a0866cSChuck Lever 	p = xdr_reserve_space(xdr, NFS_sattr_sz << 2);
34325a0866cSChuck Lever 
34425a0866cSChuck Lever 	if (attr->ia_valid & ATTR_MODE)
34525a0866cSChuck Lever 		*p++ = cpu_to_be32(attr->ia_mode);
34625a0866cSChuck Lever 	else
34725a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
34825a0866cSChuck Lever 	if (attr->ia_valid & ATTR_UID)
34925a0866cSChuck Lever 		*p++ = cpu_to_be32(attr->ia_uid);
35025a0866cSChuck Lever 	else
35125a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
35225a0866cSChuck Lever 	if (attr->ia_valid & ATTR_GID)
35325a0866cSChuck Lever 		*p++ = cpu_to_be32(attr->ia_gid);
35425a0866cSChuck Lever 	else
35525a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
35625a0866cSChuck Lever 	if (attr->ia_valid & ATTR_SIZE)
35725a0866cSChuck Lever 		*p++ = cpu_to_be32((u32)attr->ia_size);
35825a0866cSChuck Lever 	else
35925a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
36025a0866cSChuck Lever 
36125a0866cSChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET)
36225a0866cSChuck Lever 		p = xdr_encode_time(p, &attr->ia_atime);
36325a0866cSChuck Lever 	else if (attr->ia_valid & ATTR_ATIME)
36425a0866cSChuck Lever 		p = xdr_encode_current_server_time(p, &attr->ia_atime);
36525a0866cSChuck Lever 	else
36625a0866cSChuck Lever 		p = xdr_time_not_set(p);
36725a0866cSChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET)
36825a0866cSChuck Lever 		xdr_encode_time(p, &attr->ia_mtime);
36925a0866cSChuck Lever 	else if (attr->ia_valid & ATTR_MTIME)
37025a0866cSChuck Lever 		xdr_encode_current_server_time(p, &attr->ia_mtime);
37125a0866cSChuck Lever 	else
37225a0866cSChuck Lever 		xdr_time_not_set(p);
37325a0866cSChuck Lever }
37425a0866cSChuck Lever 
37525a0866cSChuck Lever /*
37625a0866cSChuck Lever  * 2.3.7.  filename
37725a0866cSChuck Lever  *
37825a0866cSChuck Lever  *	typedef string filename<MAXNAMLEN>;
37925a0866cSChuck Lever  */
38025a0866cSChuck Lever static void encode_filename(struct xdr_stream *xdr,
38125a0866cSChuck Lever 			    const char *name, u32 length)
38225a0866cSChuck Lever {
38325a0866cSChuck Lever 	__be32 *p;
38425a0866cSChuck Lever 
38525a0866cSChuck Lever 	BUG_ON(length > NFS2_MAXNAMLEN);
38625a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + length);
38725a0866cSChuck Lever 	xdr_encode_opaque(p, name, length);
38825a0866cSChuck Lever }
38925a0866cSChuck Lever 
390f796f8b3SChuck Lever static int decode_filename_inline(struct xdr_stream *xdr,
391f796f8b3SChuck Lever 				  const char **name, u32 *length)
392f796f8b3SChuck Lever {
393f796f8b3SChuck Lever 	__be32 *p;
394f796f8b3SChuck Lever 	u32 count;
395f796f8b3SChuck Lever 
396f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
397f796f8b3SChuck Lever 	if (unlikely(p == NULL))
398f796f8b3SChuck Lever 		goto out_overflow;
399f796f8b3SChuck Lever 	count = be32_to_cpup(p);
400f796f8b3SChuck Lever 	if (count > NFS3_MAXNAMLEN)
401f796f8b3SChuck Lever 		goto out_nametoolong;
402f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, count);
403f796f8b3SChuck Lever 	if (unlikely(p == NULL))
404f796f8b3SChuck Lever 		goto out_overflow;
405f796f8b3SChuck Lever 	*name = (const char *)p;
406f796f8b3SChuck Lever 	*length = count;
407f796f8b3SChuck Lever 	return 0;
408f796f8b3SChuck Lever out_nametoolong:
409f796f8b3SChuck Lever 	dprintk("NFS: returned filename too long: %u\n", count);
410f796f8b3SChuck Lever 	return -ENAMETOOLONG;
411f796f8b3SChuck Lever out_overflow:
412f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
413f796f8b3SChuck Lever 	return -EIO;
414f796f8b3SChuck Lever }
415f796f8b3SChuck Lever 
41625a0866cSChuck Lever /*
41725a0866cSChuck Lever  * 2.3.8.  path
41825a0866cSChuck Lever  *
41925a0866cSChuck Lever  *	typedef string path<MAXPATHLEN>;
42025a0866cSChuck Lever  */
42125a0866cSChuck Lever static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length)
42225a0866cSChuck Lever {
42325a0866cSChuck Lever 	__be32 *p;
42425a0866cSChuck Lever 
42525a0866cSChuck Lever 	BUG_ON(length > NFS2_MAXPATHLEN);
42625a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4);
42725a0866cSChuck Lever 	*p = cpu_to_be32(length);
42825a0866cSChuck Lever 	xdr_write_pages(xdr, pages, 0, length);
42925a0866cSChuck Lever }
43025a0866cSChuck Lever 
431f796f8b3SChuck Lever static int decode_path(struct xdr_stream *xdr)
432f796f8b3SChuck Lever {
433f796f8b3SChuck Lever 	u32 length, recvd;
434f796f8b3SChuck Lever 	size_t hdrlen;
435f796f8b3SChuck Lever 	__be32 *p;
436f796f8b3SChuck Lever 
437f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
438f796f8b3SChuck Lever 	if (unlikely(p == NULL))
439f796f8b3SChuck Lever 		goto out_overflow;
440f796f8b3SChuck Lever 	length = be32_to_cpup(p);
441f796f8b3SChuck Lever 	if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN))
442f796f8b3SChuck Lever 		goto out_size;
443f796f8b3SChuck Lever 	hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
444f796f8b3SChuck Lever 	recvd = xdr->buf->len - hdrlen;
445f796f8b3SChuck Lever 	if (unlikely(length > recvd))
446f796f8b3SChuck Lever 		goto out_cheating;
447f796f8b3SChuck Lever 
448f796f8b3SChuck Lever 	xdr_read_pages(xdr, length);
449f796f8b3SChuck Lever 	xdr_terminate_string(xdr->buf, length);
450f796f8b3SChuck Lever 	return 0;
451f796f8b3SChuck Lever out_size:
452f796f8b3SChuck Lever 	dprintk("NFS: returned pathname too long: %u\n", length);
453f796f8b3SChuck Lever 	return -ENAMETOOLONG;
454f796f8b3SChuck Lever out_cheating:
455f796f8b3SChuck Lever 	dprintk("NFS: server cheating in pathname result: "
456f796f8b3SChuck Lever 		"length %u > received %u\n", length, recvd);
457f796f8b3SChuck Lever 	return -EIO;
458f796f8b3SChuck Lever out_overflow:
459f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
460f796f8b3SChuck Lever 	return -EIO;
461f796f8b3SChuck Lever }
462f796f8b3SChuck Lever 
463f796f8b3SChuck Lever /*
464f796f8b3SChuck Lever  * 2.3.9.  attrstat
465f796f8b3SChuck Lever  *
466f796f8b3SChuck Lever  *	union attrstat switch (stat status) {
467f796f8b3SChuck Lever  *	case NFS_OK:
468f796f8b3SChuck Lever  *		fattr attributes;
469f796f8b3SChuck Lever  *	default:
470f796f8b3SChuck Lever  *		void;
471f796f8b3SChuck Lever  *	};
472f796f8b3SChuck Lever  */
473f796f8b3SChuck Lever static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result)
474f796f8b3SChuck Lever {
475f796f8b3SChuck Lever 	enum nfs_stat status;
476f796f8b3SChuck Lever 	int error;
477f796f8b3SChuck Lever 
478f796f8b3SChuck Lever 	error = decode_stat(xdr, &status);
479f796f8b3SChuck Lever 	if (unlikely(error))
480f796f8b3SChuck Lever 		goto out;
481f796f8b3SChuck Lever 	if (status != NFS_OK)
482f796f8b3SChuck Lever 		goto out_default;
483f796f8b3SChuck Lever 	error = decode_fattr(xdr, result);
484f796f8b3SChuck Lever out:
485f796f8b3SChuck Lever 	return error;
486f796f8b3SChuck Lever out_default:
487f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
488f796f8b3SChuck Lever }
489f796f8b3SChuck Lever 
49025a0866cSChuck Lever /*
49125a0866cSChuck Lever  * 2.3.10.  diropargs
49225a0866cSChuck Lever  *
49325a0866cSChuck Lever  *	struct diropargs {
49425a0866cSChuck Lever  *		fhandle  dir;
49525a0866cSChuck Lever  *		filename name;
49625a0866cSChuck Lever  *	};
49725a0866cSChuck Lever  */
49825a0866cSChuck Lever static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh,
49925a0866cSChuck Lever 			     const char *name, u32 length)
50025a0866cSChuck Lever {
50125a0866cSChuck Lever 	encode_fhandle(xdr, fh);
50225a0866cSChuck Lever 	encode_filename(xdr, name, length);
50325a0866cSChuck Lever }
50425a0866cSChuck Lever 
505f796f8b3SChuck Lever /*
506f796f8b3SChuck Lever  * 2.3.11.  diropres
507f796f8b3SChuck Lever  *
508f796f8b3SChuck Lever  *	union diropres switch (stat status) {
509f796f8b3SChuck Lever  *	case NFS_OK:
510f796f8b3SChuck Lever  *		struct {
511f796f8b3SChuck Lever  *			fhandle file;
512f796f8b3SChuck Lever  *			fattr   attributes;
513f796f8b3SChuck Lever  *		} diropok;
514f796f8b3SChuck Lever  *	default:
515f796f8b3SChuck Lever  *		void;
516f796f8b3SChuck Lever  *	};
517f796f8b3SChuck Lever  */
518f796f8b3SChuck Lever static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result)
519f796f8b3SChuck Lever {
520f796f8b3SChuck Lever 	int error;
521f796f8b3SChuck Lever 
522f796f8b3SChuck Lever 	error = decode_fhandle(xdr, result->fh);
523f796f8b3SChuck Lever 	if (unlikely(error))
524f796f8b3SChuck Lever 		goto out;
525f796f8b3SChuck Lever 	error = decode_fattr(xdr, result->fattr);
526f796f8b3SChuck Lever out:
527f796f8b3SChuck Lever 	return error;
528f796f8b3SChuck Lever }
529f796f8b3SChuck Lever 
530f796f8b3SChuck Lever static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result)
531f796f8b3SChuck Lever {
532f796f8b3SChuck Lever 	enum nfs_stat status;
533f796f8b3SChuck Lever 	int error;
534f796f8b3SChuck Lever 
535f796f8b3SChuck Lever 	error = decode_stat(xdr, &status);
536f796f8b3SChuck Lever 	if (unlikely(error))
537f796f8b3SChuck Lever 		goto out;
538f796f8b3SChuck Lever 	if (status != NFS_OK)
539f796f8b3SChuck Lever 		goto out_default;
540f796f8b3SChuck Lever 	error = decode_diropok(xdr, result);
541f796f8b3SChuck Lever out:
542f796f8b3SChuck Lever 	return error;
543f796f8b3SChuck Lever out_default:
544f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
545f796f8b3SChuck Lever }
546f796f8b3SChuck Lever 
54725a0866cSChuck Lever 
54825a0866cSChuck Lever /*
5492d70f533SChuck Lever  * NFSv2 XDR encode functions
5502d70f533SChuck Lever  *
5512d70f533SChuck Lever  * NFSv2 argument types are defined in section 2.2 of RFC 1094:
5522d70f533SChuck Lever  * "NFS: Network File System Protocol Specification".
5531da177e4SLinus Torvalds  */
5541da177e4SLinus Torvalds 
55525a0866cSChuck Lever static int nfs2_xdr_enc_fhandle(struct rpc_rqst *req, __be32 *p,
55625a0866cSChuck Lever 				const struct nfs_fh *fh)
55725a0866cSChuck Lever {
55825a0866cSChuck Lever 	struct xdr_stream xdr;
55925a0866cSChuck Lever 
56025a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
56125a0866cSChuck Lever 	encode_fhandle(&xdr, fh);
56225a0866cSChuck Lever 	return 0;
56325a0866cSChuck Lever }
56425a0866cSChuck Lever 
5651da177e4SLinus Torvalds /*
56625a0866cSChuck Lever  * 2.2.3.  sattrargs
56725a0866cSChuck Lever  *
56825a0866cSChuck Lever  *	struct sattrargs {
56925a0866cSChuck Lever  *		fhandle file;
57025a0866cSChuck Lever  *		sattr attributes;
57125a0866cSChuck Lever  *	};
57225a0866cSChuck Lever  */
57325a0866cSChuck Lever static int nfs2_xdr_enc_sattrargs(struct rpc_rqst *req, __be32 *p,
57425a0866cSChuck Lever 				  const struct nfs_sattrargs *args)
57525a0866cSChuck Lever {
57625a0866cSChuck Lever 	struct xdr_stream xdr;
57725a0866cSChuck Lever 
57825a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
57925a0866cSChuck Lever 	encode_fhandle(&xdr, args->fh);
58025a0866cSChuck Lever 	encode_sattr(&xdr, args->sattr);
58125a0866cSChuck Lever 	return 0;
58225a0866cSChuck Lever }
58325a0866cSChuck Lever 
58425a0866cSChuck Lever static int nfs2_xdr_enc_diropargs(struct rpc_rqst *req, __be32 *p,
58525a0866cSChuck Lever 				  const struct nfs_diropargs *args)
58625a0866cSChuck Lever {
58725a0866cSChuck Lever 	struct xdr_stream xdr;
58825a0866cSChuck Lever 
58925a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
59025a0866cSChuck Lever 	encode_diropargs(&xdr, args->fh, args->name, args->len);
59125a0866cSChuck Lever 	return 0;
59225a0866cSChuck Lever }
59325a0866cSChuck Lever 
59425a0866cSChuck Lever static int nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req, __be32 *p,
59525a0866cSChuck Lever 				     const struct nfs_readlinkargs *args)
59625a0866cSChuck Lever {
59725a0866cSChuck Lever 	struct xdr_stream xdr;
59825a0866cSChuck Lever 
59925a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
60025a0866cSChuck Lever 	encode_fhandle(&xdr, args->fh);
60125a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
60225a0866cSChuck Lever 					args->pglen, NFS_readlinkres_sz);
60325a0866cSChuck Lever 	return 0;
60425a0866cSChuck Lever }
60525a0866cSChuck Lever 
6064fdc17b2STrond Myklebust /*
60725a0866cSChuck Lever  * 2.2.7.  readargs
60825a0866cSChuck Lever  *
60925a0866cSChuck Lever  *	struct readargs {
61025a0866cSChuck Lever  *		fhandle file;
61125a0866cSChuck Lever  *		unsigned offset;
61225a0866cSChuck Lever  *		unsigned count;
61325a0866cSChuck Lever  *		unsigned totalcount;
61425a0866cSChuck Lever  *	};
61525a0866cSChuck Lever  */
61625a0866cSChuck Lever static void encode_readargs(struct xdr_stream *xdr,
61725a0866cSChuck Lever 			    const struct nfs_readargs *args)
61825a0866cSChuck Lever {
61925a0866cSChuck Lever 	u32 offset = args->offset;
62025a0866cSChuck Lever 	u32 count = args->count;
62125a0866cSChuck Lever 	__be32 *p;
62225a0866cSChuck Lever 
62325a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
62425a0866cSChuck Lever 
62525a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4 + 4);
62625a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
62725a0866cSChuck Lever 	*p++ = cpu_to_be32(count);
62825a0866cSChuck Lever 	*p = cpu_to_be32(count);
62925a0866cSChuck Lever }
63025a0866cSChuck Lever 
63125a0866cSChuck Lever static int nfs2_xdr_enc_readargs(struct rpc_rqst *req, __be32 *p,
63225a0866cSChuck Lever 				 const struct nfs_readargs *args)
63325a0866cSChuck Lever {
63425a0866cSChuck Lever 	struct xdr_stream xdr;
63525a0866cSChuck Lever 
63625a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
63725a0866cSChuck Lever 	encode_readargs(&xdr, args);
63825a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
63925a0866cSChuck Lever 					args->count, NFS_readres_sz);
64025a0866cSChuck Lever 	req->rq_rcv_buf.flags |= XDRBUF_READ;
64125a0866cSChuck Lever 	return 0;
64225a0866cSChuck Lever }
64325a0866cSChuck Lever 
64425a0866cSChuck Lever /*
6451da177e4SLinus Torvalds  * Decode READ reply
6461da177e4SLinus Torvalds  */
6471da177e4SLinus Torvalds static int
6489d787a75SAl Viro nfs_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
6491da177e4SLinus Torvalds {
6501da177e4SLinus Torvalds 	struct kvec *iov = req->rq_rcv_buf.head;
6516232dbbcSChuck Lever 	size_t hdrlen;
6526232dbbcSChuck Lever 	u32 count, recvd;
6536232dbbcSChuck Lever 	int status;
6541da177e4SLinus Torvalds 
6551da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
656856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
6571da177e4SLinus Torvalds 	p = xdr_decode_fattr(p, res->fattr);
6581da177e4SLinus Torvalds 
6591da177e4SLinus Torvalds 	count = ntohl(*p++);
6601da177e4SLinus Torvalds 	res->eof = 0;
6611da177e4SLinus Torvalds 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
6621da177e4SLinus Torvalds 	if (iov->iov_len < hdrlen) {
663fe82a183SChuck Lever 		dprintk("NFS: READ reply header overflowed:"
6646232dbbcSChuck Lever 				"length %Zu > %Zu\n", hdrlen, iov->iov_len);
6651da177e4SLinus Torvalds 		return -errno_NFSERR_IO;
6661da177e4SLinus Torvalds 	} else if (iov->iov_len != hdrlen) {
6671da177e4SLinus Torvalds 		dprintk("NFS: READ header is short. iovec will be shifted.\n");
6681da177e4SLinus Torvalds 		xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
6691da177e4SLinus Torvalds 	}
6701da177e4SLinus Torvalds 
6711da177e4SLinus Torvalds 	recvd = req->rq_rcv_buf.len - hdrlen;
6721da177e4SLinus Torvalds 	if (count > recvd) {
673fe82a183SChuck Lever 		dprintk("NFS: server cheating in read reply: "
6746232dbbcSChuck Lever 			"count %u > recvd %u\n", count, recvd);
6751da177e4SLinus Torvalds 		count = recvd;
6761da177e4SLinus Torvalds 	}
6771da177e4SLinus Torvalds 
6786232dbbcSChuck Lever 	dprintk("RPC:      readres OK count %u\n", count);
6791da177e4SLinus Torvalds 	if (count < res->count)
6801da177e4SLinus Torvalds 		res->count = count;
6811da177e4SLinus Torvalds 
6821da177e4SLinus Torvalds 	return count;
6831da177e4SLinus Torvalds }
6841da177e4SLinus Torvalds 
6851da177e4SLinus Torvalds 
6861da177e4SLinus Torvalds /*
68725a0866cSChuck Lever  * 2.2.9.  writeargs
68825a0866cSChuck Lever  *
68925a0866cSChuck Lever  *	struct writeargs {
69025a0866cSChuck Lever  *		fhandle file;
69125a0866cSChuck Lever  *		unsigned beginoffset;
69225a0866cSChuck Lever  *		unsigned offset;
69325a0866cSChuck Lever  *		unsigned totalcount;
69425a0866cSChuck Lever  *		nfsdata data;
69525a0866cSChuck Lever  *	};
69625a0866cSChuck Lever  */
69725a0866cSChuck Lever static void encode_writeargs(struct xdr_stream *xdr,
69825a0866cSChuck Lever 			     const struct nfs_writeargs *args)
69925a0866cSChuck Lever {
70025a0866cSChuck Lever 	u32 offset = args->offset;
70125a0866cSChuck Lever 	u32 count = args->count;
70225a0866cSChuck Lever 	__be32 *p;
70325a0866cSChuck Lever 
70425a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
70525a0866cSChuck Lever 
70625a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4);
70725a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
70825a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
70925a0866cSChuck Lever 	*p++ = cpu_to_be32(count);
71025a0866cSChuck Lever 
71125a0866cSChuck Lever 	/* nfsdata */
71225a0866cSChuck Lever 	*p = cpu_to_be32(count);
71325a0866cSChuck Lever 	xdr_write_pages(xdr, args->pages, args->pgbase, count);
71425a0866cSChuck Lever }
71525a0866cSChuck Lever 
71625a0866cSChuck Lever static int nfs2_xdr_enc_writeargs(struct rpc_rqst *req, __be32 *p,
71725a0866cSChuck Lever 				  const struct nfs_writeargs *args)
71825a0866cSChuck Lever {
71925a0866cSChuck Lever 	struct xdr_stream xdr;
72025a0866cSChuck Lever 
72125a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
72225a0866cSChuck Lever 	encode_writeargs(&xdr, args);
72325a0866cSChuck Lever 	xdr.buf->flags |= XDRBUF_WRITE;
72425a0866cSChuck Lever 	return 0;
72525a0866cSChuck Lever }
72625a0866cSChuck Lever 
72725a0866cSChuck Lever /*
72825a0866cSChuck Lever  * 2.2.10.  createargs
72925a0866cSChuck Lever  *
73025a0866cSChuck Lever  *	struct createargs {
73125a0866cSChuck Lever  *		diropargs where;
73225a0866cSChuck Lever  *		sattr attributes;
73325a0866cSChuck Lever  *	};
73425a0866cSChuck Lever  */
73525a0866cSChuck Lever static int nfs2_xdr_enc_createargs(struct rpc_rqst *req, __be32 *p,
73625a0866cSChuck Lever 				   const struct nfs_createargs *args)
73725a0866cSChuck Lever {
73825a0866cSChuck Lever 	struct xdr_stream xdr;
73925a0866cSChuck Lever 
74025a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
74125a0866cSChuck Lever 	encode_diropargs(&xdr, args->fh, args->name, args->len);
74225a0866cSChuck Lever 	encode_sattr(&xdr, args->sattr);
74325a0866cSChuck Lever 	return 0;
74425a0866cSChuck Lever }
74525a0866cSChuck Lever 
74625a0866cSChuck Lever static int nfs2_xdr_enc_removeargs(struct rpc_rqst *req, __be32 *p,
74725a0866cSChuck Lever 				   const struct nfs_removeargs *args)
74825a0866cSChuck Lever {
74925a0866cSChuck Lever 	struct xdr_stream xdr;
75025a0866cSChuck Lever 
75125a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
75225a0866cSChuck Lever 	encode_diropargs(&xdr, args->fh, args->name.name, args->name.len);
75325a0866cSChuck Lever 	return 0;
75425a0866cSChuck Lever }
75525a0866cSChuck Lever 
75625a0866cSChuck Lever /*
75725a0866cSChuck Lever  * 2.2.12.  renameargs
75825a0866cSChuck Lever  *
75925a0866cSChuck Lever  *	struct renameargs {
76025a0866cSChuck Lever  *		diropargs from;
76125a0866cSChuck Lever  *		diropargs to;
76225a0866cSChuck Lever  *	};
76325a0866cSChuck Lever  */
76425a0866cSChuck Lever static int nfs2_xdr_enc_renameargs(struct rpc_rqst *req, __be32 *p,
76525a0866cSChuck Lever 				   const struct nfs_renameargs *args)
76625a0866cSChuck Lever {
76725a0866cSChuck Lever 	const struct qstr *old = args->old_name;
76825a0866cSChuck Lever 	const struct qstr *new = args->new_name;
76925a0866cSChuck Lever 	struct xdr_stream xdr;
77025a0866cSChuck Lever 
77125a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
77225a0866cSChuck Lever 	encode_diropargs(&xdr, args->old_dir, old->name, old->len);
77325a0866cSChuck Lever 	encode_diropargs(&xdr, args->new_dir, new->name, new->len);
77425a0866cSChuck Lever 	return 0;
77525a0866cSChuck Lever }
77625a0866cSChuck Lever 
77725a0866cSChuck Lever /*
77825a0866cSChuck Lever  * 2.2.13.  linkargs
77925a0866cSChuck Lever  *
78025a0866cSChuck Lever  *	struct linkargs {
78125a0866cSChuck Lever  *		fhandle from;
78225a0866cSChuck Lever  *		diropargs to;
78325a0866cSChuck Lever  *	};
78425a0866cSChuck Lever  */
78525a0866cSChuck Lever static int nfs2_xdr_enc_linkargs(struct rpc_rqst *req, __be32 *p,
78625a0866cSChuck Lever 				 const struct nfs_linkargs *args)
78725a0866cSChuck Lever {
78825a0866cSChuck Lever 	struct xdr_stream xdr;
78925a0866cSChuck Lever 
79025a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
79125a0866cSChuck Lever 	encode_fhandle(&xdr, args->fromfh);
79225a0866cSChuck Lever 	encode_diropargs(&xdr, args->tofh, args->toname, args->tolen);
79325a0866cSChuck Lever 	return 0;
79425a0866cSChuck Lever }
79525a0866cSChuck Lever 
79625a0866cSChuck Lever /*
79725a0866cSChuck Lever  * 2.2.14.  symlinkargs
79825a0866cSChuck Lever  *
79925a0866cSChuck Lever  *	struct symlinkargs {
80025a0866cSChuck Lever  *		diropargs from;
80125a0866cSChuck Lever  *		path to;
80225a0866cSChuck Lever  *		sattr attributes;
80325a0866cSChuck Lever  *	};
80425a0866cSChuck Lever  */
80525a0866cSChuck Lever static int nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req, __be32 *p,
80625a0866cSChuck Lever 				    const struct nfs_symlinkargs *args)
80725a0866cSChuck Lever {
80825a0866cSChuck Lever 	struct xdr_stream xdr;
80925a0866cSChuck Lever 
81025a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
81125a0866cSChuck Lever 	encode_diropargs(&xdr, args->fromfh, args->fromname, args->fromlen);
81225a0866cSChuck Lever 	encode_path(&xdr, args->pages, args->pathlen);
81325a0866cSChuck Lever 	encode_sattr(&xdr, args->sattr);
81425a0866cSChuck Lever 	return 0;
81525a0866cSChuck Lever }
81625a0866cSChuck Lever 
81725a0866cSChuck Lever /*
81825a0866cSChuck Lever  * 2.2.17.  readdirargs
81925a0866cSChuck Lever  *
82025a0866cSChuck Lever  *	struct readdirargs {
82125a0866cSChuck Lever  *		fhandle dir;
82225a0866cSChuck Lever  *		nfscookie cookie;
82325a0866cSChuck Lever  *		unsigned count;
82425a0866cSChuck Lever  *	};
82525a0866cSChuck Lever  */
82625a0866cSChuck Lever static void encode_readdirargs(struct xdr_stream *xdr,
82725a0866cSChuck Lever 			       const struct nfs_readdirargs *args)
82825a0866cSChuck Lever {
82925a0866cSChuck Lever 	__be32 *p;
83025a0866cSChuck Lever 
83125a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
83225a0866cSChuck Lever 
83325a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4);
83425a0866cSChuck Lever 	*p++ = cpu_to_be32(args->cookie);
83525a0866cSChuck Lever 	*p = cpu_to_be32(args->count);
83625a0866cSChuck Lever }
83725a0866cSChuck Lever 
83825a0866cSChuck Lever static int nfs2_xdr_enc_readdirargs(struct rpc_rqst *req, __be32 *p,
83925a0866cSChuck Lever 				    const struct nfs_readdirargs *args)
84025a0866cSChuck Lever {
84125a0866cSChuck Lever 	struct xdr_stream xdr;
84225a0866cSChuck Lever 
84325a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
84425a0866cSChuck Lever 	encode_readdirargs(&xdr, args);
84525a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, 0,
84625a0866cSChuck Lever 					args->count, NFS_readdirres_sz);
84725a0866cSChuck Lever 	return 0;
84825a0866cSChuck Lever }
84925a0866cSChuck Lever 
85025a0866cSChuck Lever /*
8511da177e4SLinus Torvalds  * Decode the result of a readdir call.
8521da177e4SLinus Torvalds  * We're not really decoding anymore, we just leave the buffer untouched
8531da177e4SLinus Torvalds  * and only check that it is syntactically correct.
8541da177e4SLinus Torvalds  * The real decoding happens in nfs_decode_entry below, called directly
8551da177e4SLinus Torvalds  * from nfs_readdir for each entry.
8561da177e4SLinus Torvalds  */
8571da177e4SLinus Torvalds static int
8589d787a75SAl Viro nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy)
8591da177e4SLinus Torvalds {
8601da177e4SLinus Torvalds 	struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
8611da177e4SLinus Torvalds 	struct kvec *iov = rcvbuf->head;
8621da177e4SLinus Torvalds 	struct page **page;
8636232dbbcSChuck Lever 	size_t hdrlen;
8646232dbbcSChuck Lever 	unsigned int pglen, recvd;
865ac396128STrond Myklebust 	int status;
8661da177e4SLinus Torvalds 
8671da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
868856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
8691da177e4SLinus Torvalds 
8701da177e4SLinus Torvalds 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
8711da177e4SLinus Torvalds 	if (iov->iov_len < hdrlen) {
872fe82a183SChuck Lever 		dprintk("NFS: READDIR reply header overflowed:"
8736232dbbcSChuck Lever 				"length %Zu > %Zu\n", hdrlen, iov->iov_len);
8741da177e4SLinus Torvalds 		return -errno_NFSERR_IO;
8751da177e4SLinus Torvalds 	} else if (iov->iov_len != hdrlen) {
8761da177e4SLinus Torvalds 		dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
8771da177e4SLinus Torvalds 		xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
8781da177e4SLinus Torvalds 	}
8791da177e4SLinus Torvalds 
8801da177e4SLinus Torvalds 	pglen = rcvbuf->page_len;
8811da177e4SLinus Torvalds 	recvd = rcvbuf->len - hdrlen;
8821da177e4SLinus Torvalds 	if (pglen > recvd)
8831da177e4SLinus Torvalds 		pglen = recvd;
8841da177e4SLinus Torvalds 	page = rcvbuf->pages;
885ac396128STrond Myklebust 	return pglen;
8861da177e4SLinus Torvalds }
8871da177e4SLinus Torvalds 
888babddc72SBryan Schumaker __be32 *
88982f2e547SBryan Schumaker nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus)
890babddc72SBryan Schumaker {
891babddc72SBryan Schumaker 	__be32 *p;
892babddc72SBryan Schumaker 	p = xdr_inline_decode(xdr, 4);
893babddc72SBryan Schumaker 	if (unlikely(!p))
894babddc72SBryan Schumaker 		goto out_overflow;
895babddc72SBryan Schumaker 	if (!ntohl(*p++)) {
896babddc72SBryan Schumaker 		p = xdr_inline_decode(xdr, 4);
897babddc72SBryan Schumaker 		if (unlikely(!p))
898babddc72SBryan Schumaker 			goto out_overflow;
899babddc72SBryan Schumaker 		if (!ntohl(*p++))
9001da177e4SLinus Torvalds 			return ERR_PTR(-EAGAIN);
9011da177e4SLinus Torvalds 		entry->eof = 1;
9021da177e4SLinus Torvalds 		return ERR_PTR(-EBADCOOKIE);
9031da177e4SLinus Torvalds 	}
9041da177e4SLinus Torvalds 
905babddc72SBryan Schumaker 	p = xdr_inline_decode(xdr, 8);
906babddc72SBryan Schumaker 	if (unlikely(!p))
907babddc72SBryan Schumaker 		goto out_overflow;
908babddc72SBryan Schumaker 
9091da177e4SLinus Torvalds 	entry->ino	  = ntohl(*p++);
9101da177e4SLinus Torvalds 	entry->len	  = ntohl(*p++);
911babddc72SBryan Schumaker 
912babddc72SBryan Schumaker 	p = xdr_inline_decode(xdr, entry->len + 4);
913babddc72SBryan Schumaker 	if (unlikely(!p))
914babddc72SBryan Schumaker 		goto out_overflow;
9151da177e4SLinus Torvalds 	entry->name	  = (const char *) p;
9161da177e4SLinus Torvalds 	p		 += XDR_QUADLEN(entry->len);
9171da177e4SLinus Torvalds 	entry->prev_cookie	  = entry->cookie;
9181da177e4SLinus Torvalds 	entry->cookie	  = ntohl(*p++);
919babddc72SBryan Schumaker 
9200b26a0bfSTrond Myklebust 	entry->d_type = DT_UNKNOWN;
9210b26a0bfSTrond Myklebust 
922babddc72SBryan Schumaker 	p = xdr_inline_peek(xdr, 8);
923babddc72SBryan Schumaker 	if (p != NULL)
9241da177e4SLinus Torvalds 		entry->eof = !p[0] && p[1];
925babddc72SBryan Schumaker 	else
926babddc72SBryan Schumaker 		entry->eof = 0;
9271da177e4SLinus Torvalds 
9281da177e4SLinus Torvalds 	return p;
929babddc72SBryan Schumaker 
930babddc72SBryan Schumaker out_overflow:
931babddc72SBryan Schumaker 	print_overflow_msg(__func__, xdr);
932463a376eSTrond Myklebust 	return ERR_PTR(-EAGAIN);
9331da177e4SLinus Torvalds }
9341da177e4SLinus Torvalds 
9351da177e4SLinus Torvalds /*
9361da177e4SLinus Torvalds  * NFS XDR decode functions
9371da177e4SLinus Torvalds  */
9381da177e4SLinus Torvalds /*
9391da177e4SLinus Torvalds  * Decode simple status reply
9401da177e4SLinus Torvalds  */
9411da177e4SLinus Torvalds static int
9429d787a75SAl Viro nfs_xdr_stat(struct rpc_rqst *req, __be32 *p, void *dummy)
9431da177e4SLinus Torvalds {
9441da177e4SLinus Torvalds 	int	status;
9451da177e4SLinus Torvalds 
9461da177e4SLinus Torvalds 	if ((status = ntohl(*p++)) != 0)
947856dff3dSBenny Halevy 		status = nfs_stat_to_errno(status);
9481da177e4SLinus Torvalds 	return status;
9491da177e4SLinus Torvalds }
9501da177e4SLinus Torvalds 
951f796f8b3SChuck Lever static int nfs2_xdr_dec_stat(struct rpc_rqst *req, __be32 *p,
952f796f8b3SChuck Lever 			     void *__unused)
953f796f8b3SChuck Lever {
954f796f8b3SChuck Lever 	struct xdr_stream xdr;
955f796f8b3SChuck Lever 	enum nfs_stat status;
956f796f8b3SChuck Lever 	int error;
957f796f8b3SChuck Lever 
958f796f8b3SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
959f796f8b3SChuck Lever 	error = decode_stat(&xdr, &status);
960f796f8b3SChuck Lever 	if (unlikely(error))
961f796f8b3SChuck Lever 		goto out;
962f796f8b3SChuck Lever 	if (status != NFS_OK)
963f796f8b3SChuck Lever 		goto out_default;
964f796f8b3SChuck Lever out:
965f796f8b3SChuck Lever 	return error;
966f796f8b3SChuck Lever out_default:
967f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
968f796f8b3SChuck Lever }
969f796f8b3SChuck Lever 
9701da177e4SLinus Torvalds /*
9711da177e4SLinus Torvalds  * Decode attrstat reply
9721da177e4SLinus Torvalds  * GETATTR, SETATTR, WRITE
9731da177e4SLinus Torvalds  */
9741da177e4SLinus Torvalds static int
9759d787a75SAl Viro nfs_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
9761da177e4SLinus Torvalds {
9771da177e4SLinus Torvalds 	int	status;
9781da177e4SLinus Torvalds 
9791da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
980856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
9811da177e4SLinus Torvalds 	xdr_decode_fattr(p, fattr);
9821da177e4SLinus Torvalds 	return 0;
9831da177e4SLinus Torvalds }
9841da177e4SLinus Torvalds 
985f796f8b3SChuck Lever static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, __be32 *p,
986f796f8b3SChuck Lever 				 struct nfs_fattr *result)
987f796f8b3SChuck Lever {
988f796f8b3SChuck Lever 	struct xdr_stream xdr;
989f796f8b3SChuck Lever 
990f796f8b3SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
991f796f8b3SChuck Lever 	return decode_attrstat(&xdr, result);
992f796f8b3SChuck Lever }
993f796f8b3SChuck Lever 
9941da177e4SLinus Torvalds /*
9951da177e4SLinus Torvalds  * Decode diropres reply
9961da177e4SLinus Torvalds  * LOOKUP, CREATE, MKDIR
9971da177e4SLinus Torvalds  */
9981da177e4SLinus Torvalds static int
9999d787a75SAl Viro nfs_xdr_diropres(struct rpc_rqst *req, __be32 *p, struct nfs_diropok *res)
10001da177e4SLinus Torvalds {
10011da177e4SLinus Torvalds 	int	status;
10021da177e4SLinus Torvalds 
10031da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
1004856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
10051da177e4SLinus Torvalds 	p = xdr_decode_fhandle(p, res->fh);
10061da177e4SLinus Torvalds 	xdr_decode_fattr(p, res->fattr);
10071da177e4SLinus Torvalds 	return 0;
10081da177e4SLinus Torvalds }
10091da177e4SLinus Torvalds 
1010f796f8b3SChuck Lever static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, __be32 *p,
1011f796f8b3SChuck Lever 				 struct nfs_diropok *result)
1012f796f8b3SChuck Lever {
1013f796f8b3SChuck Lever 	struct xdr_stream xdr;
1014f796f8b3SChuck Lever 
1015f796f8b3SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
1016f796f8b3SChuck Lever 	return decode_diropres(&xdr, result);
1017f796f8b3SChuck Lever }
1018f796f8b3SChuck Lever 
10191da177e4SLinus Torvalds /*
10201da177e4SLinus Torvalds  * Decode READLINK reply
10211da177e4SLinus Torvalds  */
10221da177e4SLinus Torvalds static int
10239d787a75SAl Viro nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy)
10241da177e4SLinus Torvalds {
10251da177e4SLinus Torvalds 	struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
10261da177e4SLinus Torvalds 	struct kvec *iov = rcvbuf->head;
10276232dbbcSChuck Lever 	size_t hdrlen;
10286232dbbcSChuck Lever 	u32 len, recvd;
10291da177e4SLinus Torvalds 	int	status;
10301da177e4SLinus Torvalds 
10311da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
1032856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
10331da177e4SLinus Torvalds 	/* Convert length of symlink */
10341da177e4SLinus Torvalds 	len = ntohl(*p++);
10356232dbbcSChuck Lever 	if (len >= rcvbuf->page_len) {
1036fe82a183SChuck Lever 		dprintk("nfs: server returned giant symlink!\n");
10371da177e4SLinus Torvalds 		return -ENAMETOOLONG;
10381da177e4SLinus Torvalds 	}
10391da177e4SLinus Torvalds 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
10401da177e4SLinus Torvalds 	if (iov->iov_len < hdrlen) {
1041fe82a183SChuck Lever 		dprintk("NFS: READLINK reply header overflowed:"
10426232dbbcSChuck Lever 				"length %Zu > %Zu\n", hdrlen, iov->iov_len);
10431da177e4SLinus Torvalds 		return -errno_NFSERR_IO;
10441da177e4SLinus Torvalds 	} else if (iov->iov_len != hdrlen) {
10451da177e4SLinus Torvalds 		dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
10461da177e4SLinus Torvalds 		xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
10471da177e4SLinus Torvalds 	}
10481da177e4SLinus Torvalds 	recvd = req->rq_rcv_buf.len - hdrlen;
10491da177e4SLinus Torvalds 	if (recvd < len) {
1050fe82a183SChuck Lever 		dprintk("NFS: server cheating in readlink reply: "
10511da177e4SLinus Torvalds 				"count %u > recvd %u\n", len, recvd);
10521da177e4SLinus Torvalds 		return -EIO;
10531da177e4SLinus Torvalds 	}
10541da177e4SLinus Torvalds 
1055b4687da7SChuck Lever 	xdr_terminate_string(rcvbuf, len);
10561da177e4SLinus Torvalds 	return 0;
10571da177e4SLinus Torvalds }
10581da177e4SLinus Torvalds 
10591da177e4SLinus Torvalds /*
1060f796f8b3SChuck Lever  * 2.2.6.  readlinkres
1061f796f8b3SChuck Lever  *
1062f796f8b3SChuck Lever  *	union readlinkres switch (stat status) {
1063f796f8b3SChuck Lever  *	case NFS_OK:
1064f796f8b3SChuck Lever  *		path data;
1065f796f8b3SChuck Lever  *	default:
1066f796f8b3SChuck Lever  *		void;
1067f796f8b3SChuck Lever  *	};
1068f796f8b3SChuck Lever  */
1069f796f8b3SChuck Lever static int nfs2_xdr_dec_readlinkres(struct rpc_rqst *req, __be32 *p,
1070f796f8b3SChuck Lever 				    void *__unused)
1071f796f8b3SChuck Lever {
1072f796f8b3SChuck Lever 	struct xdr_stream xdr;
1073f796f8b3SChuck Lever 	enum nfs_stat status;
1074f796f8b3SChuck Lever 	int error;
1075f796f8b3SChuck Lever 
1076f796f8b3SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
1077f796f8b3SChuck Lever 	error = decode_stat(&xdr, &status);
1078f796f8b3SChuck Lever 	if (unlikely(error))
1079f796f8b3SChuck Lever 		goto out;
1080f796f8b3SChuck Lever 	if (status != NFS_OK)
1081f796f8b3SChuck Lever 		goto out_default;
1082f796f8b3SChuck Lever 	error = decode_path(&xdr);
1083f796f8b3SChuck Lever out:
1084f796f8b3SChuck Lever 	return error;
1085f796f8b3SChuck Lever out_default:
1086f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
1087f796f8b3SChuck Lever }
1088f796f8b3SChuck Lever 
1089f796f8b3SChuck Lever /*
1090f796f8b3SChuck Lever  * 2.2.7.  readres
1091f796f8b3SChuck Lever  *
1092f796f8b3SChuck Lever  *	union readres switch (stat status) {
1093f796f8b3SChuck Lever  *	case NFS_OK:
1094f796f8b3SChuck Lever  *		fattr attributes;
1095f796f8b3SChuck Lever  *		nfsdata data;
1096f796f8b3SChuck Lever  *	default:
1097f796f8b3SChuck Lever  *		void;
1098f796f8b3SChuck Lever  *	};
1099f796f8b3SChuck Lever  */
1100f796f8b3SChuck Lever static int nfs2_xdr_dec_readres(struct rpc_rqst *req, __be32 *p,
1101f796f8b3SChuck Lever 				struct nfs_readres *result)
1102f796f8b3SChuck Lever {
1103f796f8b3SChuck Lever 	struct xdr_stream xdr;
1104f796f8b3SChuck Lever 	enum nfs_stat status;
1105f796f8b3SChuck Lever 	int error;
1106f796f8b3SChuck Lever 
1107f796f8b3SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
1108f796f8b3SChuck Lever 	error = decode_stat(&xdr, &status);
1109f796f8b3SChuck Lever 	if (unlikely(error))
1110f796f8b3SChuck Lever 		goto out;
1111f796f8b3SChuck Lever 	if (status != NFS_OK)
1112f796f8b3SChuck Lever 		goto out_default;
1113f796f8b3SChuck Lever 	error = decode_fattr(&xdr, result->fattr);
1114f796f8b3SChuck Lever 	if (unlikely(error))
1115f796f8b3SChuck Lever 		goto out;
1116f796f8b3SChuck Lever 	error = decode_nfsdata(&xdr, result);
1117f796f8b3SChuck Lever out:
1118f796f8b3SChuck Lever 	return error;
1119f796f8b3SChuck Lever out_default:
1120f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
1121f796f8b3SChuck Lever }
1122f796f8b3SChuck Lever 
1123f796f8b3SChuck Lever /*
11241da177e4SLinus Torvalds  * Decode WRITE reply
11251da177e4SLinus Torvalds  */
11261da177e4SLinus Torvalds static int
11279d787a75SAl Viro nfs_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
11281da177e4SLinus Torvalds {
11291da177e4SLinus Torvalds 	res->verf->committed = NFS_FILE_SYNC;
11301da177e4SLinus Torvalds 	return nfs_xdr_attrstat(req, p, res->fattr);
11311da177e4SLinus Torvalds }
11321da177e4SLinus Torvalds 
1133f796f8b3SChuck Lever static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, __be32 *p,
1134f796f8b3SChuck Lever 				 struct nfs_writeres *result)
1135f796f8b3SChuck Lever {
1136f796f8b3SChuck Lever 	struct xdr_stream xdr;
1137f796f8b3SChuck Lever 
1138f796f8b3SChuck Lever 	/* All NFSv2 writes are "file sync" writes */
1139f796f8b3SChuck Lever 	result->verf->committed = NFS_FILE_SYNC;
1140f796f8b3SChuck Lever 
1141f796f8b3SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
1142f796f8b3SChuck Lever 	return decode_attrstat(&xdr, result->fattr);
1143f796f8b3SChuck Lever }
1144f796f8b3SChuck Lever 
1145f796f8b3SChuck Lever /**
1146f796f8b3SChuck Lever  * nfs2_decode_dirent - Decode a single NFSv2 directory entry stored in
1147f796f8b3SChuck Lever  *                      the local page cache.
1148f796f8b3SChuck Lever  * @xdr: XDR stream where entry resides
1149f796f8b3SChuck Lever  * @entry: buffer to fill in with entry data
1150f796f8b3SChuck Lever  * @server: nfs_server data for this directory
1151f796f8b3SChuck Lever  * @plus: boolean indicating whether this should be a readdirplus entry
1152f796f8b3SChuck Lever  *
1153f796f8b3SChuck Lever  * Returns the position of the next item in the buffer, or an ERR_PTR.
1154f796f8b3SChuck Lever  *
1155f796f8b3SChuck Lever  * This function is not invoked during READDIR reply decoding, but
1156f796f8b3SChuck Lever  * rather whenever an application invokes the getdents(2) system call
1157f796f8b3SChuck Lever  * on a directory already in our cache.
1158f796f8b3SChuck Lever  *
1159f796f8b3SChuck Lever  * 2.2.17.  entry
1160f796f8b3SChuck Lever  *
1161f796f8b3SChuck Lever  *	struct entry {
1162f796f8b3SChuck Lever  *		unsigned	fileid;
1163f796f8b3SChuck Lever  *		filename	name;
1164f796f8b3SChuck Lever  *		nfscookie	cookie;
1165f796f8b3SChuck Lever  *		entry		*nextentry;
1166f796f8b3SChuck Lever  *	};
1167f796f8b3SChuck Lever  */
1168f796f8b3SChuck Lever __be32 *nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
1169f796f8b3SChuck Lever 			   struct nfs_server *server, int plus)
1170f796f8b3SChuck Lever {
1171f796f8b3SChuck Lever 	__be32 *p;
1172f796f8b3SChuck Lever 	int error;
1173f796f8b3SChuck Lever 
1174f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
1175f796f8b3SChuck Lever 	if (unlikely(p == NULL))
1176f796f8b3SChuck Lever 		goto out_overflow;
1177f796f8b3SChuck Lever 	if (*p++ == xdr_zero) {
1178f796f8b3SChuck Lever 		p = xdr_inline_decode(xdr, 4);
1179f796f8b3SChuck Lever 		if (unlikely(p == NULL))
1180f796f8b3SChuck Lever 			goto out_overflow;
1181f796f8b3SChuck Lever 		if (*p++ == xdr_zero)
1182f796f8b3SChuck Lever 			return ERR_PTR(-EAGAIN);
1183f796f8b3SChuck Lever 		entry->eof = 1;
1184f796f8b3SChuck Lever 		return ERR_PTR(-EBADCOOKIE);
1185f796f8b3SChuck Lever 	}
1186f796f8b3SChuck Lever 
1187f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
1188f796f8b3SChuck Lever 	if (unlikely(p == NULL))
1189f796f8b3SChuck Lever 		goto out_overflow;
1190f796f8b3SChuck Lever 	entry->ino = be32_to_cpup(p);
1191f796f8b3SChuck Lever 
1192f796f8b3SChuck Lever 	error = decode_filename_inline(xdr, &entry->name, &entry->len);
1193f796f8b3SChuck Lever 	if (unlikely(error))
1194f796f8b3SChuck Lever 		return ERR_PTR(error);
1195f796f8b3SChuck Lever 
1196f796f8b3SChuck Lever 	/*
1197f796f8b3SChuck Lever 	 * The type (size and byte order) of nfscookie isn't defined in
1198f796f8b3SChuck Lever 	 * RFC 1094.  This implementation assumes that it's an XDR uint32.
1199f796f8b3SChuck Lever 	 */
1200f796f8b3SChuck Lever 	entry->prev_cookie = entry->cookie;
1201f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
1202f796f8b3SChuck Lever 	if (unlikely(p == NULL))
1203f796f8b3SChuck Lever 		goto out_overflow;
1204f796f8b3SChuck Lever 	entry->cookie = be32_to_cpup(p);
1205f796f8b3SChuck Lever 
1206f796f8b3SChuck Lever 	entry->d_type = DT_UNKNOWN;
1207f796f8b3SChuck Lever 
1208f796f8b3SChuck Lever 	/* Peek at the next entry to see if we're at EOD */
1209f796f8b3SChuck Lever 	p = xdr_inline_peek(xdr, 4 + 4);
1210f796f8b3SChuck Lever 	entry->eof = 0;
1211f796f8b3SChuck Lever 	if (p != NULL)
1212f796f8b3SChuck Lever 		entry->eof = (p[0] == xdr_zero) && (p[1] != xdr_zero);
1213f796f8b3SChuck Lever 	return p;
1214f796f8b3SChuck Lever 
1215f796f8b3SChuck Lever out_overflow:
1216f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
1217f796f8b3SChuck Lever 	return ERR_PTR(-EAGAIN);
1218f796f8b3SChuck Lever }
1219f796f8b3SChuck Lever 
1220f796f8b3SChuck Lever /*
1221f796f8b3SChuck Lever  * 2.2.17.  readdirres
1222f796f8b3SChuck Lever  *
1223f796f8b3SChuck Lever  *	union readdirres switch (stat status) {
1224f796f8b3SChuck Lever  *	case NFS_OK:
1225f796f8b3SChuck Lever  *		struct {
1226f796f8b3SChuck Lever  *			entry *entries;
1227f796f8b3SChuck Lever  *			bool eof;
1228f796f8b3SChuck Lever  *		} readdirok;
1229f796f8b3SChuck Lever  *	default:
1230f796f8b3SChuck Lever  *		void;
1231f796f8b3SChuck Lever  *	};
1232f796f8b3SChuck Lever  *
1233f796f8b3SChuck Lever  * Read the directory contents into the page cache, but don't
1234f796f8b3SChuck Lever  * touch them.  The actual decoding is done by nfs2_decode_dirent()
1235f796f8b3SChuck Lever  * during subsequent nfs_readdir() calls.
1236f796f8b3SChuck Lever  */
1237f796f8b3SChuck Lever static int decode_readdirok(struct xdr_stream *xdr)
1238f796f8b3SChuck Lever {
1239f796f8b3SChuck Lever 	u32 recvd, pglen;
1240f796f8b3SChuck Lever 	size_t hdrlen;
1241f796f8b3SChuck Lever 
1242f796f8b3SChuck Lever 	pglen = xdr->buf->page_len;
1243f796f8b3SChuck Lever 	hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
1244f796f8b3SChuck Lever 	recvd = xdr->buf->len - hdrlen;
1245f796f8b3SChuck Lever 	if (unlikely(pglen > recvd))
1246f796f8b3SChuck Lever 		goto out_cheating;
1247f796f8b3SChuck Lever out:
1248f796f8b3SChuck Lever 	xdr_read_pages(xdr, pglen);
1249f796f8b3SChuck Lever 	return pglen;
1250f796f8b3SChuck Lever out_cheating:
1251f796f8b3SChuck Lever 	dprintk("NFS: server cheating in readdir result: "
1252f796f8b3SChuck Lever 		"pglen %u > recvd %u\n", pglen, recvd);
1253f796f8b3SChuck Lever 	pglen = recvd;
1254f796f8b3SChuck Lever 	goto out;
1255f796f8b3SChuck Lever }
1256f796f8b3SChuck Lever 
1257f796f8b3SChuck Lever static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req, __be32 *p,
1258f796f8b3SChuck Lever 				   void *__unused)
1259f796f8b3SChuck Lever {
1260f796f8b3SChuck Lever 	struct xdr_stream xdr;
1261f796f8b3SChuck Lever 	enum nfs_stat status;
1262f796f8b3SChuck Lever 	int error;
1263f796f8b3SChuck Lever 
1264f796f8b3SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
1265f796f8b3SChuck Lever 	error = decode_stat(&xdr, &status);
1266f796f8b3SChuck Lever 	if (unlikely(error))
1267f796f8b3SChuck Lever 		goto out;
1268f796f8b3SChuck Lever 	if (status != NFS_OK)
1269f796f8b3SChuck Lever 		goto out_default;
1270f796f8b3SChuck Lever 	error = decode_readdirok(&xdr);
1271f796f8b3SChuck Lever out:
1272f796f8b3SChuck Lever 	return error;
1273f796f8b3SChuck Lever out_default:
1274f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
1275f796f8b3SChuck Lever }
1276f796f8b3SChuck Lever 
12771da177e4SLinus Torvalds /*
12781da177e4SLinus Torvalds  * Decode STATFS reply
12791da177e4SLinus Torvalds  */
12801da177e4SLinus Torvalds static int
12819d787a75SAl Viro nfs_xdr_statfsres(struct rpc_rqst *req, __be32 *p, struct nfs2_fsstat *res)
12821da177e4SLinus Torvalds {
12831da177e4SLinus Torvalds 	int	status;
12841da177e4SLinus Torvalds 
12851da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
1286856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
12871da177e4SLinus Torvalds 
12881da177e4SLinus Torvalds 	res->tsize  = ntohl(*p++);
12891da177e4SLinus Torvalds 	res->bsize  = ntohl(*p++);
12901da177e4SLinus Torvalds 	res->blocks = ntohl(*p++);
12911da177e4SLinus Torvalds 	res->bfree  = ntohl(*p++);
12921da177e4SLinus Torvalds 	res->bavail = ntohl(*p++);
12931da177e4SLinus Torvalds 	return 0;
12941da177e4SLinus Torvalds }
12951da177e4SLinus Torvalds 
12961da177e4SLinus Torvalds /*
1297f796f8b3SChuck Lever  * 2.2.18.  statfsres
1298f796f8b3SChuck Lever  *
1299f796f8b3SChuck Lever  *	union statfsres (stat status) {
1300f796f8b3SChuck Lever  *	case NFS_OK:
1301f796f8b3SChuck Lever  *		struct {
1302f796f8b3SChuck Lever  *			unsigned tsize;
1303f796f8b3SChuck Lever  *			unsigned bsize;
1304f796f8b3SChuck Lever  *			unsigned blocks;
1305f796f8b3SChuck Lever  *			unsigned bfree;
1306f796f8b3SChuck Lever  *			unsigned bavail;
1307f796f8b3SChuck Lever  *		} info;
1308f796f8b3SChuck Lever  *	default:
1309f796f8b3SChuck Lever  *		void;
1310f796f8b3SChuck Lever  *	};
1311f796f8b3SChuck Lever  */
1312f796f8b3SChuck Lever static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result)
1313f796f8b3SChuck Lever {
1314f796f8b3SChuck Lever 	__be32 *p;
1315f796f8b3SChuck Lever 
1316f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS_info_sz << 2);
1317f796f8b3SChuck Lever 	if (unlikely(p == NULL))
1318f796f8b3SChuck Lever 		goto out_overflow;
1319f796f8b3SChuck Lever 	result->tsize  = be32_to_cpup(p++);
1320f796f8b3SChuck Lever 	result->bsize  = be32_to_cpup(p++);
1321f796f8b3SChuck Lever 	result->blocks = be32_to_cpup(p++);
1322f796f8b3SChuck Lever 	result->bfree  = be32_to_cpup(p++);
1323f796f8b3SChuck Lever 	result->bavail = be32_to_cpup(p);
1324f796f8b3SChuck Lever 	return 0;
1325f796f8b3SChuck Lever out_overflow:
1326f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
1327f796f8b3SChuck Lever 	return -EIO;
1328f796f8b3SChuck Lever }
1329f796f8b3SChuck Lever 
1330f796f8b3SChuck Lever static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, __be32 *p,
1331f796f8b3SChuck Lever 				  struct nfs2_fsstat *result)
1332f796f8b3SChuck Lever {
1333f796f8b3SChuck Lever 	struct xdr_stream xdr;
1334f796f8b3SChuck Lever 	enum nfs_stat status;
1335f796f8b3SChuck Lever 	int error;
1336f796f8b3SChuck Lever 
1337f796f8b3SChuck Lever 	xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
1338f796f8b3SChuck Lever 	error = decode_stat(&xdr, &status);
1339f796f8b3SChuck Lever 	if (unlikely(error))
1340f796f8b3SChuck Lever 		goto out;
1341f796f8b3SChuck Lever 	if (status != NFS_OK)
1342f796f8b3SChuck Lever 		goto out_default;
1343f796f8b3SChuck Lever 	error = decode_info(&xdr, result);
1344f796f8b3SChuck Lever out:
1345f796f8b3SChuck Lever 	return error;
1346f796f8b3SChuck Lever out_default:
1347f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
1348f796f8b3SChuck Lever }
1349f796f8b3SChuck Lever 
1350f796f8b3SChuck Lever 
1351f796f8b3SChuck Lever /*
13521da177e4SLinus Torvalds  * We need to translate between nfs status return values and
13531da177e4SLinus Torvalds  * the local errno values which may not be the same.
13541da177e4SLinus Torvalds  */
135585828493SChuck Lever static const struct {
13561da177e4SLinus Torvalds 	int stat;
13571da177e4SLinus Torvalds 	int errno;
13581da177e4SLinus Torvalds } nfs_errtbl[] = {
13591da177e4SLinus Torvalds 	{ NFS_OK,		0		},
1360856dff3dSBenny Halevy 	{ NFSERR_PERM,		-EPERM		},
1361856dff3dSBenny Halevy 	{ NFSERR_NOENT,		-ENOENT		},
1362856dff3dSBenny Halevy 	{ NFSERR_IO,		-errno_NFSERR_IO},
1363856dff3dSBenny Halevy 	{ NFSERR_NXIO,		-ENXIO		},
1364856dff3dSBenny Halevy /*	{ NFSERR_EAGAIN,	-EAGAIN		}, */
1365856dff3dSBenny Halevy 	{ NFSERR_ACCES,		-EACCES		},
1366856dff3dSBenny Halevy 	{ NFSERR_EXIST,		-EEXIST		},
1367856dff3dSBenny Halevy 	{ NFSERR_XDEV,		-EXDEV		},
1368856dff3dSBenny Halevy 	{ NFSERR_NODEV,		-ENODEV		},
1369856dff3dSBenny Halevy 	{ NFSERR_NOTDIR,	-ENOTDIR	},
1370856dff3dSBenny Halevy 	{ NFSERR_ISDIR,		-EISDIR		},
1371856dff3dSBenny Halevy 	{ NFSERR_INVAL,		-EINVAL		},
1372856dff3dSBenny Halevy 	{ NFSERR_FBIG,		-EFBIG		},
1373856dff3dSBenny Halevy 	{ NFSERR_NOSPC,		-ENOSPC		},
1374856dff3dSBenny Halevy 	{ NFSERR_ROFS,		-EROFS		},
1375856dff3dSBenny Halevy 	{ NFSERR_MLINK,		-EMLINK		},
1376856dff3dSBenny Halevy 	{ NFSERR_NAMETOOLONG,	-ENAMETOOLONG	},
1377856dff3dSBenny Halevy 	{ NFSERR_NOTEMPTY,	-ENOTEMPTY	},
1378856dff3dSBenny Halevy 	{ NFSERR_DQUOT,		-EDQUOT		},
1379856dff3dSBenny Halevy 	{ NFSERR_STALE,		-ESTALE		},
1380856dff3dSBenny Halevy 	{ NFSERR_REMOTE,	-EREMOTE	},
13811da177e4SLinus Torvalds #ifdef EWFLUSH
1382856dff3dSBenny Halevy 	{ NFSERR_WFLUSH,	-EWFLUSH	},
13831da177e4SLinus Torvalds #endif
1384856dff3dSBenny Halevy 	{ NFSERR_BADHANDLE,	-EBADHANDLE	},
1385856dff3dSBenny Halevy 	{ NFSERR_NOT_SYNC,	-ENOTSYNC	},
1386856dff3dSBenny Halevy 	{ NFSERR_BAD_COOKIE,	-EBADCOOKIE	},
1387856dff3dSBenny Halevy 	{ NFSERR_NOTSUPP,	-ENOTSUPP	},
1388856dff3dSBenny Halevy 	{ NFSERR_TOOSMALL,	-ETOOSMALL	},
1389fdcb4577STrond Myklebust 	{ NFSERR_SERVERFAULT,	-EREMOTEIO	},
1390856dff3dSBenny Halevy 	{ NFSERR_BADTYPE,	-EBADTYPE	},
1391856dff3dSBenny Halevy 	{ NFSERR_JUKEBOX,	-EJUKEBOX	},
1392856dff3dSBenny Halevy 	{ -1,			-EIO		}
13931da177e4SLinus Torvalds };
13941da177e4SLinus Torvalds 
139585828493SChuck Lever /**
139685828493SChuck Lever  * nfs_stat_to_errno - convert an NFS status code to a local errno
139785828493SChuck Lever  * @status: NFS status code to convert
139885828493SChuck Lever  *
139985828493SChuck Lever  * Returns a local errno value, or -EIO if the NFS status code is
140085828493SChuck Lever  * not recognized.  This function is used jointly by NFSv2 and NFSv3.
14011da177e4SLinus Torvalds  */
140285828493SChuck Lever int nfs_stat_to_errno(enum nfs_stat status)
14031da177e4SLinus Torvalds {
14041da177e4SLinus Torvalds 	int i;
14051da177e4SLinus Torvalds 
14061da177e4SLinus Torvalds 	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
140785828493SChuck Lever 		if (nfs_errtbl[i].stat == (int)status)
14081da177e4SLinus Torvalds 			return nfs_errtbl[i].errno;
14091da177e4SLinus Torvalds 	}
141085828493SChuck Lever 	dprintk("NFS: Unrecognized nfs status value: %u\n", status);
14111da177e4SLinus Torvalds 	return nfs_errtbl[i].errno;
14121da177e4SLinus Torvalds }
14131da177e4SLinus Torvalds 
14141da177e4SLinus Torvalds #define PROC(proc, argtype, restype, timer)				\
14151da177e4SLinus Torvalds [NFSPROC_##proc] = {							\
14161da177e4SLinus Torvalds 	.p_proc	    =  NFSPROC_##proc,					\
141725a0866cSChuck Lever 	.p_encode   =  (kxdrproc_t)nfs2_xdr_enc_##argtype,		\
1418f796f8b3SChuck Lever 	.p_decode   =  (kxdrproc_t)nfs2_xdr_dec_##restype,		\
14192bea90d4SChuck Lever 	.p_arglen   =  NFS_##argtype##_sz,				\
14202bea90d4SChuck Lever 	.p_replen   =  NFS_##restype##_sz,				\
1421cc0175c1SChuck Lever 	.p_timer    =  timer,						\
1422cc0175c1SChuck Lever 	.p_statidx  =  NFSPROC_##proc,					\
1423cc0175c1SChuck Lever 	.p_name     =  #proc,						\
14241da177e4SLinus Torvalds 	}
14251da177e4SLinus Torvalds struct rpc_procinfo	nfs_procedures[] = {
14261da177e4SLinus Torvalds     PROC(GETATTR,	fhandle,	attrstat, 1),
14271da177e4SLinus Torvalds     PROC(SETATTR,	sattrargs,	attrstat, 0),
14281da177e4SLinus Torvalds     PROC(LOOKUP,	diropargs,	diropres, 2),
14291da177e4SLinus Torvalds     PROC(READLINK,	readlinkargs,	readlinkres, 3),
14301da177e4SLinus Torvalds     PROC(READ,		readargs,	readres, 3),
14311da177e4SLinus Torvalds     PROC(WRITE,		writeargs,	writeres, 4),
14321da177e4SLinus Torvalds     PROC(CREATE,	createargs,	diropres, 0),
14334fdc17b2STrond Myklebust     PROC(REMOVE,	removeargs,	stat, 0),
14341da177e4SLinus Torvalds     PROC(RENAME,	renameargs,	stat, 0),
14351da177e4SLinus Torvalds     PROC(LINK,		linkargs,	stat, 0),
14361da177e4SLinus Torvalds     PROC(SYMLINK,	symlinkargs,	stat, 0),
14371da177e4SLinus Torvalds     PROC(MKDIR,		createargs,	diropres, 0),
14381da177e4SLinus Torvalds     PROC(RMDIR,		diropargs,	stat, 0),
14391da177e4SLinus Torvalds     PROC(READDIR,	readdirargs,	readdirres, 3),
14401da177e4SLinus Torvalds     PROC(STATFS,	fhandle,	statfsres, 0),
14411da177e4SLinus Torvalds };
14421da177e4SLinus Torvalds 
14431da177e4SLinus Torvalds struct rpc_version		nfs_version2 = {
14441da177e4SLinus Torvalds 	.number			= 2,
1445e8c96f8cSTobias Klauser 	.nrprocs		= ARRAY_SIZE(nfs_procedures),
14461da177e4SLinus Torvalds 	.procs			= nfs_procedures
14471da177e4SLinus Torvalds };
1448