xref: /openbmc/linux/fs/nfs/nfs2xdr.c (revision 25a0866c)
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 
8025a0866cSChuck Lever 
811da177e4SLinus Torvalds /*
821da177e4SLinus Torvalds  * Common NFS XDR functions as inlines
831da177e4SLinus Torvalds  */
849d787a75SAl Viro static inline __be32 *
854fdc17b2STrond Myklebust xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fhandle)
861da177e4SLinus Torvalds {
871da177e4SLinus Torvalds 	memcpy(p, fhandle->data, NFS2_FHSIZE);
881da177e4SLinus Torvalds 	return p + XDR_QUADLEN(NFS2_FHSIZE);
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds 
919d787a75SAl Viro static inline __be32 *
929d787a75SAl Viro xdr_decode_fhandle(__be32 *p, struct nfs_fh *fhandle)
931da177e4SLinus Torvalds {
941da177e4SLinus Torvalds 	/* NFSv2 handles have a fixed length */
951da177e4SLinus Torvalds 	fhandle->size = NFS2_FHSIZE;
961da177e4SLinus Torvalds 	memcpy(fhandle->data, p, NFS2_FHSIZE);
971da177e4SLinus Torvalds 	return p + XDR_QUADLEN(NFS2_FHSIZE);
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds 
1009d787a75SAl Viro static inline __be32*
10125a0866cSChuck Lever xdr_encode_time(__be32 *p, const struct timespec *timep)
1021da177e4SLinus Torvalds {
1031da177e4SLinus Torvalds 	*p++ = htonl(timep->tv_sec);
1041da177e4SLinus Torvalds 	/* Convert nanoseconds into microseconds */
1051da177e4SLinus Torvalds 	*p++ = htonl(timep->tv_nsec ? timep->tv_nsec / 1000 : 0);
1061da177e4SLinus Torvalds 	return p;
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds 
1099d787a75SAl Viro static inline __be32*
11025a0866cSChuck Lever xdr_encode_current_server_time(__be32 *p, const struct timespec *timep)
1111da177e4SLinus Torvalds {
1121da177e4SLinus Torvalds 	/*
1131da177e4SLinus Torvalds 	 * Passing the invalid value useconds=1000000 is a
1141da177e4SLinus Torvalds 	 * Sun convention for "set to current server time".
1151da177e4SLinus Torvalds 	 * It's needed to make permissions checks for the
1161da177e4SLinus Torvalds 	 * "touch" program across v2 mounts to Solaris and
1171da177e4SLinus Torvalds 	 * Irix boxes work correctly. See description of
1181da177e4SLinus Torvalds 	 * sattr in section 6.1 of "NFS Illustrated" by
1191da177e4SLinus Torvalds 	 * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5
1201da177e4SLinus Torvalds 	 */
1211da177e4SLinus Torvalds 	*p++ = htonl(timep->tv_sec);
1221da177e4SLinus Torvalds 	*p++ = htonl(1000000);
1231da177e4SLinus Torvalds 	return p;
1241da177e4SLinus Torvalds }
1251da177e4SLinus Torvalds 
1269d787a75SAl Viro static inline __be32*
1279d787a75SAl Viro xdr_decode_time(__be32 *p, struct timespec *timep)
1281da177e4SLinus Torvalds {
1291da177e4SLinus Torvalds 	timep->tv_sec = ntohl(*p++);
1301da177e4SLinus Torvalds 	/* Convert microseconds into nanoseconds */
1311da177e4SLinus Torvalds 	timep->tv_nsec = ntohl(*p++) * 1000;
1321da177e4SLinus Torvalds 	return p;
1331da177e4SLinus Torvalds }
1341da177e4SLinus Torvalds 
1359d787a75SAl Viro static __be32 *
1369d787a75SAl Viro xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
1371da177e4SLinus Torvalds {
138bca79478STrond Myklebust 	u32 rdev, type;
139bca79478STrond Myklebust 	type = ntohl(*p++);
1401da177e4SLinus Torvalds 	fattr->mode = ntohl(*p++);
1411da177e4SLinus Torvalds 	fattr->nlink = ntohl(*p++);
1421da177e4SLinus Torvalds 	fattr->uid = ntohl(*p++);
1431da177e4SLinus Torvalds 	fattr->gid = ntohl(*p++);
1441da177e4SLinus Torvalds 	fattr->size = ntohl(*p++);
1451da177e4SLinus Torvalds 	fattr->du.nfs2.blocksize = ntohl(*p++);
1461da177e4SLinus Torvalds 	rdev = ntohl(*p++);
1471da177e4SLinus Torvalds 	fattr->du.nfs2.blocks = ntohl(*p++);
1488b4bdcf8STrond Myklebust 	fattr->fsid.major = ntohl(*p++);
1498b4bdcf8STrond Myklebust 	fattr->fsid.minor = 0;
1501da177e4SLinus Torvalds 	fattr->fileid = ntohl(*p++);
1511da177e4SLinus Torvalds 	p = xdr_decode_time(p, &fattr->atime);
1521da177e4SLinus Torvalds 	p = xdr_decode_time(p, &fattr->mtime);
1531da177e4SLinus Torvalds 	p = xdr_decode_time(p, &fattr->ctime);
1549e6e70f8STrond Myklebust 	fattr->valid |= NFS_ATTR_FATTR_V2;
1551da177e4SLinus Torvalds 	fattr->rdev = new_decode_dev(rdev);
156bca79478STrond Myklebust 	if (type == NFCHR && rdev == NFS2_FIFO_DEV) {
1571da177e4SLinus Torvalds 		fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
1581da177e4SLinus Torvalds 		fattr->rdev = 0;
1591da177e4SLinus Torvalds 	}
1601da177e4SLinus Torvalds 	return p;
1611da177e4SLinus Torvalds }
1621da177e4SLinus Torvalds 
1639d787a75SAl Viro static inline __be32 *
1649d787a75SAl Viro xdr_encode_sattr(__be32 *p, struct iattr *attr)
1651da177e4SLinus Torvalds {
1669d787a75SAl Viro 	const __be32 not_set = __constant_htonl(0xFFFFFFFF);
167eadb8c14STrond Myklebust 
168eadb8c14STrond Myklebust 	*p++ = (attr->ia_valid & ATTR_MODE) ? htonl(attr->ia_mode) : not_set;
169eadb8c14STrond Myklebust 	*p++ = (attr->ia_valid & ATTR_UID) ? htonl(attr->ia_uid) : not_set;
170eadb8c14STrond Myklebust 	*p++ = (attr->ia_valid & ATTR_GID) ? htonl(attr->ia_gid) : not_set;
171eadb8c14STrond Myklebust 	*p++ = (attr->ia_valid & ATTR_SIZE) ? htonl(attr->ia_size) : not_set;
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds 	if (attr->ia_valid & ATTR_ATIME_SET) {
1741da177e4SLinus Torvalds 		p = xdr_encode_time(p, &attr->ia_atime);
1751da177e4SLinus Torvalds 	} else if (attr->ia_valid & ATTR_ATIME) {
1761da177e4SLinus Torvalds 		p = xdr_encode_current_server_time(p, &attr->ia_atime);
1771da177e4SLinus Torvalds 	} else {
178eadb8c14STrond Myklebust 		*p++ = not_set;
179eadb8c14STrond Myklebust 		*p++ = not_set;
1801da177e4SLinus Torvalds 	}
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds 	if (attr->ia_valid & ATTR_MTIME_SET) {
1831da177e4SLinus Torvalds 		p = xdr_encode_time(p, &attr->ia_mtime);
1841da177e4SLinus Torvalds 	} else if (attr->ia_valid & ATTR_MTIME) {
1851da177e4SLinus Torvalds 		p = xdr_encode_current_server_time(p, &attr->ia_mtime);
1861da177e4SLinus Torvalds 	} else {
187eadb8c14STrond Myklebust 		*p++ = not_set;
188eadb8c14STrond Myklebust 		*p++ = not_set;
1891da177e4SLinus Torvalds 	}
1901da177e4SLinus Torvalds   	return p;
1911da177e4SLinus Torvalds }
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds /*
19425a0866cSChuck Lever  * Encode/decode NFSv2 basic data types
19525a0866cSChuck Lever  *
19625a0866cSChuck Lever  * Basic NFSv2 data types are defined in section 2.3 of RFC 1094:
19725a0866cSChuck Lever  * "NFS: Network File System Protocol Specification".
19825a0866cSChuck Lever  *
19925a0866cSChuck Lever  * Not all basic data types have their own encoding and decoding
20025a0866cSChuck Lever  * functions.  For run-time efficiency, some data types are encoded
20125a0866cSChuck Lever  * or decoded inline.
20225a0866cSChuck Lever  */
20325a0866cSChuck Lever 
20425a0866cSChuck Lever /*
20525a0866cSChuck Lever  * 2.3.3.  fhandle
20625a0866cSChuck Lever  *
20725a0866cSChuck Lever  *	typedef opaque fhandle[FHSIZE];
20825a0866cSChuck Lever  */
20925a0866cSChuck Lever static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh)
21025a0866cSChuck Lever {
21125a0866cSChuck Lever 	__be32 *p;
21225a0866cSChuck Lever 
21325a0866cSChuck Lever 	BUG_ON(fh->size != NFS2_FHSIZE);
21425a0866cSChuck Lever 	p = xdr_reserve_space(xdr, NFS2_FHSIZE);
21525a0866cSChuck Lever 	memcpy(p, fh->data, NFS2_FHSIZE);
21625a0866cSChuck Lever }
21725a0866cSChuck Lever 
21825a0866cSChuck Lever /*
21925a0866cSChuck Lever  * 2.3.6.  sattr
22025a0866cSChuck Lever  *
22125a0866cSChuck Lever  *	struct sattr {
22225a0866cSChuck Lever  *		unsigned int	mode;
22325a0866cSChuck Lever  *		unsigned int	uid;
22425a0866cSChuck Lever  *		unsigned int	gid;
22525a0866cSChuck Lever  *		unsigned int	size;
22625a0866cSChuck Lever  *		timeval		atime;
22725a0866cSChuck Lever  *		timeval		mtime;
22825a0866cSChuck Lever  *	};
22925a0866cSChuck Lever  */
23025a0866cSChuck Lever 
23125a0866cSChuck Lever #define NFS2_SATTR_NOT_SET	(0xffffffff)
23225a0866cSChuck Lever 
23325a0866cSChuck Lever static __be32 *xdr_time_not_set(__be32 *p)
23425a0866cSChuck Lever {
23525a0866cSChuck Lever 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
23625a0866cSChuck Lever 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
23725a0866cSChuck Lever 	return p;
23825a0866cSChuck Lever }
23925a0866cSChuck Lever 
24025a0866cSChuck Lever static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr)
24125a0866cSChuck Lever {
24225a0866cSChuck Lever 	__be32 *p;
24325a0866cSChuck Lever 
24425a0866cSChuck Lever 	p = xdr_reserve_space(xdr, NFS_sattr_sz << 2);
24525a0866cSChuck Lever 
24625a0866cSChuck Lever 	if (attr->ia_valid & ATTR_MODE)
24725a0866cSChuck Lever 		*p++ = cpu_to_be32(attr->ia_mode);
24825a0866cSChuck Lever 	else
24925a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
25025a0866cSChuck Lever 	if (attr->ia_valid & ATTR_UID)
25125a0866cSChuck Lever 		*p++ = cpu_to_be32(attr->ia_uid);
25225a0866cSChuck Lever 	else
25325a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
25425a0866cSChuck Lever 	if (attr->ia_valid & ATTR_GID)
25525a0866cSChuck Lever 		*p++ = cpu_to_be32(attr->ia_gid);
25625a0866cSChuck Lever 	else
25725a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
25825a0866cSChuck Lever 	if (attr->ia_valid & ATTR_SIZE)
25925a0866cSChuck Lever 		*p++ = cpu_to_be32((u32)attr->ia_size);
26025a0866cSChuck Lever 	else
26125a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
26225a0866cSChuck Lever 
26325a0866cSChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET)
26425a0866cSChuck Lever 		p = xdr_encode_time(p, &attr->ia_atime);
26525a0866cSChuck Lever 	else if (attr->ia_valid & ATTR_ATIME)
26625a0866cSChuck Lever 		p = xdr_encode_current_server_time(p, &attr->ia_atime);
26725a0866cSChuck Lever 	else
26825a0866cSChuck Lever 		p = xdr_time_not_set(p);
26925a0866cSChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET)
27025a0866cSChuck Lever 		xdr_encode_time(p, &attr->ia_mtime);
27125a0866cSChuck Lever 	else if (attr->ia_valid & ATTR_MTIME)
27225a0866cSChuck Lever 		xdr_encode_current_server_time(p, &attr->ia_mtime);
27325a0866cSChuck Lever 	else
27425a0866cSChuck Lever 		xdr_time_not_set(p);
27525a0866cSChuck Lever }
27625a0866cSChuck Lever 
27725a0866cSChuck Lever /*
27825a0866cSChuck Lever  * 2.3.7.  filename
27925a0866cSChuck Lever  *
28025a0866cSChuck Lever  *	typedef string filename<MAXNAMLEN>;
28125a0866cSChuck Lever  */
28225a0866cSChuck Lever static void encode_filename(struct xdr_stream *xdr,
28325a0866cSChuck Lever 			    const char *name, u32 length)
28425a0866cSChuck Lever {
28525a0866cSChuck Lever 	__be32 *p;
28625a0866cSChuck Lever 
28725a0866cSChuck Lever 	BUG_ON(length > NFS2_MAXNAMLEN);
28825a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + length);
28925a0866cSChuck Lever 	xdr_encode_opaque(p, name, length);
29025a0866cSChuck Lever }
29125a0866cSChuck Lever 
29225a0866cSChuck Lever /*
29325a0866cSChuck Lever  * 2.3.8.  path
29425a0866cSChuck Lever  *
29525a0866cSChuck Lever  *	typedef string path<MAXPATHLEN>;
29625a0866cSChuck Lever  */
29725a0866cSChuck Lever static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length)
29825a0866cSChuck Lever {
29925a0866cSChuck Lever 	__be32 *p;
30025a0866cSChuck Lever 
30125a0866cSChuck Lever 	BUG_ON(length > NFS2_MAXPATHLEN);
30225a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4);
30325a0866cSChuck Lever 	*p = cpu_to_be32(length);
30425a0866cSChuck Lever 	xdr_write_pages(xdr, pages, 0, length);
30525a0866cSChuck Lever }
30625a0866cSChuck Lever 
30725a0866cSChuck Lever /*
30825a0866cSChuck Lever  * 2.3.10.  diropargs
30925a0866cSChuck Lever  *
31025a0866cSChuck Lever  *	struct diropargs {
31125a0866cSChuck Lever  *		fhandle  dir;
31225a0866cSChuck Lever  *		filename name;
31325a0866cSChuck Lever  *	};
31425a0866cSChuck Lever  */
31525a0866cSChuck Lever static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh,
31625a0866cSChuck Lever 			     const char *name, u32 length)
31725a0866cSChuck Lever {
31825a0866cSChuck Lever 	encode_fhandle(xdr, fh);
31925a0866cSChuck Lever 	encode_filename(xdr, name, length);
32025a0866cSChuck Lever }
32125a0866cSChuck Lever 
32225a0866cSChuck Lever 
32325a0866cSChuck Lever /*
3241da177e4SLinus Torvalds  * NFS encode functions
3251da177e4SLinus Torvalds  */
3261da177e4SLinus Torvalds /*
3271da177e4SLinus Torvalds  * Encode file handle argument
3281da177e4SLinus Torvalds  * GETATTR, READLINK, STATFS
3291da177e4SLinus Torvalds  */
3301da177e4SLinus Torvalds static int
3319d787a75SAl Viro nfs_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh)
3321da177e4SLinus Torvalds {
3331da177e4SLinus Torvalds 	p = xdr_encode_fhandle(p, fh);
3341da177e4SLinus Torvalds 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
3351da177e4SLinus Torvalds 	return 0;
3361da177e4SLinus Torvalds }
3371da177e4SLinus Torvalds 
33825a0866cSChuck Lever static int nfs2_xdr_enc_fhandle(struct rpc_rqst *req, __be32 *p,
33925a0866cSChuck Lever 				const struct nfs_fh *fh)
34025a0866cSChuck Lever {
34125a0866cSChuck Lever 	struct xdr_stream xdr;
34225a0866cSChuck Lever 
34325a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
34425a0866cSChuck Lever 	encode_fhandle(&xdr, fh);
34525a0866cSChuck Lever 	return 0;
34625a0866cSChuck Lever }
34725a0866cSChuck Lever 
3481da177e4SLinus Torvalds /*
3491da177e4SLinus Torvalds  * Encode SETATTR arguments
3501da177e4SLinus Torvalds  */
3511da177e4SLinus Torvalds static int
3529d787a75SAl Viro nfs_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs_sattrargs *args)
3531da177e4SLinus Torvalds {
3541da177e4SLinus Torvalds 	p = xdr_encode_fhandle(p, args->fh);
3551da177e4SLinus Torvalds 	p = xdr_encode_sattr(p, args->sattr);
3561da177e4SLinus Torvalds 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
3571da177e4SLinus Torvalds 	return 0;
3581da177e4SLinus Torvalds }
3591da177e4SLinus Torvalds 
3601da177e4SLinus Torvalds /*
36125a0866cSChuck Lever  * 2.2.3.  sattrargs
36225a0866cSChuck Lever  *
36325a0866cSChuck Lever  *	struct sattrargs {
36425a0866cSChuck Lever  *		fhandle file;
36525a0866cSChuck Lever  *		sattr attributes;
36625a0866cSChuck Lever  *	};
36725a0866cSChuck Lever  */
36825a0866cSChuck Lever static int nfs2_xdr_enc_sattrargs(struct rpc_rqst *req, __be32 *p,
36925a0866cSChuck Lever 				  const struct nfs_sattrargs *args)
37025a0866cSChuck Lever {
37125a0866cSChuck Lever 	struct xdr_stream xdr;
37225a0866cSChuck Lever 
37325a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
37425a0866cSChuck Lever 	encode_fhandle(&xdr, args->fh);
37525a0866cSChuck Lever 	encode_sattr(&xdr, args->sattr);
37625a0866cSChuck Lever 	return 0;
37725a0866cSChuck Lever }
37825a0866cSChuck Lever 
37925a0866cSChuck Lever /*
3801da177e4SLinus Torvalds  * Encode directory ops argument
3814fdc17b2STrond Myklebust  * LOOKUP, RMDIR
3821da177e4SLinus Torvalds  */
3831da177e4SLinus Torvalds static int
3849d787a75SAl Viro nfs_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs_diropargs *args)
3851da177e4SLinus Torvalds {
3861da177e4SLinus Torvalds 	p = xdr_encode_fhandle(p, args->fh);
3871da177e4SLinus Torvalds 	p = xdr_encode_array(p, args->name, args->len);
3881da177e4SLinus Torvalds 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
3891da177e4SLinus Torvalds 	return 0;
3901da177e4SLinus Torvalds }
3911da177e4SLinus Torvalds 
39225a0866cSChuck Lever static int nfs2_xdr_enc_diropargs(struct rpc_rqst *req, __be32 *p,
39325a0866cSChuck Lever 				  const struct nfs_diropargs *args)
39425a0866cSChuck Lever {
39525a0866cSChuck Lever 	struct xdr_stream xdr;
39625a0866cSChuck Lever 
39725a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
39825a0866cSChuck Lever 	encode_diropargs(&xdr, args->fh, args->name, args->len);
39925a0866cSChuck Lever 	return 0;
40025a0866cSChuck Lever }
40125a0866cSChuck Lever 
4021da177e4SLinus Torvalds /*
4034fdc17b2STrond Myklebust  * Encode REMOVE argument
4044fdc17b2STrond Myklebust  */
4054fdc17b2STrond Myklebust static int
4064fdc17b2STrond Myklebust nfs_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args)
4074fdc17b2STrond Myklebust {
4084fdc17b2STrond Myklebust 	p = xdr_encode_fhandle(p, args->fh);
4094fdc17b2STrond Myklebust 	p = xdr_encode_array(p, args->name.name, args->name.len);
4104fdc17b2STrond Myklebust 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
4114fdc17b2STrond Myklebust 	return 0;
4124fdc17b2STrond Myklebust }
4134fdc17b2STrond Myklebust 
41425a0866cSChuck Lever static int nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req, __be32 *p,
41525a0866cSChuck Lever 				     const struct nfs_readlinkargs *args)
41625a0866cSChuck Lever {
41725a0866cSChuck Lever 	struct xdr_stream xdr;
41825a0866cSChuck Lever 
41925a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
42025a0866cSChuck Lever 	encode_fhandle(&xdr, args->fh);
42125a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
42225a0866cSChuck Lever 					args->pglen, NFS_readlinkres_sz);
42325a0866cSChuck Lever 	return 0;
42425a0866cSChuck Lever }
42525a0866cSChuck Lever 
4264fdc17b2STrond Myklebust /*
4271da177e4SLinus Torvalds  * Arguments to a READ call. Since we read data directly into the page
4281da177e4SLinus Torvalds  * cache, we also set up the reply iovec here so that iov[1] points
4291da177e4SLinus Torvalds  * exactly to the page we want to fetch.
4301da177e4SLinus Torvalds  */
4311da177e4SLinus Torvalds static int
4329d787a75SAl Viro nfs_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
4331da177e4SLinus Torvalds {
434a17c2153STrond Myklebust 	struct rpc_auth	*auth = req->rq_cred->cr_auth;
4351da177e4SLinus Torvalds 	unsigned int replen;
4361da177e4SLinus Torvalds 	u32 offset = (u32)args->offset;
4371da177e4SLinus Torvalds 	u32 count = args->count;
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds 	p = xdr_encode_fhandle(p, args->fh);
4401da177e4SLinus Torvalds 	*p++ = htonl(offset);
4411da177e4SLinus Torvalds 	*p++ = htonl(count);
4421da177e4SLinus Torvalds 	*p++ = htonl(count);
4431da177e4SLinus Torvalds 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds 	/* Inline the page array */
4461da177e4SLinus Torvalds 	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2;
4471da177e4SLinus Torvalds 	xdr_inline_pages(&req->rq_rcv_buf, replen,
4481da177e4SLinus Torvalds 			 args->pages, args->pgbase, count);
4494f22ccc3S\"Talpey, Thomas\ 	req->rq_rcv_buf.flags |= XDRBUF_READ;
4501da177e4SLinus Torvalds 	return 0;
4511da177e4SLinus Torvalds }
4521da177e4SLinus Torvalds 
4531da177e4SLinus Torvalds /*
45425a0866cSChuck Lever  * 2.2.7.  readargs
45525a0866cSChuck Lever  *
45625a0866cSChuck Lever  *	struct readargs {
45725a0866cSChuck Lever  *		fhandle file;
45825a0866cSChuck Lever  *		unsigned offset;
45925a0866cSChuck Lever  *		unsigned count;
46025a0866cSChuck Lever  *		unsigned totalcount;
46125a0866cSChuck Lever  *	};
46225a0866cSChuck Lever  */
46325a0866cSChuck Lever static void encode_readargs(struct xdr_stream *xdr,
46425a0866cSChuck Lever 			    const struct nfs_readargs *args)
46525a0866cSChuck Lever {
46625a0866cSChuck Lever 	u32 offset = args->offset;
46725a0866cSChuck Lever 	u32 count = args->count;
46825a0866cSChuck Lever 	__be32 *p;
46925a0866cSChuck Lever 
47025a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
47125a0866cSChuck Lever 
47225a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4 + 4);
47325a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
47425a0866cSChuck Lever 	*p++ = cpu_to_be32(count);
47525a0866cSChuck Lever 	*p = cpu_to_be32(count);
47625a0866cSChuck Lever }
47725a0866cSChuck Lever 
47825a0866cSChuck Lever static int nfs2_xdr_enc_readargs(struct rpc_rqst *req, __be32 *p,
47925a0866cSChuck Lever 				 const struct nfs_readargs *args)
48025a0866cSChuck Lever {
48125a0866cSChuck Lever 	struct xdr_stream xdr;
48225a0866cSChuck Lever 
48325a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
48425a0866cSChuck Lever 	encode_readargs(&xdr, args);
48525a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
48625a0866cSChuck Lever 					args->count, NFS_readres_sz);
48725a0866cSChuck Lever 	req->rq_rcv_buf.flags |= XDRBUF_READ;
48825a0866cSChuck Lever 	return 0;
48925a0866cSChuck Lever }
49025a0866cSChuck Lever 
49125a0866cSChuck Lever /*
4921da177e4SLinus Torvalds  * Decode READ reply
4931da177e4SLinus Torvalds  */
4941da177e4SLinus Torvalds static int
4959d787a75SAl Viro nfs_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
4961da177e4SLinus Torvalds {
4971da177e4SLinus Torvalds 	struct kvec *iov = req->rq_rcv_buf.head;
4986232dbbcSChuck Lever 	size_t hdrlen;
4996232dbbcSChuck Lever 	u32 count, recvd;
5006232dbbcSChuck Lever 	int status;
5011da177e4SLinus Torvalds 
5021da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
503856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
5041da177e4SLinus Torvalds 	p = xdr_decode_fattr(p, res->fattr);
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds 	count = ntohl(*p++);
5071da177e4SLinus Torvalds 	res->eof = 0;
5081da177e4SLinus Torvalds 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
5091da177e4SLinus Torvalds 	if (iov->iov_len < hdrlen) {
510fe82a183SChuck Lever 		dprintk("NFS: READ reply header overflowed:"
5116232dbbcSChuck Lever 				"length %Zu > %Zu\n", hdrlen, iov->iov_len);
5121da177e4SLinus Torvalds 		return -errno_NFSERR_IO;
5131da177e4SLinus Torvalds 	} else if (iov->iov_len != hdrlen) {
5141da177e4SLinus Torvalds 		dprintk("NFS: READ header is short. iovec will be shifted.\n");
5151da177e4SLinus Torvalds 		xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
5161da177e4SLinus Torvalds 	}
5171da177e4SLinus Torvalds 
5181da177e4SLinus Torvalds 	recvd = req->rq_rcv_buf.len - hdrlen;
5191da177e4SLinus Torvalds 	if (count > recvd) {
520fe82a183SChuck Lever 		dprintk("NFS: server cheating in read reply: "
5216232dbbcSChuck Lever 			"count %u > recvd %u\n", count, recvd);
5221da177e4SLinus Torvalds 		count = recvd;
5231da177e4SLinus Torvalds 	}
5241da177e4SLinus Torvalds 
5256232dbbcSChuck Lever 	dprintk("RPC:      readres OK count %u\n", count);
5261da177e4SLinus Torvalds 	if (count < res->count)
5271da177e4SLinus Torvalds 		res->count = count;
5281da177e4SLinus Torvalds 
5291da177e4SLinus Torvalds 	return count;
5301da177e4SLinus Torvalds }
5311da177e4SLinus Torvalds 
5321da177e4SLinus Torvalds 
5331da177e4SLinus Torvalds /*
5341da177e4SLinus Torvalds  * Write arguments. Splice the buffer to be written into the iovec.
5351da177e4SLinus Torvalds  */
5361da177e4SLinus Torvalds static int
5379d787a75SAl Viro nfs_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
5381da177e4SLinus Torvalds {
5391da177e4SLinus Torvalds 	struct xdr_buf *sndbuf = &req->rq_snd_buf;
5401da177e4SLinus Torvalds 	u32 offset = (u32)args->offset;
5411da177e4SLinus Torvalds 	u32 count = args->count;
5421da177e4SLinus Torvalds 
5431da177e4SLinus Torvalds 	p = xdr_encode_fhandle(p, args->fh);
5441da177e4SLinus Torvalds 	*p++ = htonl(offset);
5451da177e4SLinus Torvalds 	*p++ = htonl(offset);
5461da177e4SLinus Torvalds 	*p++ = htonl(count);
5471da177e4SLinus Torvalds 	*p++ = htonl(count);
5481da177e4SLinus Torvalds 	sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
5491da177e4SLinus Torvalds 
5501da177e4SLinus Torvalds 	/* Copy the page array */
5511da177e4SLinus Torvalds 	xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
5524f22ccc3S\"Talpey, Thomas\ 	sndbuf->flags |= XDRBUF_WRITE;
5531da177e4SLinus Torvalds 	return 0;
5541da177e4SLinus Torvalds }
5551da177e4SLinus Torvalds 
5561da177e4SLinus Torvalds /*
55725a0866cSChuck Lever  * 2.2.9.  writeargs
55825a0866cSChuck Lever  *
55925a0866cSChuck Lever  *	struct writeargs {
56025a0866cSChuck Lever  *		fhandle file;
56125a0866cSChuck Lever  *		unsigned beginoffset;
56225a0866cSChuck Lever  *		unsigned offset;
56325a0866cSChuck Lever  *		unsigned totalcount;
56425a0866cSChuck Lever  *		nfsdata data;
56525a0866cSChuck Lever  *	};
56625a0866cSChuck Lever  */
56725a0866cSChuck Lever static void encode_writeargs(struct xdr_stream *xdr,
56825a0866cSChuck Lever 			     const struct nfs_writeargs *args)
56925a0866cSChuck Lever {
57025a0866cSChuck Lever 	u32 offset = args->offset;
57125a0866cSChuck Lever 	u32 count = args->count;
57225a0866cSChuck Lever 	__be32 *p;
57325a0866cSChuck Lever 
57425a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
57525a0866cSChuck Lever 
57625a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4);
57725a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
57825a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
57925a0866cSChuck Lever 	*p++ = cpu_to_be32(count);
58025a0866cSChuck Lever 
58125a0866cSChuck Lever 	/* nfsdata */
58225a0866cSChuck Lever 	*p = cpu_to_be32(count);
58325a0866cSChuck Lever 	xdr_write_pages(xdr, args->pages, args->pgbase, count);
58425a0866cSChuck Lever }
58525a0866cSChuck Lever 
58625a0866cSChuck Lever static int nfs2_xdr_enc_writeargs(struct rpc_rqst *req, __be32 *p,
58725a0866cSChuck Lever 				  const struct nfs_writeargs *args)
58825a0866cSChuck Lever {
58925a0866cSChuck Lever 	struct xdr_stream xdr;
59025a0866cSChuck Lever 
59125a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
59225a0866cSChuck Lever 	encode_writeargs(&xdr, args);
59325a0866cSChuck Lever 	xdr.buf->flags |= XDRBUF_WRITE;
59425a0866cSChuck Lever 	return 0;
59525a0866cSChuck Lever }
59625a0866cSChuck Lever 
59725a0866cSChuck Lever /*
5981da177e4SLinus Torvalds  * Encode create arguments
5991da177e4SLinus Torvalds  * CREATE, MKDIR
6001da177e4SLinus Torvalds  */
6011da177e4SLinus Torvalds static int
6029d787a75SAl Viro nfs_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs_createargs *args)
6031da177e4SLinus Torvalds {
6041da177e4SLinus Torvalds 	p = xdr_encode_fhandle(p, args->fh);
6051da177e4SLinus Torvalds 	p = xdr_encode_array(p, args->name, args->len);
6061da177e4SLinus Torvalds 	p = xdr_encode_sattr(p, args->sattr);
6071da177e4SLinus Torvalds 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
6081da177e4SLinus Torvalds 	return 0;
6091da177e4SLinus Torvalds }
6101da177e4SLinus Torvalds 
6111da177e4SLinus Torvalds /*
61225a0866cSChuck Lever  * 2.2.10.  createargs
61325a0866cSChuck Lever  *
61425a0866cSChuck Lever  *	struct createargs {
61525a0866cSChuck Lever  *		diropargs where;
61625a0866cSChuck Lever  *		sattr attributes;
61725a0866cSChuck Lever  *	};
61825a0866cSChuck Lever  */
61925a0866cSChuck Lever static int nfs2_xdr_enc_createargs(struct rpc_rqst *req, __be32 *p,
62025a0866cSChuck Lever 				   const struct nfs_createargs *args)
62125a0866cSChuck Lever {
62225a0866cSChuck Lever 	struct xdr_stream xdr;
62325a0866cSChuck Lever 
62425a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
62525a0866cSChuck Lever 	encode_diropargs(&xdr, args->fh, args->name, args->len);
62625a0866cSChuck Lever 	encode_sattr(&xdr, args->sattr);
62725a0866cSChuck Lever 	return 0;
62825a0866cSChuck Lever }
62925a0866cSChuck Lever 
63025a0866cSChuck Lever static int nfs2_xdr_enc_removeargs(struct rpc_rqst *req, __be32 *p,
63125a0866cSChuck Lever 				   const struct nfs_removeargs *args)
63225a0866cSChuck Lever {
63325a0866cSChuck Lever 	struct xdr_stream xdr;
63425a0866cSChuck Lever 
63525a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
63625a0866cSChuck Lever 	encode_diropargs(&xdr, args->fh, args->name.name, args->name.len);
63725a0866cSChuck Lever 	return 0;
63825a0866cSChuck Lever }
63925a0866cSChuck Lever 
64025a0866cSChuck Lever /*
6411da177e4SLinus Torvalds  * Encode RENAME arguments
6421da177e4SLinus Torvalds  */
6431da177e4SLinus Torvalds static int
6449d787a75SAl Viro nfs_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args)
6451da177e4SLinus Torvalds {
646920769f0SJeff Layton 	p = xdr_encode_fhandle(p, args->old_dir);
647920769f0SJeff Layton 	p = xdr_encode_array(p, args->old_name->name, args->old_name->len);
648920769f0SJeff Layton 	p = xdr_encode_fhandle(p, args->new_dir);
649920769f0SJeff Layton 	p = xdr_encode_array(p, args->new_name->name, args->new_name->len);
6501da177e4SLinus Torvalds 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
6511da177e4SLinus Torvalds 	return 0;
6521da177e4SLinus Torvalds }
6531da177e4SLinus Torvalds 
6541da177e4SLinus Torvalds /*
65525a0866cSChuck Lever  * 2.2.12.  renameargs
65625a0866cSChuck Lever  *
65725a0866cSChuck Lever  *	struct renameargs {
65825a0866cSChuck Lever  *		diropargs from;
65925a0866cSChuck Lever  *		diropargs to;
66025a0866cSChuck Lever  *	};
66125a0866cSChuck Lever  */
66225a0866cSChuck Lever static int nfs2_xdr_enc_renameargs(struct rpc_rqst *req, __be32 *p,
66325a0866cSChuck Lever 				   const struct nfs_renameargs *args)
66425a0866cSChuck Lever {
66525a0866cSChuck Lever 	const struct qstr *old = args->old_name;
66625a0866cSChuck Lever 	const struct qstr *new = args->new_name;
66725a0866cSChuck Lever 	struct xdr_stream xdr;
66825a0866cSChuck Lever 
66925a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
67025a0866cSChuck Lever 	encode_diropargs(&xdr, args->old_dir, old->name, old->len);
67125a0866cSChuck Lever 	encode_diropargs(&xdr, args->new_dir, new->name, new->len);
67225a0866cSChuck Lever 	return 0;
67325a0866cSChuck Lever }
67425a0866cSChuck Lever 
67525a0866cSChuck Lever /*
6761da177e4SLinus Torvalds  * Encode LINK arguments
6771da177e4SLinus Torvalds  */
6781da177e4SLinus Torvalds static int
6799d787a75SAl Viro nfs_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs_linkargs *args)
6801da177e4SLinus Torvalds {
6811da177e4SLinus Torvalds 	p = xdr_encode_fhandle(p, args->fromfh);
6821da177e4SLinus Torvalds 	p = xdr_encode_fhandle(p, args->tofh);
6831da177e4SLinus Torvalds 	p = xdr_encode_array(p, args->toname, args->tolen);
6841da177e4SLinus Torvalds 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
6851da177e4SLinus Torvalds 	return 0;
6861da177e4SLinus Torvalds }
6871da177e4SLinus Torvalds 
6881da177e4SLinus Torvalds /*
68925a0866cSChuck Lever  * 2.2.13.  linkargs
69025a0866cSChuck Lever  *
69125a0866cSChuck Lever  *	struct linkargs {
69225a0866cSChuck Lever  *		fhandle from;
69325a0866cSChuck Lever  *		diropargs to;
69425a0866cSChuck Lever  *	};
69525a0866cSChuck Lever  */
69625a0866cSChuck Lever static int nfs2_xdr_enc_linkargs(struct rpc_rqst *req, __be32 *p,
69725a0866cSChuck Lever 				 const struct nfs_linkargs *args)
69825a0866cSChuck Lever {
69925a0866cSChuck Lever 	struct xdr_stream xdr;
70025a0866cSChuck Lever 
70125a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
70225a0866cSChuck Lever 	encode_fhandle(&xdr, args->fromfh);
70325a0866cSChuck Lever 	encode_diropargs(&xdr, args->tofh, args->toname, args->tolen);
70425a0866cSChuck Lever 	return 0;
70525a0866cSChuck Lever }
70625a0866cSChuck Lever 
70725a0866cSChuck Lever /*
7081da177e4SLinus Torvalds  * Encode SYMLINK arguments
7091da177e4SLinus Torvalds  */
7101da177e4SLinus Torvalds static int
7119d787a75SAl Viro nfs_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_symlinkargs *args)
7121da177e4SLinus Torvalds {
71394a6d753SChuck Lever 	struct xdr_buf *sndbuf = &req->rq_snd_buf;
71494a6d753SChuck Lever 	size_t pad;
71594a6d753SChuck Lever 
7161da177e4SLinus Torvalds 	p = xdr_encode_fhandle(p, args->fromfh);
7171da177e4SLinus Torvalds 	p = xdr_encode_array(p, args->fromname, args->fromlen);
71894a6d753SChuck Lever 	*p++ = htonl(args->pathlen);
71994a6d753SChuck Lever 	sndbuf->len = xdr_adjust_iovec(sndbuf->head, p);
72094a6d753SChuck Lever 
72194a6d753SChuck Lever 	xdr_encode_pages(sndbuf, args->pages, 0, args->pathlen);
72294a6d753SChuck Lever 
72394a6d753SChuck Lever 	/*
72494a6d753SChuck Lever 	 * xdr_encode_pages may have added a few bytes to ensure the
72594a6d753SChuck Lever 	 * pathname ends on a 4-byte boundary.  Start encoding the
72694a6d753SChuck Lever 	 * attributes after the pad bytes.
72794a6d753SChuck Lever 	 */
72894a6d753SChuck Lever 	pad = sndbuf->tail->iov_len;
72994a6d753SChuck Lever 	if (pad > 0)
73094a6d753SChuck Lever 		p++;
7311da177e4SLinus Torvalds 	p = xdr_encode_sattr(p, args->sattr);
73294a6d753SChuck Lever 	sndbuf->len += xdr_adjust_iovec(sndbuf->tail, p) - pad;
7331da177e4SLinus Torvalds 	return 0;
7341da177e4SLinus Torvalds }
7351da177e4SLinus Torvalds 
7361da177e4SLinus Torvalds /*
73725a0866cSChuck Lever  * 2.2.14.  symlinkargs
73825a0866cSChuck Lever  *
73925a0866cSChuck Lever  *	struct symlinkargs {
74025a0866cSChuck Lever  *		diropargs from;
74125a0866cSChuck Lever  *		path to;
74225a0866cSChuck Lever  *		sattr attributes;
74325a0866cSChuck Lever  *	};
74425a0866cSChuck Lever  */
74525a0866cSChuck Lever static int nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req, __be32 *p,
74625a0866cSChuck Lever 				    const struct nfs_symlinkargs *args)
74725a0866cSChuck Lever {
74825a0866cSChuck Lever 	struct xdr_stream xdr;
74925a0866cSChuck Lever 
75025a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
75125a0866cSChuck Lever 	encode_diropargs(&xdr, args->fromfh, args->fromname, args->fromlen);
75225a0866cSChuck Lever 	encode_path(&xdr, args->pages, args->pathlen);
75325a0866cSChuck Lever 	encode_sattr(&xdr, args->sattr);
75425a0866cSChuck Lever 	return 0;
75525a0866cSChuck Lever }
75625a0866cSChuck Lever 
75725a0866cSChuck Lever /*
7581da177e4SLinus Torvalds  * Encode arguments to readdir call
7591da177e4SLinus Torvalds  */
7601da177e4SLinus Torvalds static int
7619d787a75SAl Viro nfs_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs_readdirargs *args)
7621da177e4SLinus Torvalds {
763a17c2153STrond Myklebust 	struct rpc_auth	*auth = req->rq_cred->cr_auth;
7641da177e4SLinus Torvalds 	unsigned int replen;
7651da177e4SLinus Torvalds 	u32 count = args->count;
7661da177e4SLinus Torvalds 
7671da177e4SLinus Torvalds 	p = xdr_encode_fhandle(p, args->fh);
7681da177e4SLinus Torvalds 	*p++ = htonl(args->cookie);
7691da177e4SLinus Torvalds 	*p++ = htonl(count); /* see above */
7701da177e4SLinus Torvalds 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
7711da177e4SLinus Torvalds 
7721da177e4SLinus Torvalds 	/* Inline the page array */
7731da177e4SLinus Torvalds 	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2;
7741da177e4SLinus Torvalds 	xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count);
7751da177e4SLinus Torvalds 	return 0;
7761da177e4SLinus Torvalds }
7771da177e4SLinus Torvalds 
7781da177e4SLinus Torvalds /*
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 
79925a0866cSChuck Lever static int nfs2_xdr_enc_readdirargs(struct rpc_rqst *req, __be32 *p,
80025a0866cSChuck Lever 				    const struct nfs_readdirargs *args)
80125a0866cSChuck Lever {
80225a0866cSChuck Lever 	struct xdr_stream xdr;
80325a0866cSChuck Lever 
80425a0866cSChuck Lever 	xdr_init_encode(&xdr, &req->rq_snd_buf, p);
80525a0866cSChuck Lever 	encode_readdirargs(&xdr, args);
80625a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, 0,
80725a0866cSChuck Lever 					args->count, NFS_readdirres_sz);
80825a0866cSChuck Lever 	return 0;
80925a0866cSChuck Lever }
81025a0866cSChuck Lever 
81125a0866cSChuck Lever /*
8121da177e4SLinus Torvalds  * Decode the result of a readdir call.
8131da177e4SLinus Torvalds  * We're not really decoding anymore, we just leave the buffer untouched
8141da177e4SLinus Torvalds  * and only check that it is syntactically correct.
8151da177e4SLinus Torvalds  * The real decoding happens in nfs_decode_entry below, called directly
8161da177e4SLinus Torvalds  * from nfs_readdir for each entry.
8171da177e4SLinus Torvalds  */
8181da177e4SLinus Torvalds static int
8199d787a75SAl Viro nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy)
8201da177e4SLinus Torvalds {
8211da177e4SLinus Torvalds 	struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
8221da177e4SLinus Torvalds 	struct kvec *iov = rcvbuf->head;
8231da177e4SLinus Torvalds 	struct page **page;
8246232dbbcSChuck Lever 	size_t hdrlen;
8256232dbbcSChuck Lever 	unsigned int pglen, recvd;
826ac396128STrond Myklebust 	int status;
8271da177e4SLinus Torvalds 
8281da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
829856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
8301da177e4SLinus Torvalds 
8311da177e4SLinus Torvalds 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
8321da177e4SLinus Torvalds 	if (iov->iov_len < hdrlen) {
833fe82a183SChuck Lever 		dprintk("NFS: READDIR reply header overflowed:"
8346232dbbcSChuck Lever 				"length %Zu > %Zu\n", hdrlen, iov->iov_len);
8351da177e4SLinus Torvalds 		return -errno_NFSERR_IO;
8361da177e4SLinus Torvalds 	} else if (iov->iov_len != hdrlen) {
8371da177e4SLinus Torvalds 		dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
8381da177e4SLinus Torvalds 		xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
8391da177e4SLinus Torvalds 	}
8401da177e4SLinus Torvalds 
8411da177e4SLinus Torvalds 	pglen = rcvbuf->page_len;
8421da177e4SLinus Torvalds 	recvd = rcvbuf->len - hdrlen;
8431da177e4SLinus Torvalds 	if (pglen > recvd)
8441da177e4SLinus Torvalds 		pglen = recvd;
8451da177e4SLinus Torvalds 	page = rcvbuf->pages;
846ac396128STrond Myklebust 	return pglen;
8471da177e4SLinus Torvalds }
8481da177e4SLinus Torvalds 
849babddc72SBryan Schumaker static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
8501da177e4SLinus Torvalds {
851babddc72SBryan Schumaker 	dprintk("nfs: %s: prematurely hit end of receive buffer. "
852babddc72SBryan Schumaker 		"Remaining buffer length is %tu words.\n",
853babddc72SBryan Schumaker 		func, xdr->end - xdr->p);
854babddc72SBryan Schumaker }
855babddc72SBryan Schumaker 
856babddc72SBryan Schumaker __be32 *
85782f2e547SBryan Schumaker nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus)
858babddc72SBryan Schumaker {
859babddc72SBryan Schumaker 	__be32 *p;
860babddc72SBryan Schumaker 	p = xdr_inline_decode(xdr, 4);
861babddc72SBryan Schumaker 	if (unlikely(!p))
862babddc72SBryan Schumaker 		goto out_overflow;
863babddc72SBryan Schumaker 	if (!ntohl(*p++)) {
864babddc72SBryan Schumaker 		p = xdr_inline_decode(xdr, 4);
865babddc72SBryan Schumaker 		if (unlikely(!p))
866babddc72SBryan Schumaker 			goto out_overflow;
867babddc72SBryan Schumaker 		if (!ntohl(*p++))
8681da177e4SLinus Torvalds 			return ERR_PTR(-EAGAIN);
8691da177e4SLinus Torvalds 		entry->eof = 1;
8701da177e4SLinus Torvalds 		return ERR_PTR(-EBADCOOKIE);
8711da177e4SLinus Torvalds 	}
8721da177e4SLinus Torvalds 
873babddc72SBryan Schumaker 	p = xdr_inline_decode(xdr, 8);
874babddc72SBryan Schumaker 	if (unlikely(!p))
875babddc72SBryan Schumaker 		goto out_overflow;
876babddc72SBryan Schumaker 
8771da177e4SLinus Torvalds 	entry->ino	  = ntohl(*p++);
8781da177e4SLinus Torvalds 	entry->len	  = ntohl(*p++);
879babddc72SBryan Schumaker 
880babddc72SBryan Schumaker 	p = xdr_inline_decode(xdr, entry->len + 4);
881babddc72SBryan Schumaker 	if (unlikely(!p))
882babddc72SBryan Schumaker 		goto out_overflow;
8831da177e4SLinus Torvalds 	entry->name	  = (const char *) p;
8841da177e4SLinus Torvalds 	p		 += XDR_QUADLEN(entry->len);
8851da177e4SLinus Torvalds 	entry->prev_cookie	  = entry->cookie;
8861da177e4SLinus Torvalds 	entry->cookie	  = ntohl(*p++);
887babddc72SBryan Schumaker 
8880b26a0bfSTrond Myklebust 	entry->d_type = DT_UNKNOWN;
8890b26a0bfSTrond Myklebust 
890babddc72SBryan Schumaker 	p = xdr_inline_peek(xdr, 8);
891babddc72SBryan Schumaker 	if (p != NULL)
8921da177e4SLinus Torvalds 		entry->eof = !p[0] && p[1];
893babddc72SBryan Schumaker 	else
894babddc72SBryan Schumaker 		entry->eof = 0;
8951da177e4SLinus Torvalds 
8961da177e4SLinus Torvalds 	return p;
897babddc72SBryan Schumaker 
898babddc72SBryan Schumaker out_overflow:
899babddc72SBryan Schumaker 	print_overflow_msg(__func__, xdr);
900463a376eSTrond Myklebust 	return ERR_PTR(-EAGAIN);
9011da177e4SLinus Torvalds }
9021da177e4SLinus Torvalds 
9031da177e4SLinus Torvalds /*
9041da177e4SLinus Torvalds  * NFS XDR decode functions
9051da177e4SLinus Torvalds  */
9061da177e4SLinus Torvalds /*
9071da177e4SLinus Torvalds  * Decode simple status reply
9081da177e4SLinus Torvalds  */
9091da177e4SLinus Torvalds static int
9109d787a75SAl Viro nfs_xdr_stat(struct rpc_rqst *req, __be32 *p, void *dummy)
9111da177e4SLinus Torvalds {
9121da177e4SLinus Torvalds 	int	status;
9131da177e4SLinus Torvalds 
9141da177e4SLinus Torvalds 	if ((status = ntohl(*p++)) != 0)
915856dff3dSBenny Halevy 		status = nfs_stat_to_errno(status);
9161da177e4SLinus Torvalds 	return status;
9171da177e4SLinus Torvalds }
9181da177e4SLinus Torvalds 
9191da177e4SLinus Torvalds /*
9201da177e4SLinus Torvalds  * Decode attrstat reply
9211da177e4SLinus Torvalds  * GETATTR, SETATTR, WRITE
9221da177e4SLinus Torvalds  */
9231da177e4SLinus Torvalds static int
9249d787a75SAl Viro nfs_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
9251da177e4SLinus Torvalds {
9261da177e4SLinus Torvalds 	int	status;
9271da177e4SLinus Torvalds 
9281da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
929856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
9301da177e4SLinus Torvalds 	xdr_decode_fattr(p, fattr);
9311da177e4SLinus Torvalds 	return 0;
9321da177e4SLinus Torvalds }
9331da177e4SLinus Torvalds 
9341da177e4SLinus Torvalds /*
9351da177e4SLinus Torvalds  * Decode diropres reply
9361da177e4SLinus Torvalds  * LOOKUP, CREATE, MKDIR
9371da177e4SLinus Torvalds  */
9381da177e4SLinus Torvalds static int
9399d787a75SAl Viro nfs_xdr_diropres(struct rpc_rqst *req, __be32 *p, struct nfs_diropok *res)
9401da177e4SLinus Torvalds {
9411da177e4SLinus Torvalds 	int	status;
9421da177e4SLinus Torvalds 
9431da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
944856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
9451da177e4SLinus Torvalds 	p = xdr_decode_fhandle(p, res->fh);
9461da177e4SLinus Torvalds 	xdr_decode_fattr(p, res->fattr);
9471da177e4SLinus Torvalds 	return 0;
9481da177e4SLinus Torvalds }
9491da177e4SLinus Torvalds 
9501da177e4SLinus Torvalds /*
9511da177e4SLinus Torvalds  * Encode READLINK args
9521da177e4SLinus Torvalds  */
9531da177e4SLinus Torvalds static int
9549d787a75SAl Viro nfs_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_readlinkargs *args)
9551da177e4SLinus Torvalds {
956a17c2153STrond Myklebust 	struct rpc_auth	*auth = req->rq_cred->cr_auth;
9571da177e4SLinus Torvalds 	unsigned int replen;
9581da177e4SLinus Torvalds 
9591da177e4SLinus Torvalds 	p = xdr_encode_fhandle(p, args->fh);
9601da177e4SLinus Torvalds 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
9611da177e4SLinus Torvalds 
9621da177e4SLinus Torvalds 	/* Inline the page array */
9631da177e4SLinus Torvalds 	replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2;
9641da177e4SLinus Torvalds 	xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen);
9651da177e4SLinus Torvalds 	return 0;
9661da177e4SLinus Torvalds }
9671da177e4SLinus Torvalds 
9681da177e4SLinus Torvalds /*
9691da177e4SLinus Torvalds  * Decode READLINK reply
9701da177e4SLinus Torvalds  */
9711da177e4SLinus Torvalds static int
9729d787a75SAl Viro nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy)
9731da177e4SLinus Torvalds {
9741da177e4SLinus Torvalds 	struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
9751da177e4SLinus Torvalds 	struct kvec *iov = rcvbuf->head;
9766232dbbcSChuck Lever 	size_t hdrlen;
9776232dbbcSChuck Lever 	u32 len, recvd;
9781da177e4SLinus Torvalds 	int	status;
9791da177e4SLinus Torvalds 
9801da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
981856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
9821da177e4SLinus Torvalds 	/* Convert length of symlink */
9831da177e4SLinus Torvalds 	len = ntohl(*p++);
9846232dbbcSChuck Lever 	if (len >= rcvbuf->page_len) {
985fe82a183SChuck Lever 		dprintk("nfs: server returned giant symlink!\n");
9861da177e4SLinus Torvalds 		return -ENAMETOOLONG;
9871da177e4SLinus Torvalds 	}
9881da177e4SLinus Torvalds 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
9891da177e4SLinus Torvalds 	if (iov->iov_len < hdrlen) {
990fe82a183SChuck Lever 		dprintk("NFS: READLINK reply header overflowed:"
9916232dbbcSChuck Lever 				"length %Zu > %Zu\n", hdrlen, iov->iov_len);
9921da177e4SLinus Torvalds 		return -errno_NFSERR_IO;
9931da177e4SLinus Torvalds 	} else if (iov->iov_len != hdrlen) {
9941da177e4SLinus Torvalds 		dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
9951da177e4SLinus Torvalds 		xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
9961da177e4SLinus Torvalds 	}
9971da177e4SLinus Torvalds 	recvd = req->rq_rcv_buf.len - hdrlen;
9981da177e4SLinus Torvalds 	if (recvd < len) {
999fe82a183SChuck Lever 		dprintk("NFS: server cheating in readlink reply: "
10001da177e4SLinus Torvalds 				"count %u > recvd %u\n", len, recvd);
10011da177e4SLinus Torvalds 		return -EIO;
10021da177e4SLinus Torvalds 	}
10031da177e4SLinus Torvalds 
1004b4687da7SChuck Lever 	xdr_terminate_string(rcvbuf, len);
10051da177e4SLinus Torvalds 	return 0;
10061da177e4SLinus Torvalds }
10071da177e4SLinus Torvalds 
10081da177e4SLinus Torvalds /*
10091da177e4SLinus Torvalds  * Decode WRITE reply
10101da177e4SLinus Torvalds  */
10111da177e4SLinus Torvalds static int
10129d787a75SAl Viro nfs_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
10131da177e4SLinus Torvalds {
10141da177e4SLinus Torvalds 	res->verf->committed = NFS_FILE_SYNC;
10151da177e4SLinus Torvalds 	return nfs_xdr_attrstat(req, p, res->fattr);
10161da177e4SLinus Torvalds }
10171da177e4SLinus Torvalds 
10181da177e4SLinus Torvalds /*
10191da177e4SLinus Torvalds  * Decode STATFS reply
10201da177e4SLinus Torvalds  */
10211da177e4SLinus Torvalds static int
10229d787a75SAl Viro nfs_xdr_statfsres(struct rpc_rqst *req, __be32 *p, struct nfs2_fsstat *res)
10231da177e4SLinus Torvalds {
10241da177e4SLinus Torvalds 	int	status;
10251da177e4SLinus Torvalds 
10261da177e4SLinus Torvalds 	if ((status = ntohl(*p++)))
1027856dff3dSBenny Halevy 		return nfs_stat_to_errno(status);
10281da177e4SLinus Torvalds 
10291da177e4SLinus Torvalds 	res->tsize  = ntohl(*p++);
10301da177e4SLinus Torvalds 	res->bsize  = ntohl(*p++);
10311da177e4SLinus Torvalds 	res->blocks = ntohl(*p++);
10321da177e4SLinus Torvalds 	res->bfree  = ntohl(*p++);
10331da177e4SLinus Torvalds 	res->bavail = ntohl(*p++);
10341da177e4SLinus Torvalds 	return 0;
10351da177e4SLinus Torvalds }
10361da177e4SLinus Torvalds 
10371da177e4SLinus Torvalds /*
10381da177e4SLinus Torvalds  * We need to translate between nfs status return values and
10391da177e4SLinus Torvalds  * the local errno values which may not be the same.
10401da177e4SLinus Torvalds  */
10411da177e4SLinus Torvalds static struct {
10421da177e4SLinus Torvalds 	int stat;
10431da177e4SLinus Torvalds 	int errno;
10441da177e4SLinus Torvalds } nfs_errtbl[] = {
10451da177e4SLinus Torvalds 	{ NFS_OK,		0		},
1046856dff3dSBenny Halevy 	{ NFSERR_PERM,		-EPERM		},
1047856dff3dSBenny Halevy 	{ NFSERR_NOENT,		-ENOENT		},
1048856dff3dSBenny Halevy 	{ NFSERR_IO,		-errno_NFSERR_IO},
1049856dff3dSBenny Halevy 	{ NFSERR_NXIO,		-ENXIO		},
1050856dff3dSBenny Halevy /*	{ NFSERR_EAGAIN,	-EAGAIN		}, */
1051856dff3dSBenny Halevy 	{ NFSERR_ACCES,		-EACCES		},
1052856dff3dSBenny Halevy 	{ NFSERR_EXIST,		-EEXIST		},
1053856dff3dSBenny Halevy 	{ NFSERR_XDEV,		-EXDEV		},
1054856dff3dSBenny Halevy 	{ NFSERR_NODEV,		-ENODEV		},
1055856dff3dSBenny Halevy 	{ NFSERR_NOTDIR,	-ENOTDIR	},
1056856dff3dSBenny Halevy 	{ NFSERR_ISDIR,		-EISDIR		},
1057856dff3dSBenny Halevy 	{ NFSERR_INVAL,		-EINVAL		},
1058856dff3dSBenny Halevy 	{ NFSERR_FBIG,		-EFBIG		},
1059856dff3dSBenny Halevy 	{ NFSERR_NOSPC,		-ENOSPC		},
1060856dff3dSBenny Halevy 	{ NFSERR_ROFS,		-EROFS		},
1061856dff3dSBenny Halevy 	{ NFSERR_MLINK,		-EMLINK		},
1062856dff3dSBenny Halevy 	{ NFSERR_NAMETOOLONG,	-ENAMETOOLONG	},
1063856dff3dSBenny Halevy 	{ NFSERR_NOTEMPTY,	-ENOTEMPTY	},
1064856dff3dSBenny Halevy 	{ NFSERR_DQUOT,		-EDQUOT		},
1065856dff3dSBenny Halevy 	{ NFSERR_STALE,		-ESTALE		},
1066856dff3dSBenny Halevy 	{ NFSERR_REMOTE,	-EREMOTE	},
10671da177e4SLinus Torvalds #ifdef EWFLUSH
1068856dff3dSBenny Halevy 	{ NFSERR_WFLUSH,	-EWFLUSH	},
10691da177e4SLinus Torvalds #endif
1070856dff3dSBenny Halevy 	{ NFSERR_BADHANDLE,	-EBADHANDLE	},
1071856dff3dSBenny Halevy 	{ NFSERR_NOT_SYNC,	-ENOTSYNC	},
1072856dff3dSBenny Halevy 	{ NFSERR_BAD_COOKIE,	-EBADCOOKIE	},
1073856dff3dSBenny Halevy 	{ NFSERR_NOTSUPP,	-ENOTSUPP	},
1074856dff3dSBenny Halevy 	{ NFSERR_TOOSMALL,	-ETOOSMALL	},
1075fdcb4577STrond Myklebust 	{ NFSERR_SERVERFAULT,	-EREMOTEIO	},
1076856dff3dSBenny Halevy 	{ NFSERR_BADTYPE,	-EBADTYPE	},
1077856dff3dSBenny Halevy 	{ NFSERR_JUKEBOX,	-EJUKEBOX	},
1078856dff3dSBenny Halevy 	{ -1,			-EIO		}
10791da177e4SLinus Torvalds };
10801da177e4SLinus Torvalds 
10811da177e4SLinus Torvalds /*
10821da177e4SLinus Torvalds  * Convert an NFS error code to a local one.
10831da177e4SLinus Torvalds  * This one is used jointly by NFSv2 and NFSv3.
10841da177e4SLinus Torvalds  */
10851da177e4SLinus Torvalds int
10861da177e4SLinus Torvalds nfs_stat_to_errno(int stat)
10871da177e4SLinus Torvalds {
10881da177e4SLinus Torvalds 	int i;
10891da177e4SLinus Torvalds 
10901da177e4SLinus Torvalds 	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
10911da177e4SLinus Torvalds 		if (nfs_errtbl[i].stat == stat)
10921da177e4SLinus Torvalds 			return nfs_errtbl[i].errno;
10931da177e4SLinus Torvalds 	}
1094fe82a183SChuck Lever 	dprintk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat);
10951da177e4SLinus Torvalds 	return nfs_errtbl[i].errno;
10961da177e4SLinus Torvalds }
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds #define PROC(proc, argtype, restype, timer)				\
10991da177e4SLinus Torvalds [NFSPROC_##proc] = {							\
11001da177e4SLinus Torvalds 	.p_proc	    =  NFSPROC_##proc,					\
110125a0866cSChuck Lever 	.p_encode   =  (kxdrproc_t)nfs2_xdr_enc_##argtype,		\
11021da177e4SLinus Torvalds 	.p_decode   =  (kxdrproc_t) nfs_xdr_##restype,			\
11032bea90d4SChuck Lever 	.p_arglen   =  NFS_##argtype##_sz,				\
11042bea90d4SChuck Lever 	.p_replen   =  NFS_##restype##_sz,				\
1105cc0175c1SChuck Lever 	.p_timer    =  timer,						\
1106cc0175c1SChuck Lever 	.p_statidx  =  NFSPROC_##proc,					\
1107cc0175c1SChuck Lever 	.p_name     =  #proc,						\
11081da177e4SLinus Torvalds 	}
11091da177e4SLinus Torvalds struct rpc_procinfo	nfs_procedures[] = {
11101da177e4SLinus Torvalds     PROC(GETATTR,	fhandle,	attrstat, 1),
11111da177e4SLinus Torvalds     PROC(SETATTR,	sattrargs,	attrstat, 0),
11121da177e4SLinus Torvalds     PROC(LOOKUP,	diropargs,	diropres, 2),
11131da177e4SLinus Torvalds     PROC(READLINK,	readlinkargs,	readlinkres, 3),
11141da177e4SLinus Torvalds     PROC(READ,		readargs,	readres, 3),
11151da177e4SLinus Torvalds     PROC(WRITE,		writeargs,	writeres, 4),
11161da177e4SLinus Torvalds     PROC(CREATE,	createargs,	diropres, 0),
11174fdc17b2STrond Myklebust     PROC(REMOVE,	removeargs,	stat, 0),
11181da177e4SLinus Torvalds     PROC(RENAME,	renameargs,	stat, 0),
11191da177e4SLinus Torvalds     PROC(LINK,		linkargs,	stat, 0),
11201da177e4SLinus Torvalds     PROC(SYMLINK,	symlinkargs,	stat, 0),
11211da177e4SLinus Torvalds     PROC(MKDIR,		createargs,	diropres, 0),
11221da177e4SLinus Torvalds     PROC(RMDIR,		diropargs,	stat, 0),
11231da177e4SLinus Torvalds     PROC(READDIR,	readdirargs,	readdirres, 3),
11241da177e4SLinus Torvalds     PROC(STATFS,	fhandle,	statfsres, 0),
11251da177e4SLinus Torvalds };
11261da177e4SLinus Torvalds 
11271da177e4SLinus Torvalds struct rpc_version		nfs_version2 = {
11281da177e4SLinus Torvalds 	.number			= 2,
1129e8c96f8cSTobias Klauser 	.nrprocs		= ARRAY_SIZE(nfs_procedures),
11301da177e4SLinus Torvalds 	.procs			= nfs_procedures
11311da177e4SLinus Torvalds };
1132