xref: /openbmc/linux/fs/nfs/nfs2xdr.c (revision 64bd577e)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/fs/nfs/nfs2xdr.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * XDR functions to encode/decode NFS RPC arguments and results.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Copyright (C) 1992, 1993, 1994  Rick Sladkey
71da177e4SLinus Torvalds  * Copyright (C) 1996 Olaf Kirch
81da177e4SLinus Torvalds  * 04 Aug 1998  Ion Badulescu <ionut@cs.columbia.edu>
91da177e4SLinus Torvalds  * 		FIFO's need special handling in NFSv2
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/param.h>
131da177e4SLinus Torvalds #include <linux/time.h>
141da177e4SLinus Torvalds #include <linux/mm.h>
151da177e4SLinus Torvalds #include <linux/errno.h>
161da177e4SLinus Torvalds #include <linux/string.h>
171da177e4SLinus Torvalds #include <linux/in.h>
181da177e4SLinus Torvalds #include <linux/pagemap.h>
191da177e4SLinus Torvalds #include <linux/proc_fs.h>
201da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
211da177e4SLinus Torvalds #include <linux/nfs.h>
221da177e4SLinus Torvalds #include <linux/nfs2.h>
231da177e4SLinus Torvalds #include <linux/nfs_fs.h>
24816724e6STrond Myklebust #include "internal.h"
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds #define NFSDBG_FACILITY		NFSDBG_XDR
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds /* Mapping from NFS error code to "errno" error code. */
291da177e4SLinus Torvalds #define errno_NFSERR_IO		EIO
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds /*
321da177e4SLinus Torvalds  * Declare the space requirements for NFS arguments and replies as
331da177e4SLinus Torvalds  * number of 32bit-words
341da177e4SLinus Torvalds  */
351da177e4SLinus Torvalds #define NFS_fhandle_sz		(8)
361da177e4SLinus Torvalds #define NFS_sattr_sz		(8)
371da177e4SLinus Torvalds #define NFS_filename_sz		(1+(NFS2_MAXNAMLEN>>2))
381da177e4SLinus Torvalds #define NFS_path_sz		(1+(NFS2_MAXPATHLEN>>2))
391da177e4SLinus Torvalds #define NFS_fattr_sz		(17)
401da177e4SLinus Torvalds #define NFS_info_sz		(5)
411da177e4SLinus Torvalds #define NFS_entry_sz		(NFS_filename_sz+3)
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds #define NFS_diropargs_sz	(NFS_fhandle_sz+NFS_filename_sz)
444fdc17b2STrond Myklebust #define NFS_removeargs_sz	(NFS_fhandle_sz+NFS_filename_sz)
451da177e4SLinus Torvalds #define NFS_sattrargs_sz	(NFS_fhandle_sz+NFS_sattr_sz)
461da177e4SLinus Torvalds #define NFS_readlinkargs_sz	(NFS_fhandle_sz)
471da177e4SLinus Torvalds #define NFS_readargs_sz		(NFS_fhandle_sz+3)
481da177e4SLinus Torvalds #define NFS_writeargs_sz	(NFS_fhandle_sz+4)
491da177e4SLinus Torvalds #define NFS_createargs_sz	(NFS_diropargs_sz+NFS_sattr_sz)
501da177e4SLinus Torvalds #define NFS_renameargs_sz	(NFS_diropargs_sz+NFS_diropargs_sz)
511da177e4SLinus Torvalds #define NFS_linkargs_sz		(NFS_fhandle_sz+NFS_diropargs_sz)
5294a6d753SChuck Lever #define NFS_symlinkargs_sz	(NFS_diropargs_sz+1+NFS_sattr_sz)
531da177e4SLinus Torvalds #define NFS_readdirargs_sz	(NFS_fhandle_sz+2)
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds #define NFS_attrstat_sz		(1+NFS_fattr_sz)
561da177e4SLinus Torvalds #define NFS_diropres_sz		(1+NFS_fhandle_sz+NFS_fattr_sz)
571da177e4SLinus Torvalds #define NFS_readlinkres_sz	(2)
581da177e4SLinus Torvalds #define NFS_readres_sz		(1+NFS_fattr_sz+1)
591da177e4SLinus Torvalds #define NFS_writeres_sz         (NFS_attrstat_sz)
601da177e4SLinus Torvalds #define NFS_stat_sz		(1)
611da177e4SLinus Torvalds #define NFS_readdirres_sz	(1)
621da177e4SLinus Torvalds #define NFS_statfsres_sz	(1+NFS_info_sz)
631da177e4SLinus Torvalds 
645e7e5a0dSBryan Schumaker static int nfs_stat_to_errno(enum nfs_stat);
6525a0866cSChuck Lever 
6625a0866cSChuck Lever /*
6725a0866cSChuck Lever  * While encoding arguments, set up the reply buffer in advance to
6825a0866cSChuck Lever  * receive reply data directly into the page cache.
6925a0866cSChuck Lever  */
7025a0866cSChuck Lever static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages,
7125a0866cSChuck Lever 				 unsigned int base, unsigned int len,
7225a0866cSChuck Lever 				 unsigned int bufsize)
7325a0866cSChuck Lever {
7425a0866cSChuck Lever 	struct rpc_auth	*auth = req->rq_cred->cr_auth;
7525a0866cSChuck Lever 	unsigned int replen;
7625a0866cSChuck Lever 
7725a0866cSChuck Lever 	replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize;
7825a0866cSChuck Lever 	xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len);
7925a0866cSChuck Lever }
8025a0866cSChuck Lever 
81f796f8b3SChuck Lever /*
82f796f8b3SChuck Lever  * Handle decode buffer overflows out-of-line.
83f796f8b3SChuck Lever  */
84f796f8b3SChuck Lever static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
85f796f8b3SChuck Lever {
86f796f8b3SChuck Lever 	dprintk("NFS: %s prematurely hit the end of our receive buffer. "
87f796f8b3SChuck Lever 		"Remaining buffer length is %tu words.\n",
88f796f8b3SChuck Lever 		func, xdr->end - xdr->p);
89f796f8b3SChuck Lever }
90f796f8b3SChuck Lever 
9125a0866cSChuck Lever 
921da177e4SLinus Torvalds /*
9325a0866cSChuck Lever  * Encode/decode NFSv2 basic data types
9425a0866cSChuck Lever  *
9525a0866cSChuck Lever  * Basic NFSv2 data types are defined in section 2.3 of RFC 1094:
9625a0866cSChuck Lever  * "NFS: Network File System Protocol Specification".
9725a0866cSChuck Lever  *
9825a0866cSChuck Lever  * Not all basic data types have their own encoding and decoding
9925a0866cSChuck Lever  * functions.  For run-time efficiency, some data types are encoded
10025a0866cSChuck Lever  * or decoded inline.
10125a0866cSChuck Lever  */
10225a0866cSChuck Lever 
10325a0866cSChuck Lever /*
104f796f8b3SChuck Lever  *	typedef opaque	nfsdata<>;
105f796f8b3SChuck Lever  */
106f796f8b3SChuck Lever static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result)
107f796f8b3SChuck Lever {
108f796f8b3SChuck Lever 	u32 recvd, count;
109f796f8b3SChuck Lever 	__be32 *p;
110f796f8b3SChuck Lever 
111f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
112f796f8b3SChuck Lever 	if (unlikely(p == NULL))
113f796f8b3SChuck Lever 		goto out_overflow;
114f796f8b3SChuck Lever 	count = be32_to_cpup(p);
11564bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, count);
116f796f8b3SChuck Lever 	if (unlikely(count > recvd))
117f796f8b3SChuck Lever 		goto out_cheating;
118f796f8b3SChuck Lever out:
119f796f8b3SChuck Lever 	result->eof = 0;	/* NFSv2 does not pass EOF flag on the wire. */
120f796f8b3SChuck Lever 	result->count = count;
121f796f8b3SChuck Lever 	return count;
122f796f8b3SChuck Lever out_cheating:
123f796f8b3SChuck Lever 	dprintk("NFS: server cheating in read result: "
124f796f8b3SChuck Lever 		"count %u > recvd %u\n", count, recvd);
125f796f8b3SChuck Lever 	count = recvd;
126f796f8b3SChuck Lever 	goto out;
127f796f8b3SChuck Lever out_overflow:
128f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
129f796f8b3SChuck Lever 	return -EIO;
130f796f8b3SChuck Lever }
131f796f8b3SChuck Lever 
132f796f8b3SChuck Lever /*
133f796f8b3SChuck Lever  *	enum stat {
134f796f8b3SChuck Lever  *		NFS_OK = 0,
135f796f8b3SChuck Lever  *		NFSERR_PERM = 1,
136f796f8b3SChuck Lever  *		NFSERR_NOENT = 2,
137f796f8b3SChuck Lever  *		NFSERR_IO = 5,
138f796f8b3SChuck Lever  *		NFSERR_NXIO = 6,
139f796f8b3SChuck Lever  *		NFSERR_ACCES = 13,
140f796f8b3SChuck Lever  *		NFSERR_EXIST = 17,
141f796f8b3SChuck Lever  *		NFSERR_NODEV = 19,
142f796f8b3SChuck Lever  *		NFSERR_NOTDIR = 20,
143f796f8b3SChuck Lever  *		NFSERR_ISDIR = 21,
144f796f8b3SChuck Lever  *		NFSERR_FBIG = 27,
145f796f8b3SChuck Lever  *		NFSERR_NOSPC = 28,
146f796f8b3SChuck Lever  *		NFSERR_ROFS = 30,
147f796f8b3SChuck Lever  *		NFSERR_NAMETOOLONG = 63,
148f796f8b3SChuck Lever  *		NFSERR_NOTEMPTY = 66,
149f796f8b3SChuck Lever  *		NFSERR_DQUOT = 69,
150f796f8b3SChuck Lever  *		NFSERR_STALE = 70,
151f796f8b3SChuck Lever  *		NFSERR_WFLUSH = 99
152f796f8b3SChuck Lever  *	};
153f796f8b3SChuck Lever  */
154f796f8b3SChuck Lever static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status)
155f796f8b3SChuck Lever {
156f796f8b3SChuck Lever 	__be32 *p;
157f796f8b3SChuck Lever 
158f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
159f796f8b3SChuck Lever 	if (unlikely(p == NULL))
160f796f8b3SChuck Lever 		goto out_overflow;
161f796f8b3SChuck Lever 	*status = be32_to_cpup(p);
162f796f8b3SChuck Lever 	return 0;
163f796f8b3SChuck Lever out_overflow:
164f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
165f796f8b3SChuck Lever 	return -EIO;
166f796f8b3SChuck Lever }
167f796f8b3SChuck Lever 
168f796f8b3SChuck Lever /*
1695f96e5e3SChuck Lever  * 2.3.2.  ftype
1705f96e5e3SChuck Lever  *
1715f96e5e3SChuck Lever  *	enum ftype {
1725f96e5e3SChuck Lever  *		NFNON = 0,
1735f96e5e3SChuck Lever  *		NFREG = 1,
1745f96e5e3SChuck Lever  *		NFDIR = 2,
1755f96e5e3SChuck Lever  *		NFBLK = 3,
1765f96e5e3SChuck Lever  *		NFCHR = 4,
1775f96e5e3SChuck Lever  *		NFLNK = 5
1785f96e5e3SChuck Lever  *	};
1795f96e5e3SChuck Lever  *
1805f96e5e3SChuck Lever  */
1815f96e5e3SChuck Lever static __be32 *xdr_decode_ftype(__be32 *p, u32 *type)
1825f96e5e3SChuck Lever {
1835f96e5e3SChuck Lever 	*type = be32_to_cpup(p++);
1845f96e5e3SChuck Lever 	if (unlikely(*type > NF2FIFO))
1855f96e5e3SChuck Lever 		*type = NFBAD;
1865f96e5e3SChuck Lever 	return p;
1875f96e5e3SChuck Lever }
1885f96e5e3SChuck Lever 
1895f96e5e3SChuck Lever /*
19025a0866cSChuck Lever  * 2.3.3.  fhandle
19125a0866cSChuck Lever  *
19225a0866cSChuck Lever  *	typedef opaque fhandle[FHSIZE];
19325a0866cSChuck Lever  */
19425a0866cSChuck Lever static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh)
19525a0866cSChuck Lever {
19625a0866cSChuck Lever 	__be32 *p;
19725a0866cSChuck Lever 
19825a0866cSChuck Lever 	BUG_ON(fh->size != NFS2_FHSIZE);
19925a0866cSChuck Lever 	p = xdr_reserve_space(xdr, NFS2_FHSIZE);
20025a0866cSChuck Lever 	memcpy(p, fh->data, NFS2_FHSIZE);
20125a0866cSChuck Lever }
20225a0866cSChuck Lever 
203f796f8b3SChuck Lever static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh)
204f796f8b3SChuck Lever {
205f796f8b3SChuck Lever 	__be32 *p;
206f796f8b3SChuck Lever 
207f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS2_FHSIZE);
208f796f8b3SChuck Lever 	if (unlikely(p == NULL))
209f796f8b3SChuck Lever 		goto out_overflow;
210f796f8b3SChuck Lever 	fh->size = NFS2_FHSIZE;
211f796f8b3SChuck Lever 	memcpy(fh->data, p, NFS2_FHSIZE);
212f796f8b3SChuck Lever 	return 0;
213f796f8b3SChuck Lever out_overflow:
214f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
215f796f8b3SChuck Lever 	return -EIO;
216f796f8b3SChuck Lever }
217f796f8b3SChuck Lever 
21825a0866cSChuck Lever /*
219282ac2a5SChuck Lever  * 2.3.4.  timeval
220282ac2a5SChuck Lever  *
221282ac2a5SChuck Lever  *	struct timeval {
222282ac2a5SChuck Lever  *		unsigned int seconds;
223282ac2a5SChuck Lever  *		unsigned int useconds;
224282ac2a5SChuck Lever  *	};
225282ac2a5SChuck Lever  */
226282ac2a5SChuck Lever static __be32 *xdr_encode_time(__be32 *p, const struct timespec *timep)
227282ac2a5SChuck Lever {
228282ac2a5SChuck Lever 	*p++ = cpu_to_be32(timep->tv_sec);
229282ac2a5SChuck Lever 	if (timep->tv_nsec != 0)
230282ac2a5SChuck Lever 		*p++ = cpu_to_be32(timep->tv_nsec / NSEC_PER_USEC);
231282ac2a5SChuck Lever 	else
232282ac2a5SChuck Lever 		*p++ = cpu_to_be32(0);
233282ac2a5SChuck Lever 	return p;
234282ac2a5SChuck Lever }
235282ac2a5SChuck Lever 
236282ac2a5SChuck Lever /*
237282ac2a5SChuck Lever  * Passing the invalid value useconds=1000000 is a Sun convention for
238282ac2a5SChuck Lever  * "set to current server time".  It's needed to make permissions checks
239282ac2a5SChuck Lever  * for the "touch" program across v2 mounts to Solaris and Irix servers
240282ac2a5SChuck Lever  * work correctly.  See description of sattr in section 6.1 of "NFS
241282ac2a5SChuck Lever  * Illustrated" by Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5.
242282ac2a5SChuck Lever  */
243282ac2a5SChuck Lever static __be32 *xdr_encode_current_server_time(__be32 *p,
244282ac2a5SChuck Lever 					      const struct timespec *timep)
245282ac2a5SChuck Lever {
246282ac2a5SChuck Lever 	*p++ = cpu_to_be32(timep->tv_sec);
247282ac2a5SChuck Lever 	*p++ = cpu_to_be32(1000000);
248282ac2a5SChuck Lever 	return p;
249282ac2a5SChuck Lever }
250282ac2a5SChuck Lever 
2515f96e5e3SChuck Lever static __be32 *xdr_decode_time(__be32 *p, struct timespec *timep)
2525f96e5e3SChuck Lever {
2535f96e5e3SChuck Lever 	timep->tv_sec = be32_to_cpup(p++);
2545f96e5e3SChuck Lever 	timep->tv_nsec = be32_to_cpup(p++) * NSEC_PER_USEC;
2555f96e5e3SChuck Lever 	return p;
2565f96e5e3SChuck Lever }
2575f96e5e3SChuck Lever 
258282ac2a5SChuck Lever /*
259f796f8b3SChuck Lever  * 2.3.5.  fattr
260f796f8b3SChuck Lever  *
261f796f8b3SChuck Lever  *	struct fattr {
262f796f8b3SChuck Lever  *		ftype		type;
263f796f8b3SChuck Lever  *		unsigned int	mode;
264f796f8b3SChuck Lever  *		unsigned int	nlink;
265f796f8b3SChuck Lever  *		unsigned int	uid;
266f796f8b3SChuck Lever  *		unsigned int	gid;
267f796f8b3SChuck Lever  *		unsigned int	size;
268f796f8b3SChuck Lever  *		unsigned int	blocksize;
269f796f8b3SChuck Lever  *		unsigned int	rdev;
270f796f8b3SChuck Lever  *		unsigned int	blocks;
271f796f8b3SChuck Lever  *		unsigned int	fsid;
272f796f8b3SChuck Lever  *		unsigned int	fileid;
273f796f8b3SChuck Lever  *		timeval		atime;
274f796f8b3SChuck Lever  *		timeval		mtime;
275f796f8b3SChuck Lever  *		timeval		ctime;
276f796f8b3SChuck Lever  *	};
277f796f8b3SChuck Lever  *
278f796f8b3SChuck Lever  */
279f796f8b3SChuck Lever static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
280f796f8b3SChuck Lever {
2815f96e5e3SChuck Lever 	u32 rdev, type;
282f796f8b3SChuck Lever 	__be32 *p;
283f796f8b3SChuck Lever 
284f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS_fattr_sz << 2);
285f796f8b3SChuck Lever 	if (unlikely(p == NULL))
286f796f8b3SChuck Lever 		goto out_overflow;
2875f96e5e3SChuck Lever 
2885f96e5e3SChuck Lever 	fattr->valid |= NFS_ATTR_FATTR_V2;
2895f96e5e3SChuck Lever 
2905f96e5e3SChuck Lever 	p = xdr_decode_ftype(p, &type);
2915f96e5e3SChuck Lever 
2925f96e5e3SChuck Lever 	fattr->mode = be32_to_cpup(p++);
2935f96e5e3SChuck Lever 	fattr->nlink = be32_to_cpup(p++);
2945f96e5e3SChuck Lever 	fattr->uid = be32_to_cpup(p++);
2955f96e5e3SChuck Lever 	fattr->gid = be32_to_cpup(p++);
2965f96e5e3SChuck Lever 	fattr->size = be32_to_cpup(p++);
2975f96e5e3SChuck Lever 	fattr->du.nfs2.blocksize = be32_to_cpup(p++);
2985f96e5e3SChuck Lever 
2995f96e5e3SChuck Lever 	rdev = be32_to_cpup(p++);
3005f96e5e3SChuck Lever 	fattr->rdev = new_decode_dev(rdev);
3015f96e5e3SChuck Lever 	if (type == (u32)NFCHR && rdev == (u32)NFS2_FIFO_DEV) {
3025f96e5e3SChuck Lever 		fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
3035f96e5e3SChuck Lever 		fattr->rdev = 0;
3045f96e5e3SChuck Lever 	}
3055f96e5e3SChuck Lever 
3065f96e5e3SChuck Lever 	fattr->du.nfs2.blocks = be32_to_cpup(p++);
3075f96e5e3SChuck Lever 	fattr->fsid.major = be32_to_cpup(p++);
3085f96e5e3SChuck Lever 	fattr->fsid.minor = 0;
3095f96e5e3SChuck Lever 	fattr->fileid = be32_to_cpup(p++);
3105f96e5e3SChuck Lever 
3115f96e5e3SChuck Lever 	p = xdr_decode_time(p, &fattr->atime);
3125f96e5e3SChuck Lever 	p = xdr_decode_time(p, &fattr->mtime);
3135f96e5e3SChuck Lever 	xdr_decode_time(p, &fattr->ctime);
3143a1556e8STrond Myklebust 	fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime);
3153a1556e8STrond Myklebust 
316f796f8b3SChuck Lever 	return 0;
317f796f8b3SChuck Lever out_overflow:
318f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
319f796f8b3SChuck Lever 	return -EIO;
320f796f8b3SChuck Lever }
321f796f8b3SChuck Lever 
322f796f8b3SChuck Lever /*
32325a0866cSChuck Lever  * 2.3.6.  sattr
32425a0866cSChuck Lever  *
32525a0866cSChuck Lever  *	struct sattr {
32625a0866cSChuck Lever  *		unsigned int	mode;
32725a0866cSChuck Lever  *		unsigned int	uid;
32825a0866cSChuck Lever  *		unsigned int	gid;
32925a0866cSChuck Lever  *		unsigned int	size;
33025a0866cSChuck Lever  *		timeval		atime;
33125a0866cSChuck Lever  *		timeval		mtime;
33225a0866cSChuck Lever  *	};
33325a0866cSChuck Lever  */
33425a0866cSChuck Lever 
33525a0866cSChuck Lever #define NFS2_SATTR_NOT_SET	(0xffffffff)
33625a0866cSChuck Lever 
33725a0866cSChuck Lever static __be32 *xdr_time_not_set(__be32 *p)
33825a0866cSChuck Lever {
33925a0866cSChuck Lever 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
34025a0866cSChuck Lever 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
34125a0866cSChuck Lever 	return p;
34225a0866cSChuck Lever }
34325a0866cSChuck Lever 
34425a0866cSChuck Lever static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr)
34525a0866cSChuck Lever {
34625a0866cSChuck Lever 	__be32 *p;
34725a0866cSChuck Lever 
34825a0866cSChuck Lever 	p = xdr_reserve_space(xdr, NFS_sattr_sz << 2);
34925a0866cSChuck Lever 
35025a0866cSChuck Lever 	if (attr->ia_valid & ATTR_MODE)
35125a0866cSChuck Lever 		*p++ = cpu_to_be32(attr->ia_mode);
35225a0866cSChuck Lever 	else
35325a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
35425a0866cSChuck Lever 	if (attr->ia_valid & ATTR_UID)
35525a0866cSChuck Lever 		*p++ = cpu_to_be32(attr->ia_uid);
35625a0866cSChuck Lever 	else
35725a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
35825a0866cSChuck Lever 	if (attr->ia_valid & ATTR_GID)
35925a0866cSChuck Lever 		*p++ = cpu_to_be32(attr->ia_gid);
36025a0866cSChuck Lever 	else
36125a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
36225a0866cSChuck Lever 	if (attr->ia_valid & ATTR_SIZE)
36325a0866cSChuck Lever 		*p++ = cpu_to_be32((u32)attr->ia_size);
36425a0866cSChuck Lever 	else
36525a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
36625a0866cSChuck Lever 
36725a0866cSChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET)
36825a0866cSChuck Lever 		p = xdr_encode_time(p, &attr->ia_atime);
36925a0866cSChuck Lever 	else if (attr->ia_valid & ATTR_ATIME)
37025a0866cSChuck Lever 		p = xdr_encode_current_server_time(p, &attr->ia_atime);
37125a0866cSChuck Lever 	else
37225a0866cSChuck Lever 		p = xdr_time_not_set(p);
37325a0866cSChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET)
37425a0866cSChuck Lever 		xdr_encode_time(p, &attr->ia_mtime);
37525a0866cSChuck Lever 	else if (attr->ia_valid & ATTR_MTIME)
37625a0866cSChuck Lever 		xdr_encode_current_server_time(p, &attr->ia_mtime);
37725a0866cSChuck Lever 	else
37825a0866cSChuck Lever 		xdr_time_not_set(p);
37925a0866cSChuck Lever }
38025a0866cSChuck Lever 
38125a0866cSChuck Lever /*
38225a0866cSChuck Lever  * 2.3.7.  filename
38325a0866cSChuck Lever  *
38425a0866cSChuck Lever  *	typedef string filename<MAXNAMLEN>;
38525a0866cSChuck Lever  */
38625a0866cSChuck Lever static void encode_filename(struct xdr_stream *xdr,
38725a0866cSChuck Lever 			    const char *name, u32 length)
38825a0866cSChuck Lever {
38925a0866cSChuck Lever 	__be32 *p;
39025a0866cSChuck Lever 
39125a0866cSChuck Lever 	BUG_ON(length > NFS2_MAXNAMLEN);
39225a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + length);
39325a0866cSChuck Lever 	xdr_encode_opaque(p, name, length);
39425a0866cSChuck Lever }
39525a0866cSChuck Lever 
396f796f8b3SChuck Lever static int decode_filename_inline(struct xdr_stream *xdr,
397f796f8b3SChuck Lever 				  const char **name, u32 *length)
398f796f8b3SChuck Lever {
399f796f8b3SChuck Lever 	__be32 *p;
400f796f8b3SChuck Lever 	u32 count;
401f796f8b3SChuck Lever 
402f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
403f796f8b3SChuck Lever 	if (unlikely(p == NULL))
404f796f8b3SChuck Lever 		goto out_overflow;
405f796f8b3SChuck Lever 	count = be32_to_cpup(p);
406f796f8b3SChuck Lever 	if (count > NFS3_MAXNAMLEN)
407f796f8b3SChuck Lever 		goto out_nametoolong;
408f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, count);
409f796f8b3SChuck Lever 	if (unlikely(p == NULL))
410f796f8b3SChuck Lever 		goto out_overflow;
411f796f8b3SChuck Lever 	*name = (const char *)p;
412f796f8b3SChuck Lever 	*length = count;
413f796f8b3SChuck Lever 	return 0;
414f796f8b3SChuck Lever out_nametoolong:
415f796f8b3SChuck Lever 	dprintk("NFS: returned filename too long: %u\n", count);
416f796f8b3SChuck Lever 	return -ENAMETOOLONG;
417f796f8b3SChuck Lever out_overflow:
418f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
419f796f8b3SChuck Lever 	return -EIO;
420f796f8b3SChuck Lever }
421f796f8b3SChuck Lever 
42225a0866cSChuck Lever /*
42325a0866cSChuck Lever  * 2.3.8.  path
42425a0866cSChuck Lever  *
42525a0866cSChuck Lever  *	typedef string path<MAXPATHLEN>;
42625a0866cSChuck Lever  */
42725a0866cSChuck Lever static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length)
42825a0866cSChuck Lever {
42925a0866cSChuck Lever 	__be32 *p;
43025a0866cSChuck Lever 
43125a0866cSChuck Lever 	BUG_ON(length > NFS2_MAXPATHLEN);
43225a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4);
43325a0866cSChuck Lever 	*p = cpu_to_be32(length);
43425a0866cSChuck Lever 	xdr_write_pages(xdr, pages, 0, length);
43525a0866cSChuck Lever }
43625a0866cSChuck Lever 
437f796f8b3SChuck Lever static int decode_path(struct xdr_stream *xdr)
438f796f8b3SChuck Lever {
439f796f8b3SChuck Lever 	u32 length, recvd;
440f796f8b3SChuck Lever 	__be32 *p;
441f796f8b3SChuck Lever 
442f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
443f796f8b3SChuck Lever 	if (unlikely(p == NULL))
444f796f8b3SChuck Lever 		goto out_overflow;
445f796f8b3SChuck Lever 	length = be32_to_cpup(p);
446f796f8b3SChuck Lever 	if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN))
447f796f8b3SChuck Lever 		goto out_size;
44864bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, length);
449f796f8b3SChuck Lever 	if (unlikely(length > recvd))
450f796f8b3SChuck Lever 		goto out_cheating;
451f796f8b3SChuck Lever 	xdr_terminate_string(xdr->buf, length);
452f796f8b3SChuck Lever 	return 0;
453f796f8b3SChuck Lever out_size:
454f796f8b3SChuck Lever 	dprintk("NFS: returned pathname too long: %u\n", length);
455f796f8b3SChuck Lever 	return -ENAMETOOLONG;
456f796f8b3SChuck Lever out_cheating:
457f796f8b3SChuck Lever 	dprintk("NFS: server cheating in pathname result: "
458f796f8b3SChuck Lever 		"length %u > received %u\n", length, recvd);
459f796f8b3SChuck Lever 	return -EIO;
460f796f8b3SChuck Lever out_overflow:
461f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
462f796f8b3SChuck Lever 	return -EIO;
463f796f8b3SChuck Lever }
464f796f8b3SChuck Lever 
465f796f8b3SChuck Lever /*
466f796f8b3SChuck Lever  * 2.3.9.  attrstat
467f796f8b3SChuck Lever  *
468f796f8b3SChuck Lever  *	union attrstat switch (stat status) {
469f796f8b3SChuck Lever  *	case NFS_OK:
470f796f8b3SChuck Lever  *		fattr attributes;
471f796f8b3SChuck Lever  *	default:
472f796f8b3SChuck Lever  *		void;
473f796f8b3SChuck Lever  *	};
474f796f8b3SChuck Lever  */
475f796f8b3SChuck Lever static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result)
476f796f8b3SChuck Lever {
477f796f8b3SChuck Lever 	enum nfs_stat status;
478f796f8b3SChuck Lever 	int error;
479f796f8b3SChuck Lever 
480f796f8b3SChuck Lever 	error = decode_stat(xdr, &status);
481f796f8b3SChuck Lever 	if (unlikely(error))
482f796f8b3SChuck Lever 		goto out;
483f796f8b3SChuck Lever 	if (status != NFS_OK)
484f796f8b3SChuck Lever 		goto out_default;
485f796f8b3SChuck Lever 	error = decode_fattr(xdr, result);
486f796f8b3SChuck Lever out:
487f796f8b3SChuck Lever 	return error;
488f796f8b3SChuck Lever out_default:
489f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
490f796f8b3SChuck Lever }
491f796f8b3SChuck Lever 
49225a0866cSChuck Lever /*
49325a0866cSChuck Lever  * 2.3.10.  diropargs
49425a0866cSChuck Lever  *
49525a0866cSChuck Lever  *	struct diropargs {
49625a0866cSChuck Lever  *		fhandle  dir;
49725a0866cSChuck Lever  *		filename name;
49825a0866cSChuck Lever  *	};
49925a0866cSChuck Lever  */
50025a0866cSChuck Lever static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh,
50125a0866cSChuck Lever 			     const char *name, u32 length)
50225a0866cSChuck Lever {
50325a0866cSChuck Lever 	encode_fhandle(xdr, fh);
50425a0866cSChuck Lever 	encode_filename(xdr, name, length);
50525a0866cSChuck Lever }
50625a0866cSChuck Lever 
507f796f8b3SChuck Lever /*
508f796f8b3SChuck Lever  * 2.3.11.  diropres
509f796f8b3SChuck Lever  *
510f796f8b3SChuck Lever  *	union diropres switch (stat status) {
511f796f8b3SChuck Lever  *	case NFS_OK:
512f796f8b3SChuck Lever  *		struct {
513f796f8b3SChuck Lever  *			fhandle file;
514f796f8b3SChuck Lever  *			fattr   attributes;
515f796f8b3SChuck Lever  *		} diropok;
516f796f8b3SChuck Lever  *	default:
517f796f8b3SChuck Lever  *		void;
518f796f8b3SChuck Lever  *	};
519f796f8b3SChuck Lever  */
520f796f8b3SChuck Lever static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result)
521f796f8b3SChuck Lever {
522f796f8b3SChuck Lever 	int error;
523f796f8b3SChuck Lever 
524f796f8b3SChuck Lever 	error = decode_fhandle(xdr, result->fh);
525f796f8b3SChuck Lever 	if (unlikely(error))
526f796f8b3SChuck Lever 		goto out;
527f796f8b3SChuck Lever 	error = decode_fattr(xdr, result->fattr);
528f796f8b3SChuck Lever out:
529f796f8b3SChuck Lever 	return error;
530f796f8b3SChuck Lever }
531f796f8b3SChuck Lever 
532f796f8b3SChuck Lever static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result)
533f796f8b3SChuck Lever {
534f796f8b3SChuck Lever 	enum nfs_stat status;
535f796f8b3SChuck Lever 	int error;
536f796f8b3SChuck Lever 
537f796f8b3SChuck Lever 	error = decode_stat(xdr, &status);
538f796f8b3SChuck Lever 	if (unlikely(error))
539f796f8b3SChuck Lever 		goto out;
540f796f8b3SChuck Lever 	if (status != NFS_OK)
541f796f8b3SChuck Lever 		goto out_default;
542f796f8b3SChuck Lever 	error = decode_diropok(xdr, result);
543f796f8b3SChuck Lever out:
544f796f8b3SChuck Lever 	return error;
545f796f8b3SChuck Lever out_default:
546f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
547f796f8b3SChuck Lever }
548f796f8b3SChuck Lever 
54925a0866cSChuck Lever 
55025a0866cSChuck Lever /*
5512d70f533SChuck Lever  * NFSv2 XDR encode functions
5522d70f533SChuck Lever  *
5532d70f533SChuck Lever  * NFSv2 argument types are defined in section 2.2 of RFC 1094:
5542d70f533SChuck Lever  * "NFS: Network File System Protocol Specification".
5551da177e4SLinus Torvalds  */
5561da177e4SLinus Torvalds 
5579f06c719SChuck Lever static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req,
5589f06c719SChuck Lever 				 struct xdr_stream *xdr,
55925a0866cSChuck Lever 				 const struct nfs_fh *fh)
56025a0866cSChuck Lever {
5619f06c719SChuck Lever 	encode_fhandle(xdr, fh);
56225a0866cSChuck Lever }
56325a0866cSChuck Lever 
5641da177e4SLinus Torvalds /*
56525a0866cSChuck Lever  * 2.2.3.  sattrargs
56625a0866cSChuck Lever  *
56725a0866cSChuck Lever  *	struct sattrargs {
56825a0866cSChuck Lever  *		fhandle file;
56925a0866cSChuck Lever  *		sattr attributes;
57025a0866cSChuck Lever  *	};
57125a0866cSChuck Lever  */
5729f06c719SChuck Lever static void nfs2_xdr_enc_sattrargs(struct rpc_rqst *req,
5739f06c719SChuck Lever 				   struct xdr_stream *xdr,
57425a0866cSChuck Lever 				   const struct nfs_sattrargs *args)
57525a0866cSChuck Lever {
5769f06c719SChuck Lever 	encode_fhandle(xdr, args->fh);
5779f06c719SChuck Lever 	encode_sattr(xdr, args->sattr);
57825a0866cSChuck Lever }
57925a0866cSChuck Lever 
5809f06c719SChuck Lever static void nfs2_xdr_enc_diropargs(struct rpc_rqst *req,
5819f06c719SChuck Lever 				   struct xdr_stream *xdr,
58225a0866cSChuck Lever 				   const struct nfs_diropargs *args)
58325a0866cSChuck Lever {
5849f06c719SChuck Lever 	encode_diropargs(xdr, args->fh, args->name, args->len);
58525a0866cSChuck Lever }
58625a0866cSChuck Lever 
5879f06c719SChuck Lever static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req,
5889f06c719SChuck Lever 				      struct xdr_stream *xdr,
58925a0866cSChuck Lever 				      const struct nfs_readlinkargs *args)
59025a0866cSChuck Lever {
5919f06c719SChuck Lever 	encode_fhandle(xdr, args->fh);
59225a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
59325a0866cSChuck Lever 					args->pglen, NFS_readlinkres_sz);
59425a0866cSChuck Lever }
59525a0866cSChuck Lever 
5964fdc17b2STrond Myklebust /*
59725a0866cSChuck Lever  * 2.2.7.  readargs
59825a0866cSChuck Lever  *
59925a0866cSChuck Lever  *	struct readargs {
60025a0866cSChuck Lever  *		fhandle file;
60125a0866cSChuck Lever  *		unsigned offset;
60225a0866cSChuck Lever  *		unsigned count;
60325a0866cSChuck Lever  *		unsigned totalcount;
60425a0866cSChuck Lever  *	};
60525a0866cSChuck Lever  */
60625a0866cSChuck Lever static void encode_readargs(struct xdr_stream *xdr,
60725a0866cSChuck Lever 			    const struct nfs_readargs *args)
60825a0866cSChuck Lever {
60925a0866cSChuck Lever 	u32 offset = args->offset;
61025a0866cSChuck Lever 	u32 count = args->count;
61125a0866cSChuck Lever 	__be32 *p;
61225a0866cSChuck Lever 
61325a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
61425a0866cSChuck Lever 
61525a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4 + 4);
61625a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
61725a0866cSChuck Lever 	*p++ = cpu_to_be32(count);
61825a0866cSChuck Lever 	*p = cpu_to_be32(count);
61925a0866cSChuck Lever }
62025a0866cSChuck Lever 
6219f06c719SChuck Lever static void nfs2_xdr_enc_readargs(struct rpc_rqst *req,
6229f06c719SChuck Lever 				  struct xdr_stream *xdr,
62325a0866cSChuck Lever 				  const struct nfs_readargs *args)
62425a0866cSChuck Lever {
6259f06c719SChuck Lever 	encode_readargs(xdr, args);
62625a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
62725a0866cSChuck Lever 					args->count, NFS_readres_sz);
62825a0866cSChuck Lever 	req->rq_rcv_buf.flags |= XDRBUF_READ;
62925a0866cSChuck Lever }
63025a0866cSChuck Lever 
63125a0866cSChuck Lever /*
63225a0866cSChuck Lever  * 2.2.9.  writeargs
63325a0866cSChuck Lever  *
63425a0866cSChuck Lever  *	struct writeargs {
63525a0866cSChuck Lever  *		fhandle file;
63625a0866cSChuck Lever  *		unsigned beginoffset;
63725a0866cSChuck Lever  *		unsigned offset;
63825a0866cSChuck Lever  *		unsigned totalcount;
63925a0866cSChuck Lever  *		nfsdata data;
64025a0866cSChuck Lever  *	};
64125a0866cSChuck Lever  */
64225a0866cSChuck Lever static void encode_writeargs(struct xdr_stream *xdr,
64325a0866cSChuck Lever 			     const struct nfs_writeargs *args)
64425a0866cSChuck Lever {
64525a0866cSChuck Lever 	u32 offset = args->offset;
64625a0866cSChuck Lever 	u32 count = args->count;
64725a0866cSChuck Lever 	__be32 *p;
64825a0866cSChuck Lever 
64925a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
65025a0866cSChuck Lever 
65125a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4);
65225a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
65325a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
65425a0866cSChuck Lever 	*p++ = cpu_to_be32(count);
65525a0866cSChuck Lever 
65625a0866cSChuck Lever 	/* nfsdata */
65725a0866cSChuck Lever 	*p = cpu_to_be32(count);
65825a0866cSChuck Lever 	xdr_write_pages(xdr, args->pages, args->pgbase, count);
65925a0866cSChuck Lever }
66025a0866cSChuck Lever 
6619f06c719SChuck Lever static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req,
6629f06c719SChuck Lever 				   struct xdr_stream *xdr,
66325a0866cSChuck Lever 				   const struct nfs_writeargs *args)
66425a0866cSChuck Lever {
6659f06c719SChuck Lever 	encode_writeargs(xdr, args);
6669f06c719SChuck Lever 	xdr->buf->flags |= XDRBUF_WRITE;
66725a0866cSChuck Lever }
66825a0866cSChuck Lever 
66925a0866cSChuck Lever /*
67025a0866cSChuck Lever  * 2.2.10.  createargs
67125a0866cSChuck Lever  *
67225a0866cSChuck Lever  *	struct createargs {
67325a0866cSChuck Lever  *		diropargs where;
67425a0866cSChuck Lever  *		sattr attributes;
67525a0866cSChuck Lever  *	};
67625a0866cSChuck Lever  */
6779f06c719SChuck Lever static void nfs2_xdr_enc_createargs(struct rpc_rqst *req,
6789f06c719SChuck Lever 				    struct xdr_stream *xdr,
67925a0866cSChuck Lever 				    const struct nfs_createargs *args)
68025a0866cSChuck Lever {
6819f06c719SChuck Lever 	encode_diropargs(xdr, args->fh, args->name, args->len);
6829f06c719SChuck Lever 	encode_sattr(xdr, args->sattr);
68325a0866cSChuck Lever }
68425a0866cSChuck Lever 
6859f06c719SChuck Lever static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req,
6869f06c719SChuck Lever 				    struct xdr_stream *xdr,
68725a0866cSChuck Lever 				    const struct nfs_removeargs *args)
68825a0866cSChuck Lever {
6899f06c719SChuck Lever 	encode_diropargs(xdr, args->fh, args->name.name, args->name.len);
69025a0866cSChuck Lever }
69125a0866cSChuck Lever 
69225a0866cSChuck Lever /*
69325a0866cSChuck Lever  * 2.2.12.  renameargs
69425a0866cSChuck Lever  *
69525a0866cSChuck Lever  *	struct renameargs {
69625a0866cSChuck Lever  *		diropargs from;
69725a0866cSChuck Lever  *		diropargs to;
69825a0866cSChuck Lever  *	};
69925a0866cSChuck Lever  */
7009f06c719SChuck Lever static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req,
7019f06c719SChuck Lever 				    struct xdr_stream *xdr,
70225a0866cSChuck Lever 				    const struct nfs_renameargs *args)
70325a0866cSChuck Lever {
70425a0866cSChuck Lever 	const struct qstr *old = args->old_name;
70525a0866cSChuck Lever 	const struct qstr *new = args->new_name;
70625a0866cSChuck Lever 
7079f06c719SChuck Lever 	encode_diropargs(xdr, args->old_dir, old->name, old->len);
7089f06c719SChuck Lever 	encode_diropargs(xdr, args->new_dir, new->name, new->len);
70925a0866cSChuck Lever }
71025a0866cSChuck Lever 
71125a0866cSChuck Lever /*
71225a0866cSChuck Lever  * 2.2.13.  linkargs
71325a0866cSChuck Lever  *
71425a0866cSChuck Lever  *	struct linkargs {
71525a0866cSChuck Lever  *		fhandle from;
71625a0866cSChuck Lever  *		diropargs to;
71725a0866cSChuck Lever  *	};
71825a0866cSChuck Lever  */
7199f06c719SChuck Lever static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req,
7209f06c719SChuck Lever 				  struct xdr_stream *xdr,
72125a0866cSChuck Lever 				  const struct nfs_linkargs *args)
72225a0866cSChuck Lever {
7239f06c719SChuck Lever 	encode_fhandle(xdr, args->fromfh);
7249f06c719SChuck Lever 	encode_diropargs(xdr, args->tofh, args->toname, args->tolen);
72525a0866cSChuck Lever }
72625a0866cSChuck Lever 
72725a0866cSChuck Lever /*
72825a0866cSChuck Lever  * 2.2.14.  symlinkargs
72925a0866cSChuck Lever  *
73025a0866cSChuck Lever  *	struct symlinkargs {
73125a0866cSChuck Lever  *		diropargs from;
73225a0866cSChuck Lever  *		path to;
73325a0866cSChuck Lever  *		sattr attributes;
73425a0866cSChuck Lever  *	};
73525a0866cSChuck Lever  */
7369f06c719SChuck Lever static void nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req,
7379f06c719SChuck Lever 				     struct xdr_stream *xdr,
73825a0866cSChuck Lever 				     const struct nfs_symlinkargs *args)
73925a0866cSChuck Lever {
7409f06c719SChuck Lever 	encode_diropargs(xdr, args->fromfh, args->fromname, args->fromlen);
7419f06c719SChuck Lever 	encode_path(xdr, args->pages, args->pathlen);
7429f06c719SChuck Lever 	encode_sattr(xdr, args->sattr);
74325a0866cSChuck Lever }
74425a0866cSChuck Lever 
74525a0866cSChuck Lever /*
74625a0866cSChuck Lever  * 2.2.17.  readdirargs
74725a0866cSChuck Lever  *
74825a0866cSChuck Lever  *	struct readdirargs {
74925a0866cSChuck Lever  *		fhandle dir;
75025a0866cSChuck Lever  *		nfscookie cookie;
75125a0866cSChuck Lever  *		unsigned count;
75225a0866cSChuck Lever  *	};
75325a0866cSChuck Lever  */
75425a0866cSChuck Lever static void encode_readdirargs(struct xdr_stream *xdr,
75525a0866cSChuck Lever 			       const struct nfs_readdirargs *args)
75625a0866cSChuck Lever {
75725a0866cSChuck Lever 	__be32 *p;
75825a0866cSChuck Lever 
75925a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
76025a0866cSChuck Lever 
76125a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4);
76225a0866cSChuck Lever 	*p++ = cpu_to_be32(args->cookie);
76325a0866cSChuck Lever 	*p = cpu_to_be32(args->count);
76425a0866cSChuck Lever }
76525a0866cSChuck Lever 
7669f06c719SChuck Lever static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req,
7679f06c719SChuck Lever 				     struct xdr_stream *xdr,
76825a0866cSChuck Lever 				     const struct nfs_readdirargs *args)
76925a0866cSChuck Lever {
7709f06c719SChuck Lever 	encode_readdirargs(xdr, args);
77125a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, 0,
77225a0866cSChuck Lever 					args->count, NFS_readdirres_sz);
77325a0866cSChuck Lever }
77425a0866cSChuck Lever 
77525a0866cSChuck Lever /*
776661ad423SChuck Lever  * NFSv2 XDR decode functions
777661ad423SChuck Lever  *
778661ad423SChuck Lever  * NFSv2 result types are defined in section 2.2 of RFC 1094:
779661ad423SChuck Lever  * "NFS: Network File System Protocol Specification".
7801da177e4SLinus Torvalds  */
7811da177e4SLinus Torvalds 
782bf269551SChuck Lever static int nfs2_xdr_dec_stat(struct rpc_rqst *req, struct xdr_stream *xdr,
783f796f8b3SChuck Lever 			     void *__unused)
784f796f8b3SChuck Lever {
785f796f8b3SChuck Lever 	enum nfs_stat status;
786f796f8b3SChuck Lever 	int error;
787f796f8b3SChuck Lever 
788bf269551SChuck Lever 	error = decode_stat(xdr, &status);
789f796f8b3SChuck Lever 	if (unlikely(error))
790f796f8b3SChuck Lever 		goto out;
791f796f8b3SChuck Lever 	if (status != NFS_OK)
792f796f8b3SChuck Lever 		goto out_default;
793f796f8b3SChuck Lever out:
794f796f8b3SChuck Lever 	return error;
795f796f8b3SChuck Lever out_default:
796f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
797f796f8b3SChuck Lever }
798f796f8b3SChuck Lever 
799bf269551SChuck Lever static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, struct xdr_stream *xdr,
800f796f8b3SChuck Lever 				 struct nfs_fattr *result)
801f796f8b3SChuck Lever {
802bf269551SChuck Lever 	return decode_attrstat(xdr, result);
803f796f8b3SChuck Lever }
804f796f8b3SChuck Lever 
805bf269551SChuck Lever static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, struct xdr_stream *xdr,
806f796f8b3SChuck Lever 				 struct nfs_diropok *result)
807f796f8b3SChuck Lever {
808bf269551SChuck Lever 	return decode_diropres(xdr, result);
809f796f8b3SChuck Lever }
810f796f8b3SChuck Lever 
8111da177e4SLinus Torvalds /*
812f796f8b3SChuck Lever  * 2.2.6.  readlinkres
813f796f8b3SChuck Lever  *
814f796f8b3SChuck Lever  *	union readlinkres switch (stat status) {
815f796f8b3SChuck Lever  *	case NFS_OK:
816f796f8b3SChuck Lever  *		path data;
817f796f8b3SChuck Lever  *	default:
818f796f8b3SChuck Lever  *		void;
819f796f8b3SChuck Lever  *	};
820f796f8b3SChuck Lever  */
821bf269551SChuck Lever static int nfs2_xdr_dec_readlinkres(struct rpc_rqst *req,
822bf269551SChuck Lever 				    struct xdr_stream *xdr, void *__unused)
823f796f8b3SChuck Lever {
824f796f8b3SChuck Lever 	enum nfs_stat status;
825f796f8b3SChuck Lever 	int error;
826f796f8b3SChuck Lever 
827bf269551SChuck Lever 	error = decode_stat(xdr, &status);
828f796f8b3SChuck Lever 	if (unlikely(error))
829f796f8b3SChuck Lever 		goto out;
830f796f8b3SChuck Lever 	if (status != NFS_OK)
831f796f8b3SChuck Lever 		goto out_default;
832bf269551SChuck Lever 	error = decode_path(xdr);
833f796f8b3SChuck Lever out:
834f796f8b3SChuck Lever 	return error;
835f796f8b3SChuck Lever out_default:
836f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
837f796f8b3SChuck Lever }
838f796f8b3SChuck Lever 
839f796f8b3SChuck Lever /*
840f796f8b3SChuck Lever  * 2.2.7.  readres
841f796f8b3SChuck Lever  *
842f796f8b3SChuck Lever  *	union readres switch (stat status) {
843f796f8b3SChuck Lever  *	case NFS_OK:
844f796f8b3SChuck Lever  *		fattr attributes;
845f796f8b3SChuck Lever  *		nfsdata data;
846f796f8b3SChuck Lever  *	default:
847f796f8b3SChuck Lever  *		void;
848f796f8b3SChuck Lever  *	};
849f796f8b3SChuck Lever  */
850bf269551SChuck Lever static int nfs2_xdr_dec_readres(struct rpc_rqst *req, struct xdr_stream *xdr,
851f796f8b3SChuck Lever 				struct nfs_readres *result)
852f796f8b3SChuck Lever {
853f796f8b3SChuck Lever 	enum nfs_stat status;
854f796f8b3SChuck Lever 	int error;
855f796f8b3SChuck Lever 
856bf269551SChuck Lever 	error = decode_stat(xdr, &status);
857f796f8b3SChuck Lever 	if (unlikely(error))
858f796f8b3SChuck Lever 		goto out;
859f796f8b3SChuck Lever 	if (status != NFS_OK)
860f796f8b3SChuck Lever 		goto out_default;
861bf269551SChuck Lever 	error = decode_fattr(xdr, result->fattr);
862f796f8b3SChuck Lever 	if (unlikely(error))
863f796f8b3SChuck Lever 		goto out;
864bf269551SChuck Lever 	error = decode_nfsdata(xdr, result);
865f796f8b3SChuck Lever out:
866f796f8b3SChuck Lever 	return error;
867f796f8b3SChuck Lever out_default:
868f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
869f796f8b3SChuck Lever }
870f796f8b3SChuck Lever 
871bf269551SChuck Lever static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, struct xdr_stream *xdr,
872f796f8b3SChuck Lever 				 struct nfs_writeres *result)
873f796f8b3SChuck Lever {
874f796f8b3SChuck Lever 	/* All NFSv2 writes are "file sync" writes */
875f796f8b3SChuck Lever 	result->verf->committed = NFS_FILE_SYNC;
876bf269551SChuck Lever 	return decode_attrstat(xdr, result->fattr);
877f796f8b3SChuck Lever }
878f796f8b3SChuck Lever 
879f796f8b3SChuck Lever /**
880f796f8b3SChuck Lever  * nfs2_decode_dirent - Decode a single NFSv2 directory entry stored in
881f796f8b3SChuck Lever  *                      the local page cache.
882f796f8b3SChuck Lever  * @xdr: XDR stream where entry resides
883f796f8b3SChuck Lever  * @entry: buffer to fill in with entry data
884f796f8b3SChuck Lever  * @plus: boolean indicating whether this should be a readdirplus entry
885f796f8b3SChuck Lever  *
886573c4e1eSChuck Lever  * Returns zero if successful, otherwise a negative errno value is
887573c4e1eSChuck Lever  * returned.
888f796f8b3SChuck Lever  *
889f796f8b3SChuck Lever  * This function is not invoked during READDIR reply decoding, but
890f796f8b3SChuck Lever  * rather whenever an application invokes the getdents(2) system call
891f796f8b3SChuck Lever  * on a directory already in our cache.
892f796f8b3SChuck Lever  *
893f796f8b3SChuck Lever  * 2.2.17.  entry
894f796f8b3SChuck Lever  *
895f796f8b3SChuck Lever  *	struct entry {
896f796f8b3SChuck Lever  *		unsigned	fileid;
897f796f8b3SChuck Lever  *		filename	name;
898f796f8b3SChuck Lever  *		nfscookie	cookie;
899f796f8b3SChuck Lever  *		entry		*nextentry;
900f796f8b3SChuck Lever  *	};
901f796f8b3SChuck Lever  */
902573c4e1eSChuck Lever int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
903573c4e1eSChuck Lever 		       int plus)
904f796f8b3SChuck Lever {
905f796f8b3SChuck Lever 	__be32 *p;
906f796f8b3SChuck Lever 	int error;
907f796f8b3SChuck Lever 
908f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
909f796f8b3SChuck Lever 	if (unlikely(p == NULL))
910f796f8b3SChuck Lever 		goto out_overflow;
911f796f8b3SChuck Lever 	if (*p++ == xdr_zero) {
912f796f8b3SChuck Lever 		p = xdr_inline_decode(xdr, 4);
913f796f8b3SChuck Lever 		if (unlikely(p == NULL))
914f796f8b3SChuck Lever 			goto out_overflow;
915f796f8b3SChuck Lever 		if (*p++ == xdr_zero)
916573c4e1eSChuck Lever 			return -EAGAIN;
917f796f8b3SChuck Lever 		entry->eof = 1;
918573c4e1eSChuck Lever 		return -EBADCOOKIE;
919f796f8b3SChuck Lever 	}
920f796f8b3SChuck Lever 
921f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
922f796f8b3SChuck Lever 	if (unlikely(p == NULL))
923f796f8b3SChuck Lever 		goto out_overflow;
924f796f8b3SChuck Lever 	entry->ino = be32_to_cpup(p);
925f796f8b3SChuck Lever 
926f796f8b3SChuck Lever 	error = decode_filename_inline(xdr, &entry->name, &entry->len);
927f796f8b3SChuck Lever 	if (unlikely(error))
928573c4e1eSChuck Lever 		return error;
929f796f8b3SChuck Lever 
930f796f8b3SChuck Lever 	/*
931f796f8b3SChuck Lever 	 * The type (size and byte order) of nfscookie isn't defined in
932f796f8b3SChuck Lever 	 * RFC 1094.  This implementation assumes that it's an XDR uint32.
933f796f8b3SChuck Lever 	 */
934f796f8b3SChuck Lever 	entry->prev_cookie = entry->cookie;
935f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
936f796f8b3SChuck Lever 	if (unlikely(p == NULL))
937f796f8b3SChuck Lever 		goto out_overflow;
938f796f8b3SChuck Lever 	entry->cookie = be32_to_cpup(p);
939f796f8b3SChuck Lever 
940f796f8b3SChuck Lever 	entry->d_type = DT_UNKNOWN;
941f796f8b3SChuck Lever 
942573c4e1eSChuck Lever 	return 0;
943f796f8b3SChuck Lever 
944f796f8b3SChuck Lever out_overflow:
945f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
946573c4e1eSChuck Lever 	return -EAGAIN;
947f796f8b3SChuck Lever }
948f796f8b3SChuck Lever 
949f796f8b3SChuck Lever /*
950f796f8b3SChuck Lever  * 2.2.17.  readdirres
951f796f8b3SChuck Lever  *
952f796f8b3SChuck Lever  *	union readdirres switch (stat status) {
953f796f8b3SChuck Lever  *	case NFS_OK:
954f796f8b3SChuck Lever  *		struct {
955f796f8b3SChuck Lever  *			entry *entries;
956f796f8b3SChuck Lever  *			bool eof;
957f796f8b3SChuck Lever  *		} readdirok;
958f796f8b3SChuck Lever  *	default:
959f796f8b3SChuck Lever  *		void;
960f796f8b3SChuck Lever  *	};
961f796f8b3SChuck Lever  *
962f796f8b3SChuck Lever  * Read the directory contents into the page cache, but don't
963f796f8b3SChuck Lever  * touch them.  The actual decoding is done by nfs2_decode_dirent()
964f796f8b3SChuck Lever  * during subsequent nfs_readdir() calls.
965f796f8b3SChuck Lever  */
966f796f8b3SChuck Lever static int decode_readdirok(struct xdr_stream *xdr)
967f796f8b3SChuck Lever {
96864bd577eSTrond Myklebust 	return xdr_read_pages(xdr, xdr->buf->page_len);
969f796f8b3SChuck Lever }
970f796f8b3SChuck Lever 
971bf269551SChuck Lever static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req,
972bf269551SChuck Lever 				   struct xdr_stream *xdr, void *__unused)
973f796f8b3SChuck Lever {
974f796f8b3SChuck Lever 	enum nfs_stat status;
975f796f8b3SChuck Lever 	int error;
976f796f8b3SChuck Lever 
977bf269551SChuck Lever 	error = decode_stat(xdr, &status);
978f796f8b3SChuck Lever 	if (unlikely(error))
979f796f8b3SChuck Lever 		goto out;
980f796f8b3SChuck Lever 	if (status != NFS_OK)
981f796f8b3SChuck Lever 		goto out_default;
982bf269551SChuck Lever 	error = decode_readdirok(xdr);
983f796f8b3SChuck Lever out:
984f796f8b3SChuck Lever 	return error;
985f796f8b3SChuck Lever out_default:
986f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
987f796f8b3SChuck Lever }
988f796f8b3SChuck Lever 
9891da177e4SLinus Torvalds /*
990f796f8b3SChuck Lever  * 2.2.18.  statfsres
991f796f8b3SChuck Lever  *
992f796f8b3SChuck Lever  *	union statfsres (stat status) {
993f796f8b3SChuck Lever  *	case NFS_OK:
994f796f8b3SChuck Lever  *		struct {
995f796f8b3SChuck Lever  *			unsigned tsize;
996f796f8b3SChuck Lever  *			unsigned bsize;
997f796f8b3SChuck Lever  *			unsigned blocks;
998f796f8b3SChuck Lever  *			unsigned bfree;
999f796f8b3SChuck Lever  *			unsigned bavail;
1000f796f8b3SChuck Lever  *		} info;
1001f796f8b3SChuck Lever  *	default:
1002f796f8b3SChuck Lever  *		void;
1003f796f8b3SChuck Lever  *	};
1004f796f8b3SChuck Lever  */
1005f796f8b3SChuck Lever static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result)
1006f796f8b3SChuck Lever {
1007f796f8b3SChuck Lever 	__be32 *p;
1008f796f8b3SChuck Lever 
1009f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS_info_sz << 2);
1010f796f8b3SChuck Lever 	if (unlikely(p == NULL))
1011f796f8b3SChuck Lever 		goto out_overflow;
1012f796f8b3SChuck Lever 	result->tsize  = be32_to_cpup(p++);
1013f796f8b3SChuck Lever 	result->bsize  = be32_to_cpup(p++);
1014f796f8b3SChuck Lever 	result->blocks = be32_to_cpup(p++);
1015f796f8b3SChuck Lever 	result->bfree  = be32_to_cpup(p++);
1016f796f8b3SChuck Lever 	result->bavail = be32_to_cpup(p);
1017f796f8b3SChuck Lever 	return 0;
1018f796f8b3SChuck Lever out_overflow:
1019f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
1020f796f8b3SChuck Lever 	return -EIO;
1021f796f8b3SChuck Lever }
1022f796f8b3SChuck Lever 
1023bf269551SChuck Lever static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr,
1024f796f8b3SChuck Lever 				  struct nfs2_fsstat *result)
1025f796f8b3SChuck Lever {
1026f796f8b3SChuck Lever 	enum nfs_stat status;
1027f796f8b3SChuck Lever 	int error;
1028f796f8b3SChuck Lever 
1029bf269551SChuck Lever 	error = decode_stat(xdr, &status);
1030f796f8b3SChuck Lever 	if (unlikely(error))
1031f796f8b3SChuck Lever 		goto out;
1032f796f8b3SChuck Lever 	if (status != NFS_OK)
1033f796f8b3SChuck Lever 		goto out_default;
1034bf269551SChuck Lever 	error = decode_info(xdr, result);
1035f796f8b3SChuck Lever out:
1036f796f8b3SChuck Lever 	return error;
1037f796f8b3SChuck Lever out_default:
1038f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
1039f796f8b3SChuck Lever }
1040f796f8b3SChuck Lever 
1041f796f8b3SChuck Lever 
1042f796f8b3SChuck Lever /*
10431da177e4SLinus Torvalds  * We need to translate between nfs status return values and
10441da177e4SLinus Torvalds  * the local errno values which may not be the same.
10451da177e4SLinus Torvalds  */
104685828493SChuck Lever static const struct {
10471da177e4SLinus Torvalds 	int stat;
10481da177e4SLinus Torvalds 	int errno;
10491da177e4SLinus Torvalds } nfs_errtbl[] = {
10501da177e4SLinus Torvalds 	{ NFS_OK,		0		},
1051856dff3dSBenny Halevy 	{ NFSERR_PERM,		-EPERM		},
1052856dff3dSBenny Halevy 	{ NFSERR_NOENT,		-ENOENT		},
1053856dff3dSBenny Halevy 	{ NFSERR_IO,		-errno_NFSERR_IO},
1054856dff3dSBenny Halevy 	{ NFSERR_NXIO,		-ENXIO		},
1055856dff3dSBenny Halevy /*	{ NFSERR_EAGAIN,	-EAGAIN		}, */
1056856dff3dSBenny Halevy 	{ NFSERR_ACCES,		-EACCES		},
1057856dff3dSBenny Halevy 	{ NFSERR_EXIST,		-EEXIST		},
1058856dff3dSBenny Halevy 	{ NFSERR_XDEV,		-EXDEV		},
1059856dff3dSBenny Halevy 	{ NFSERR_NODEV,		-ENODEV		},
1060856dff3dSBenny Halevy 	{ NFSERR_NOTDIR,	-ENOTDIR	},
1061856dff3dSBenny Halevy 	{ NFSERR_ISDIR,		-EISDIR		},
1062856dff3dSBenny Halevy 	{ NFSERR_INVAL,		-EINVAL		},
1063856dff3dSBenny Halevy 	{ NFSERR_FBIG,		-EFBIG		},
1064856dff3dSBenny Halevy 	{ NFSERR_NOSPC,		-ENOSPC		},
1065856dff3dSBenny Halevy 	{ NFSERR_ROFS,		-EROFS		},
1066856dff3dSBenny Halevy 	{ NFSERR_MLINK,		-EMLINK		},
1067856dff3dSBenny Halevy 	{ NFSERR_NAMETOOLONG,	-ENAMETOOLONG	},
1068856dff3dSBenny Halevy 	{ NFSERR_NOTEMPTY,	-ENOTEMPTY	},
1069856dff3dSBenny Halevy 	{ NFSERR_DQUOT,		-EDQUOT		},
1070856dff3dSBenny Halevy 	{ NFSERR_STALE,		-ESTALE		},
1071856dff3dSBenny Halevy 	{ NFSERR_REMOTE,	-EREMOTE	},
10721da177e4SLinus Torvalds #ifdef EWFLUSH
1073856dff3dSBenny Halevy 	{ NFSERR_WFLUSH,	-EWFLUSH	},
10741da177e4SLinus Torvalds #endif
1075856dff3dSBenny Halevy 	{ NFSERR_BADHANDLE,	-EBADHANDLE	},
1076856dff3dSBenny Halevy 	{ NFSERR_NOT_SYNC,	-ENOTSYNC	},
1077856dff3dSBenny Halevy 	{ NFSERR_BAD_COOKIE,	-EBADCOOKIE	},
1078856dff3dSBenny Halevy 	{ NFSERR_NOTSUPP,	-ENOTSUPP	},
1079856dff3dSBenny Halevy 	{ NFSERR_TOOSMALL,	-ETOOSMALL	},
1080fdcb4577STrond Myklebust 	{ NFSERR_SERVERFAULT,	-EREMOTEIO	},
1081856dff3dSBenny Halevy 	{ NFSERR_BADTYPE,	-EBADTYPE	},
1082856dff3dSBenny Halevy 	{ NFSERR_JUKEBOX,	-EJUKEBOX	},
1083856dff3dSBenny Halevy 	{ -1,			-EIO		}
10841da177e4SLinus Torvalds };
10851da177e4SLinus Torvalds 
108685828493SChuck Lever /**
108785828493SChuck Lever  * nfs_stat_to_errno - convert an NFS status code to a local errno
108885828493SChuck Lever  * @status: NFS status code to convert
108985828493SChuck Lever  *
109085828493SChuck Lever  * Returns a local errno value, or -EIO if the NFS status code is
109185828493SChuck Lever  * not recognized.  This function is used jointly by NFSv2 and NFSv3.
10921da177e4SLinus Torvalds  */
10935e7e5a0dSBryan Schumaker static int nfs_stat_to_errno(enum nfs_stat status)
10941da177e4SLinus Torvalds {
10951da177e4SLinus Torvalds 	int i;
10961da177e4SLinus Torvalds 
10971da177e4SLinus Torvalds 	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
109885828493SChuck Lever 		if (nfs_errtbl[i].stat == (int)status)
10991da177e4SLinus Torvalds 			return nfs_errtbl[i].errno;
11001da177e4SLinus Torvalds 	}
110185828493SChuck Lever 	dprintk("NFS: Unrecognized nfs status value: %u\n", status);
11021da177e4SLinus Torvalds 	return nfs_errtbl[i].errno;
11031da177e4SLinus Torvalds }
11041da177e4SLinus Torvalds 
11051da177e4SLinus Torvalds #define PROC(proc, argtype, restype, timer)				\
11061da177e4SLinus Torvalds [NFSPROC_##proc] = {							\
11071da177e4SLinus Torvalds 	.p_proc	    =  NFSPROC_##proc,					\
11089f06c719SChuck Lever 	.p_encode   =  (kxdreproc_t)nfs2_xdr_enc_##argtype,		\
1109bf269551SChuck Lever 	.p_decode   =  (kxdrdproc_t)nfs2_xdr_dec_##restype,		\
11102bea90d4SChuck Lever 	.p_arglen   =  NFS_##argtype##_sz,				\
11112bea90d4SChuck Lever 	.p_replen   =  NFS_##restype##_sz,				\
1112cc0175c1SChuck Lever 	.p_timer    =  timer,						\
1113cc0175c1SChuck Lever 	.p_statidx  =  NFSPROC_##proc,					\
1114cc0175c1SChuck Lever 	.p_name     =  #proc,						\
11151da177e4SLinus Torvalds 	}
11161da177e4SLinus Torvalds struct rpc_procinfo	nfs_procedures[] = {
11171da177e4SLinus Torvalds 	PROC(GETATTR,	fhandle,	attrstat,	1),
11181da177e4SLinus Torvalds 	PROC(SETATTR,	sattrargs,	attrstat,	0),
11191da177e4SLinus Torvalds 	PROC(LOOKUP,	diropargs,	diropres,	2),
11201da177e4SLinus Torvalds 	PROC(READLINK,	readlinkargs,	readlinkres,	3),
11211da177e4SLinus Torvalds 	PROC(READ,	readargs,	readres,	3),
11221da177e4SLinus Torvalds 	PROC(WRITE,	writeargs,	writeres,	4),
11231da177e4SLinus Torvalds 	PROC(CREATE,	createargs,	diropres,	0),
11244fdc17b2STrond Myklebust 	PROC(REMOVE,	removeargs,	stat,		0),
11251da177e4SLinus Torvalds 	PROC(RENAME,	renameargs,	stat,		0),
11261da177e4SLinus Torvalds 	PROC(LINK,	linkargs,	stat,		0),
11271da177e4SLinus Torvalds 	PROC(SYMLINK,	symlinkargs,	stat,		0),
11281da177e4SLinus Torvalds 	PROC(MKDIR,	createargs,	diropres,	0),
11291da177e4SLinus Torvalds 	PROC(RMDIR,	diropargs,	stat,		0),
11301da177e4SLinus Torvalds 	PROC(READDIR,	readdirargs,	readdirres,	3),
11311da177e4SLinus Torvalds 	PROC(STATFS,	fhandle,	statfsres,	0),
11321da177e4SLinus Torvalds };
11331da177e4SLinus Torvalds 
1134a613fa16STrond Myklebust const struct rpc_version nfs_version2 = {
11351da177e4SLinus Torvalds 	.number			= 2,
1136e8c96f8cSTobias Klauser 	.nrprocs		= ARRAY_SIZE(nfs_procedures),
11371da177e4SLinus Torvalds 	.procs			= nfs_procedures
11381da177e4SLinus Torvalds };
1139