xref: /openbmc/linux/fs/nfs/nfs2xdr.c (revision b2441318)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * linux/fs/nfs/nfs2xdr.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * XDR functions to encode/decode NFS RPC arguments and results.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Copyright (C) 1992, 1993, 1994  Rick Sladkey
81da177e4SLinus Torvalds  * Copyright (C) 1996 Olaf Kirch
91da177e4SLinus Torvalds  * 04 Aug 1998  Ion Badulescu <ionut@cs.columbia.edu>
101da177e4SLinus Torvalds  * 		FIFO's need special handling in NFSv2
111da177e4SLinus Torvalds  */
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds #include <linux/param.h>
141da177e4SLinus Torvalds #include <linux/time.h>
151da177e4SLinus Torvalds #include <linux/mm.h>
161da177e4SLinus Torvalds #include <linux/errno.h>
171da177e4SLinus Torvalds #include <linux/string.h>
181da177e4SLinus Torvalds #include <linux/in.h>
191da177e4SLinus Torvalds #include <linux/pagemap.h>
201da177e4SLinus Torvalds #include <linux/proc_fs.h>
211da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
221da177e4SLinus Torvalds #include <linux/nfs.h>
231da177e4SLinus Torvalds #include <linux/nfs2.h>
241da177e4SLinus Torvalds #include <linux/nfs_fs.h>
25816724e6STrond Myklebust #include "internal.h"
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds #define NFSDBG_FACILITY		NFSDBG_XDR
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds /* Mapping from NFS error code to "errno" error code. */
301da177e4SLinus Torvalds #define errno_NFSERR_IO		EIO
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds /*
331da177e4SLinus Torvalds  * Declare the space requirements for NFS arguments and replies as
341da177e4SLinus Torvalds  * number of 32bit-words
351da177e4SLinus Torvalds  */
361da177e4SLinus Torvalds #define NFS_fhandle_sz		(8)
371da177e4SLinus Torvalds #define NFS_sattr_sz		(8)
381da177e4SLinus Torvalds #define NFS_filename_sz		(1+(NFS2_MAXNAMLEN>>2))
391da177e4SLinus Torvalds #define NFS_path_sz		(1+(NFS2_MAXPATHLEN>>2))
401da177e4SLinus Torvalds #define NFS_fattr_sz		(17)
411da177e4SLinus Torvalds #define NFS_info_sz		(5)
421da177e4SLinus Torvalds #define NFS_entry_sz		(NFS_filename_sz+3)
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds #define NFS_diropargs_sz	(NFS_fhandle_sz+NFS_filename_sz)
454fdc17b2STrond Myklebust #define NFS_removeargs_sz	(NFS_fhandle_sz+NFS_filename_sz)
461da177e4SLinus Torvalds #define NFS_sattrargs_sz	(NFS_fhandle_sz+NFS_sattr_sz)
471da177e4SLinus Torvalds #define NFS_readlinkargs_sz	(NFS_fhandle_sz)
481da177e4SLinus Torvalds #define NFS_readargs_sz		(NFS_fhandle_sz+3)
491da177e4SLinus Torvalds #define NFS_writeargs_sz	(NFS_fhandle_sz+4)
501da177e4SLinus Torvalds #define NFS_createargs_sz	(NFS_diropargs_sz+NFS_sattr_sz)
511da177e4SLinus Torvalds #define NFS_renameargs_sz	(NFS_diropargs_sz+NFS_diropargs_sz)
521da177e4SLinus Torvalds #define NFS_linkargs_sz		(NFS_fhandle_sz+NFS_diropargs_sz)
5394a6d753SChuck Lever #define NFS_symlinkargs_sz	(NFS_diropargs_sz+1+NFS_sattr_sz)
541da177e4SLinus Torvalds #define NFS_readdirargs_sz	(NFS_fhandle_sz+2)
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds #define NFS_attrstat_sz		(1+NFS_fattr_sz)
571da177e4SLinus Torvalds #define NFS_diropres_sz		(1+NFS_fhandle_sz+NFS_fattr_sz)
581da177e4SLinus Torvalds #define NFS_readlinkres_sz	(2)
591da177e4SLinus Torvalds #define NFS_readres_sz		(1+NFS_fattr_sz+1)
601da177e4SLinus Torvalds #define NFS_writeres_sz         (NFS_attrstat_sz)
611da177e4SLinus Torvalds #define NFS_stat_sz		(1)
621da177e4SLinus Torvalds #define NFS_readdirres_sz	(1)
631da177e4SLinus Torvalds #define NFS_statfsres_sz	(1+NFS_info_sz)
641da177e4SLinus Torvalds 
655e7e5a0dSBryan Schumaker static int nfs_stat_to_errno(enum nfs_stat);
6625a0866cSChuck Lever 
6725a0866cSChuck Lever /*
6825a0866cSChuck Lever  * While encoding arguments, set up the reply buffer in advance to
6925a0866cSChuck Lever  * receive reply data directly into the page cache.
7025a0866cSChuck Lever  */
7125a0866cSChuck Lever static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages,
7225a0866cSChuck Lever 				 unsigned int base, unsigned int len,
7325a0866cSChuck Lever 				 unsigned int bufsize)
7425a0866cSChuck Lever {
7525a0866cSChuck Lever 	struct rpc_auth	*auth = req->rq_cred->cr_auth;
7625a0866cSChuck Lever 	unsigned int replen;
7725a0866cSChuck Lever 
7825a0866cSChuck Lever 	replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize;
7925a0866cSChuck Lever 	xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len);
8025a0866cSChuck Lever }
8125a0866cSChuck Lever 
82f796f8b3SChuck Lever /*
83f796f8b3SChuck Lever  * Handle decode buffer overflows out-of-line.
84f796f8b3SChuck Lever  */
85f796f8b3SChuck Lever static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
86f796f8b3SChuck Lever {
87f796f8b3SChuck Lever 	dprintk("NFS: %s prematurely hit the end of our receive buffer. "
88f796f8b3SChuck Lever 		"Remaining buffer length is %tu words.\n",
89f796f8b3SChuck Lever 		func, xdr->end - xdr->p);
90f796f8b3SChuck Lever }
91f796f8b3SChuck Lever 
9225a0866cSChuck Lever 
931da177e4SLinus Torvalds /*
9425a0866cSChuck Lever  * Encode/decode NFSv2 basic data types
9525a0866cSChuck Lever  *
9625a0866cSChuck Lever  * Basic NFSv2 data types are defined in section 2.3 of RFC 1094:
9725a0866cSChuck Lever  * "NFS: Network File System Protocol Specification".
9825a0866cSChuck Lever  *
9925a0866cSChuck Lever  * Not all basic data types have their own encoding and decoding
10025a0866cSChuck Lever  * functions.  For run-time efficiency, some data types are encoded
10125a0866cSChuck Lever  * or decoded inline.
10225a0866cSChuck Lever  */
10325a0866cSChuck Lever 
10425a0866cSChuck Lever /*
105f796f8b3SChuck Lever  *	typedef opaque	nfsdata<>;
106f796f8b3SChuck Lever  */
1079137bdf3SAnna Schumaker static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_pgio_res *result)
108f796f8b3SChuck Lever {
109f796f8b3SChuck Lever 	u32 recvd, count;
110f796f8b3SChuck Lever 	__be32 *p;
111f796f8b3SChuck Lever 
112f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
113f796f8b3SChuck Lever 	if (unlikely(p == NULL))
114f796f8b3SChuck Lever 		goto out_overflow;
115f796f8b3SChuck Lever 	count = be32_to_cpup(p);
11664bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, count);
117f796f8b3SChuck Lever 	if (unlikely(count > recvd))
118f796f8b3SChuck Lever 		goto out_cheating;
119f796f8b3SChuck Lever out:
120f796f8b3SChuck Lever 	result->eof = 0;	/* NFSv2 does not pass EOF flag on the wire. */
121f796f8b3SChuck Lever 	result->count = count;
122f796f8b3SChuck Lever 	return count;
123f796f8b3SChuck Lever out_cheating:
124f796f8b3SChuck Lever 	dprintk("NFS: server cheating in read result: "
125f796f8b3SChuck Lever 		"count %u > recvd %u\n", count, recvd);
126f796f8b3SChuck Lever 	count = recvd;
127f796f8b3SChuck Lever 	goto out;
128f796f8b3SChuck Lever out_overflow:
129f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
130f796f8b3SChuck Lever 	return -EIO;
131f796f8b3SChuck Lever }
132f796f8b3SChuck Lever 
133f796f8b3SChuck Lever /*
134f796f8b3SChuck Lever  *	enum stat {
135f796f8b3SChuck Lever  *		NFS_OK = 0,
136f796f8b3SChuck Lever  *		NFSERR_PERM = 1,
137f796f8b3SChuck Lever  *		NFSERR_NOENT = 2,
138f796f8b3SChuck Lever  *		NFSERR_IO = 5,
139f796f8b3SChuck Lever  *		NFSERR_NXIO = 6,
140f796f8b3SChuck Lever  *		NFSERR_ACCES = 13,
141f796f8b3SChuck Lever  *		NFSERR_EXIST = 17,
142f796f8b3SChuck Lever  *		NFSERR_NODEV = 19,
143f796f8b3SChuck Lever  *		NFSERR_NOTDIR = 20,
144f796f8b3SChuck Lever  *		NFSERR_ISDIR = 21,
145f796f8b3SChuck Lever  *		NFSERR_FBIG = 27,
146f796f8b3SChuck Lever  *		NFSERR_NOSPC = 28,
147f796f8b3SChuck Lever  *		NFSERR_ROFS = 30,
148f796f8b3SChuck Lever  *		NFSERR_NAMETOOLONG = 63,
149f796f8b3SChuck Lever  *		NFSERR_NOTEMPTY = 66,
150f796f8b3SChuck Lever  *		NFSERR_DQUOT = 69,
151f796f8b3SChuck Lever  *		NFSERR_STALE = 70,
152f796f8b3SChuck Lever  *		NFSERR_WFLUSH = 99
153f796f8b3SChuck Lever  *	};
154f796f8b3SChuck Lever  */
155f796f8b3SChuck Lever static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status)
156f796f8b3SChuck Lever {
157f796f8b3SChuck Lever 	__be32 *p;
158f796f8b3SChuck Lever 
159f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
160f796f8b3SChuck Lever 	if (unlikely(p == NULL))
161f796f8b3SChuck Lever 		goto out_overflow;
162f796f8b3SChuck Lever 	*status = be32_to_cpup(p);
163f796f8b3SChuck Lever 	return 0;
164f796f8b3SChuck Lever out_overflow:
165f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
166f796f8b3SChuck Lever 	return -EIO;
167f796f8b3SChuck Lever }
168f796f8b3SChuck Lever 
169f796f8b3SChuck Lever /*
1705f96e5e3SChuck Lever  * 2.3.2.  ftype
1715f96e5e3SChuck Lever  *
1725f96e5e3SChuck Lever  *	enum ftype {
1735f96e5e3SChuck Lever  *		NFNON = 0,
1745f96e5e3SChuck Lever  *		NFREG = 1,
1755f96e5e3SChuck Lever  *		NFDIR = 2,
1765f96e5e3SChuck Lever  *		NFBLK = 3,
1775f96e5e3SChuck Lever  *		NFCHR = 4,
1785f96e5e3SChuck Lever  *		NFLNK = 5
1795f96e5e3SChuck Lever  *	};
1805f96e5e3SChuck Lever  *
1815f96e5e3SChuck Lever  */
1825f96e5e3SChuck Lever static __be32 *xdr_decode_ftype(__be32 *p, u32 *type)
1835f96e5e3SChuck Lever {
1845f96e5e3SChuck Lever 	*type = be32_to_cpup(p++);
1855f96e5e3SChuck Lever 	if (unlikely(*type > NF2FIFO))
1865f96e5e3SChuck Lever 		*type = NFBAD;
1875f96e5e3SChuck Lever 	return p;
1885f96e5e3SChuck Lever }
1895f96e5e3SChuck Lever 
1905f96e5e3SChuck Lever /*
19125a0866cSChuck Lever  * 2.3.3.  fhandle
19225a0866cSChuck Lever  *
19325a0866cSChuck Lever  *	typedef opaque fhandle[FHSIZE];
19425a0866cSChuck Lever  */
19525a0866cSChuck Lever static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh)
19625a0866cSChuck Lever {
19725a0866cSChuck Lever 	__be32 *p;
19825a0866cSChuck Lever 
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++);
294cfa0898dSEric W. Biederman 	fattr->uid = make_kuid(&init_user_ns, be32_to_cpup(p++));
295cfa0898dSEric W. Biederman 	if (!uid_valid(fattr->uid))
296cfa0898dSEric W. Biederman 		goto out_uid;
297cfa0898dSEric W. Biederman 	fattr->gid = make_kgid(&init_user_ns, be32_to_cpup(p++));
298cfa0898dSEric W. Biederman 	if (!gid_valid(fattr->gid))
299cfa0898dSEric W. Biederman 		goto out_gid;
300cfa0898dSEric W. Biederman 
3015f96e5e3SChuck Lever 	fattr->size = be32_to_cpup(p++);
3025f96e5e3SChuck Lever 	fattr->du.nfs2.blocksize = be32_to_cpup(p++);
3035f96e5e3SChuck Lever 
3045f96e5e3SChuck Lever 	rdev = be32_to_cpup(p++);
3055f96e5e3SChuck Lever 	fattr->rdev = new_decode_dev(rdev);
3065f96e5e3SChuck Lever 	if (type == (u32)NFCHR && rdev == (u32)NFS2_FIFO_DEV) {
3075f96e5e3SChuck Lever 		fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
3085f96e5e3SChuck Lever 		fattr->rdev = 0;
3095f96e5e3SChuck Lever 	}
3105f96e5e3SChuck Lever 
3115f96e5e3SChuck Lever 	fattr->du.nfs2.blocks = be32_to_cpup(p++);
3125f96e5e3SChuck Lever 	fattr->fsid.major = be32_to_cpup(p++);
3135f96e5e3SChuck Lever 	fattr->fsid.minor = 0;
3145f96e5e3SChuck Lever 	fattr->fileid = be32_to_cpup(p++);
3155f96e5e3SChuck Lever 
3165f96e5e3SChuck Lever 	p = xdr_decode_time(p, &fattr->atime);
3175f96e5e3SChuck Lever 	p = xdr_decode_time(p, &fattr->mtime);
3185f96e5e3SChuck Lever 	xdr_decode_time(p, &fattr->ctime);
3193a1556e8STrond Myklebust 	fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime);
3203a1556e8STrond Myklebust 
321f796f8b3SChuck Lever 	return 0;
322cfa0898dSEric W. Biederman out_uid:
323cfa0898dSEric W. Biederman 	dprintk("NFS: returned invalid uid\n");
324cfa0898dSEric W. Biederman 	return -EINVAL;
325cfa0898dSEric W. Biederman out_gid:
326cfa0898dSEric W. Biederman 	dprintk("NFS: returned invalid gid\n");
327cfa0898dSEric W. Biederman 	return -EINVAL;
328f796f8b3SChuck Lever out_overflow:
329f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
330f796f8b3SChuck Lever 	return -EIO;
331f796f8b3SChuck Lever }
332f796f8b3SChuck Lever 
333f796f8b3SChuck Lever /*
33425a0866cSChuck Lever  * 2.3.6.  sattr
33525a0866cSChuck Lever  *
33625a0866cSChuck Lever  *	struct sattr {
33725a0866cSChuck Lever  *		unsigned int	mode;
33825a0866cSChuck Lever  *		unsigned int	uid;
33925a0866cSChuck Lever  *		unsigned int	gid;
34025a0866cSChuck Lever  *		unsigned int	size;
34125a0866cSChuck Lever  *		timeval		atime;
34225a0866cSChuck Lever  *		timeval		mtime;
34325a0866cSChuck Lever  *	};
34425a0866cSChuck Lever  */
34525a0866cSChuck Lever 
34625a0866cSChuck Lever #define NFS2_SATTR_NOT_SET	(0xffffffff)
34725a0866cSChuck Lever 
34825a0866cSChuck Lever static __be32 *xdr_time_not_set(__be32 *p)
34925a0866cSChuck Lever {
35025a0866cSChuck Lever 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
35125a0866cSChuck Lever 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
35225a0866cSChuck Lever 	return p;
35325a0866cSChuck Lever }
35425a0866cSChuck Lever 
35525a0866cSChuck Lever static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr)
35625a0866cSChuck Lever {
35725a0866cSChuck Lever 	__be32 *p;
35825a0866cSChuck Lever 
35925a0866cSChuck Lever 	p = xdr_reserve_space(xdr, NFS_sattr_sz << 2);
36025a0866cSChuck Lever 
36125a0866cSChuck Lever 	if (attr->ia_valid & ATTR_MODE)
36225a0866cSChuck Lever 		*p++ = cpu_to_be32(attr->ia_mode);
36325a0866cSChuck Lever 	else
36425a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
36525a0866cSChuck Lever 	if (attr->ia_valid & ATTR_UID)
366cfa0898dSEric W. Biederman 		*p++ = cpu_to_be32(from_kuid(&init_user_ns, attr->ia_uid));
36725a0866cSChuck Lever 	else
36825a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
36925a0866cSChuck Lever 	if (attr->ia_valid & ATTR_GID)
370cfa0898dSEric W. Biederman 		*p++ = cpu_to_be32(from_kgid(&init_user_ns, attr->ia_gid));
37125a0866cSChuck Lever 	else
37225a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
37325a0866cSChuck Lever 	if (attr->ia_valid & ATTR_SIZE)
37425a0866cSChuck Lever 		*p++ = cpu_to_be32((u32)attr->ia_size);
37525a0866cSChuck Lever 	else
37625a0866cSChuck Lever 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
37725a0866cSChuck Lever 
37825a0866cSChuck Lever 	if (attr->ia_valid & ATTR_ATIME_SET)
37925a0866cSChuck Lever 		p = xdr_encode_time(p, &attr->ia_atime);
38025a0866cSChuck Lever 	else if (attr->ia_valid & ATTR_ATIME)
38125a0866cSChuck Lever 		p = xdr_encode_current_server_time(p, &attr->ia_atime);
38225a0866cSChuck Lever 	else
38325a0866cSChuck Lever 		p = xdr_time_not_set(p);
38425a0866cSChuck Lever 	if (attr->ia_valid & ATTR_MTIME_SET)
38525a0866cSChuck Lever 		xdr_encode_time(p, &attr->ia_mtime);
38625a0866cSChuck Lever 	else if (attr->ia_valid & ATTR_MTIME)
38725a0866cSChuck Lever 		xdr_encode_current_server_time(p, &attr->ia_mtime);
38825a0866cSChuck Lever 	else
38925a0866cSChuck Lever 		xdr_time_not_set(p);
39025a0866cSChuck Lever }
39125a0866cSChuck Lever 
39225a0866cSChuck Lever /*
39325a0866cSChuck Lever  * 2.3.7.  filename
39425a0866cSChuck Lever  *
39525a0866cSChuck Lever  *	typedef string filename<MAXNAMLEN>;
39625a0866cSChuck Lever  */
39725a0866cSChuck Lever static void encode_filename(struct xdr_stream *xdr,
39825a0866cSChuck Lever 			    const char *name, u32 length)
39925a0866cSChuck Lever {
40025a0866cSChuck Lever 	__be32 *p;
40125a0866cSChuck Lever 
4027fc38846STrond Myklebust 	WARN_ON_ONCE(length > NFS2_MAXNAMLEN);
40325a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + length);
40425a0866cSChuck Lever 	xdr_encode_opaque(p, name, length);
40525a0866cSChuck Lever }
40625a0866cSChuck Lever 
407f796f8b3SChuck Lever static int decode_filename_inline(struct xdr_stream *xdr,
408f796f8b3SChuck Lever 				  const char **name, u32 *length)
409f796f8b3SChuck Lever {
410f796f8b3SChuck Lever 	__be32 *p;
411f796f8b3SChuck Lever 	u32 count;
412f796f8b3SChuck Lever 
413f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
414f796f8b3SChuck Lever 	if (unlikely(p == NULL))
415f796f8b3SChuck Lever 		goto out_overflow;
416f796f8b3SChuck Lever 	count = be32_to_cpup(p);
417f796f8b3SChuck Lever 	if (count > NFS3_MAXNAMLEN)
418f796f8b3SChuck Lever 		goto out_nametoolong;
419f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, count);
420f796f8b3SChuck Lever 	if (unlikely(p == NULL))
421f796f8b3SChuck Lever 		goto out_overflow;
422f796f8b3SChuck Lever 	*name = (const char *)p;
423f796f8b3SChuck Lever 	*length = count;
424f796f8b3SChuck Lever 	return 0;
425f796f8b3SChuck Lever out_nametoolong:
426f796f8b3SChuck Lever 	dprintk("NFS: returned filename too long: %u\n", count);
427f796f8b3SChuck Lever 	return -ENAMETOOLONG;
428f796f8b3SChuck Lever out_overflow:
429f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
430f796f8b3SChuck Lever 	return -EIO;
431f796f8b3SChuck Lever }
432f796f8b3SChuck Lever 
43325a0866cSChuck Lever /*
43425a0866cSChuck Lever  * 2.3.8.  path
43525a0866cSChuck Lever  *
43625a0866cSChuck Lever  *	typedef string path<MAXPATHLEN>;
43725a0866cSChuck Lever  */
43825a0866cSChuck Lever static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length)
43925a0866cSChuck Lever {
44025a0866cSChuck Lever 	__be32 *p;
44125a0866cSChuck Lever 
44225a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4);
44325a0866cSChuck Lever 	*p = cpu_to_be32(length);
44425a0866cSChuck Lever 	xdr_write_pages(xdr, pages, 0, length);
44525a0866cSChuck Lever }
44625a0866cSChuck Lever 
447f796f8b3SChuck Lever static int decode_path(struct xdr_stream *xdr)
448f796f8b3SChuck Lever {
449f796f8b3SChuck Lever 	u32 length, recvd;
450f796f8b3SChuck Lever 	__be32 *p;
451f796f8b3SChuck Lever 
452f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
453f796f8b3SChuck Lever 	if (unlikely(p == NULL))
454f796f8b3SChuck Lever 		goto out_overflow;
455f796f8b3SChuck Lever 	length = be32_to_cpup(p);
456f796f8b3SChuck Lever 	if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN))
457f796f8b3SChuck Lever 		goto out_size;
45864bd577eSTrond Myklebust 	recvd = xdr_read_pages(xdr, length);
459f796f8b3SChuck Lever 	if (unlikely(length > recvd))
460f796f8b3SChuck Lever 		goto out_cheating;
461f796f8b3SChuck Lever 	xdr_terminate_string(xdr->buf, length);
462f796f8b3SChuck Lever 	return 0;
463f796f8b3SChuck Lever out_size:
464f796f8b3SChuck Lever 	dprintk("NFS: returned pathname too long: %u\n", length);
465f796f8b3SChuck Lever 	return -ENAMETOOLONG;
466f796f8b3SChuck Lever out_cheating:
467f796f8b3SChuck Lever 	dprintk("NFS: server cheating in pathname result: "
468f796f8b3SChuck Lever 		"length %u > received %u\n", length, recvd);
469f796f8b3SChuck Lever 	return -EIO;
470f796f8b3SChuck Lever out_overflow:
471f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
472f796f8b3SChuck Lever 	return -EIO;
473f796f8b3SChuck Lever }
474f796f8b3SChuck Lever 
475f796f8b3SChuck Lever /*
476f796f8b3SChuck Lever  * 2.3.9.  attrstat
477f796f8b3SChuck Lever  *
478f796f8b3SChuck Lever  *	union attrstat switch (stat status) {
479f796f8b3SChuck Lever  *	case NFS_OK:
480f796f8b3SChuck Lever  *		fattr attributes;
481f796f8b3SChuck Lever  *	default:
482f796f8b3SChuck Lever  *		void;
483f796f8b3SChuck Lever  *	};
484f796f8b3SChuck Lever  */
485aabff4ddSPeng Tao static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result,
486aabff4ddSPeng Tao 			   __u32 *op_status)
487f796f8b3SChuck Lever {
488f796f8b3SChuck Lever 	enum nfs_stat status;
489f796f8b3SChuck Lever 	int error;
490f796f8b3SChuck Lever 
491f796f8b3SChuck Lever 	error = decode_stat(xdr, &status);
492f796f8b3SChuck Lever 	if (unlikely(error))
493f796f8b3SChuck Lever 		goto out;
494aabff4ddSPeng Tao 	if (op_status)
495aabff4ddSPeng Tao 		*op_status = status;
496f796f8b3SChuck Lever 	if (status != NFS_OK)
497f796f8b3SChuck Lever 		goto out_default;
498f796f8b3SChuck Lever 	error = decode_fattr(xdr, result);
499f796f8b3SChuck Lever out:
500f796f8b3SChuck Lever 	return error;
501f796f8b3SChuck Lever out_default:
502f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
503f796f8b3SChuck Lever }
504f796f8b3SChuck Lever 
50525a0866cSChuck Lever /*
50625a0866cSChuck Lever  * 2.3.10.  diropargs
50725a0866cSChuck Lever  *
50825a0866cSChuck Lever  *	struct diropargs {
50925a0866cSChuck Lever  *		fhandle  dir;
51025a0866cSChuck Lever  *		filename name;
51125a0866cSChuck Lever  *	};
51225a0866cSChuck Lever  */
51325a0866cSChuck Lever static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh,
51425a0866cSChuck Lever 			     const char *name, u32 length)
51525a0866cSChuck Lever {
51625a0866cSChuck Lever 	encode_fhandle(xdr, fh);
51725a0866cSChuck Lever 	encode_filename(xdr, name, length);
51825a0866cSChuck Lever }
51925a0866cSChuck Lever 
520f796f8b3SChuck Lever /*
521f796f8b3SChuck Lever  * 2.3.11.  diropres
522f796f8b3SChuck Lever  *
523f796f8b3SChuck Lever  *	union diropres switch (stat status) {
524f796f8b3SChuck Lever  *	case NFS_OK:
525f796f8b3SChuck Lever  *		struct {
526f796f8b3SChuck Lever  *			fhandle file;
527f796f8b3SChuck Lever  *			fattr   attributes;
528f796f8b3SChuck Lever  *		} diropok;
529f796f8b3SChuck Lever  *	default:
530f796f8b3SChuck Lever  *		void;
531f796f8b3SChuck Lever  *	};
532f796f8b3SChuck Lever  */
533f796f8b3SChuck Lever static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result)
534f796f8b3SChuck Lever {
535f796f8b3SChuck Lever 	int error;
536f796f8b3SChuck Lever 
537f796f8b3SChuck Lever 	error = decode_fhandle(xdr, result->fh);
538f796f8b3SChuck Lever 	if (unlikely(error))
539f796f8b3SChuck Lever 		goto out;
540f796f8b3SChuck Lever 	error = decode_fattr(xdr, result->fattr);
541f796f8b3SChuck Lever out:
542f796f8b3SChuck Lever 	return error;
543f796f8b3SChuck Lever }
544f796f8b3SChuck Lever 
545f796f8b3SChuck Lever static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result)
546f796f8b3SChuck Lever {
547f796f8b3SChuck Lever 	enum nfs_stat status;
548f796f8b3SChuck Lever 	int error;
549f796f8b3SChuck Lever 
550f796f8b3SChuck Lever 	error = decode_stat(xdr, &status);
551f796f8b3SChuck Lever 	if (unlikely(error))
552f796f8b3SChuck Lever 		goto out;
553f796f8b3SChuck Lever 	if (status != NFS_OK)
554f796f8b3SChuck Lever 		goto out_default;
555f796f8b3SChuck Lever 	error = decode_diropok(xdr, result);
556f796f8b3SChuck Lever out:
557f796f8b3SChuck Lever 	return error;
558f796f8b3SChuck Lever out_default:
559f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
560f796f8b3SChuck Lever }
561f796f8b3SChuck Lever 
56225a0866cSChuck Lever 
56325a0866cSChuck Lever /*
5642d70f533SChuck Lever  * NFSv2 XDR encode functions
5652d70f533SChuck Lever  *
5662d70f533SChuck Lever  * NFSv2 argument types are defined in section 2.2 of RFC 1094:
5672d70f533SChuck Lever  * "NFS: Network File System Protocol Specification".
5681da177e4SLinus Torvalds  */
5691da177e4SLinus Torvalds 
5709f06c719SChuck Lever static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req,
5719f06c719SChuck Lever 				 struct xdr_stream *xdr,
572fcc85819SChristoph Hellwig 				 const void *data)
57325a0866cSChuck Lever {
574fcc85819SChristoph Hellwig 	const struct nfs_fh *fh = data;
575fcc85819SChristoph Hellwig 
5769f06c719SChuck Lever 	encode_fhandle(xdr, fh);
57725a0866cSChuck Lever }
57825a0866cSChuck Lever 
5791da177e4SLinus Torvalds /*
58025a0866cSChuck Lever  * 2.2.3.  sattrargs
58125a0866cSChuck Lever  *
58225a0866cSChuck Lever  *	struct sattrargs {
58325a0866cSChuck Lever  *		fhandle file;
58425a0866cSChuck Lever  *		sattr attributes;
58525a0866cSChuck Lever  *	};
58625a0866cSChuck Lever  */
5879f06c719SChuck Lever static void nfs2_xdr_enc_sattrargs(struct rpc_rqst *req,
5889f06c719SChuck Lever 				   struct xdr_stream *xdr,
589fcc85819SChristoph Hellwig 				   const void *data)
59025a0866cSChuck Lever {
591fcc85819SChristoph Hellwig 	const struct nfs_sattrargs *args = data;
592fcc85819SChristoph Hellwig 
5939f06c719SChuck Lever 	encode_fhandle(xdr, args->fh);
5949f06c719SChuck Lever 	encode_sattr(xdr, args->sattr);
59525a0866cSChuck Lever }
59625a0866cSChuck Lever 
5979f06c719SChuck Lever static void nfs2_xdr_enc_diropargs(struct rpc_rqst *req,
5989f06c719SChuck Lever 				   struct xdr_stream *xdr,
599fcc85819SChristoph Hellwig 				   const void *data)
60025a0866cSChuck Lever {
601fcc85819SChristoph Hellwig 	const struct nfs_diropargs *args = data;
602fcc85819SChristoph Hellwig 
6039f06c719SChuck Lever 	encode_diropargs(xdr, args->fh, args->name, args->len);
60425a0866cSChuck Lever }
60525a0866cSChuck Lever 
6069f06c719SChuck Lever static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req,
6079f06c719SChuck Lever 				      struct xdr_stream *xdr,
608fcc85819SChristoph Hellwig 				      const void *data)
60925a0866cSChuck Lever {
610fcc85819SChristoph Hellwig 	const struct nfs_readlinkargs *args = data;
611fcc85819SChristoph Hellwig 
6129f06c719SChuck Lever 	encode_fhandle(xdr, args->fh);
61325a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
61425a0866cSChuck Lever 					args->pglen, NFS_readlinkres_sz);
61525a0866cSChuck Lever }
61625a0866cSChuck Lever 
6174fdc17b2STrond Myklebust /*
61825a0866cSChuck Lever  * 2.2.7.  readargs
61925a0866cSChuck Lever  *
62025a0866cSChuck Lever  *	struct readargs {
62125a0866cSChuck Lever  *		fhandle file;
62225a0866cSChuck Lever  *		unsigned offset;
62325a0866cSChuck Lever  *		unsigned count;
62425a0866cSChuck Lever  *		unsigned totalcount;
62525a0866cSChuck Lever  *	};
62625a0866cSChuck Lever  */
62725a0866cSChuck Lever static void encode_readargs(struct xdr_stream *xdr,
6283c6b899cSAnna Schumaker 			    const struct nfs_pgio_args *args)
62925a0866cSChuck Lever {
63025a0866cSChuck Lever 	u32 offset = args->offset;
63125a0866cSChuck Lever 	u32 count = args->count;
63225a0866cSChuck Lever 	__be32 *p;
63325a0866cSChuck Lever 
63425a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
63525a0866cSChuck Lever 
63625a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4 + 4);
63725a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
63825a0866cSChuck Lever 	*p++ = cpu_to_be32(count);
63925a0866cSChuck Lever 	*p = cpu_to_be32(count);
64025a0866cSChuck Lever }
64125a0866cSChuck Lever 
6429f06c719SChuck Lever static void nfs2_xdr_enc_readargs(struct rpc_rqst *req,
6439f06c719SChuck Lever 				  struct xdr_stream *xdr,
644fcc85819SChristoph Hellwig 				  const void *data)
64525a0866cSChuck Lever {
646fcc85819SChristoph Hellwig 	const struct nfs_pgio_args *args = data;
647fcc85819SChristoph Hellwig 
6489f06c719SChuck Lever 	encode_readargs(xdr, args);
64925a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, args->pgbase,
65025a0866cSChuck Lever 					args->count, NFS_readres_sz);
65125a0866cSChuck Lever 	req->rq_rcv_buf.flags |= XDRBUF_READ;
65225a0866cSChuck Lever }
65325a0866cSChuck Lever 
65425a0866cSChuck Lever /*
65525a0866cSChuck Lever  * 2.2.9.  writeargs
65625a0866cSChuck Lever  *
65725a0866cSChuck Lever  *	struct writeargs {
65825a0866cSChuck Lever  *		fhandle file;
65925a0866cSChuck Lever  *		unsigned beginoffset;
66025a0866cSChuck Lever  *		unsigned offset;
66125a0866cSChuck Lever  *		unsigned totalcount;
66225a0866cSChuck Lever  *		nfsdata data;
66325a0866cSChuck Lever  *	};
66425a0866cSChuck Lever  */
66525a0866cSChuck Lever static void encode_writeargs(struct xdr_stream *xdr,
6663c6b899cSAnna Schumaker 			     const struct nfs_pgio_args *args)
66725a0866cSChuck Lever {
66825a0866cSChuck Lever 	u32 offset = args->offset;
66925a0866cSChuck Lever 	u32 count = args->count;
67025a0866cSChuck Lever 	__be32 *p;
67125a0866cSChuck Lever 
67225a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
67325a0866cSChuck Lever 
67425a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4);
67525a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
67625a0866cSChuck Lever 	*p++ = cpu_to_be32(offset);
67725a0866cSChuck Lever 	*p++ = cpu_to_be32(count);
67825a0866cSChuck Lever 
67925a0866cSChuck Lever 	/* nfsdata */
68025a0866cSChuck Lever 	*p = cpu_to_be32(count);
68125a0866cSChuck Lever 	xdr_write_pages(xdr, args->pages, args->pgbase, count);
68225a0866cSChuck Lever }
68325a0866cSChuck Lever 
6849f06c719SChuck Lever static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req,
6859f06c719SChuck Lever 				   struct xdr_stream *xdr,
686fcc85819SChristoph Hellwig 				   const void *data)
68725a0866cSChuck Lever {
688fcc85819SChristoph Hellwig 	const struct nfs_pgio_args *args = data;
689fcc85819SChristoph Hellwig 
6909f06c719SChuck Lever 	encode_writeargs(xdr, args);
6919f06c719SChuck Lever 	xdr->buf->flags |= XDRBUF_WRITE;
69225a0866cSChuck Lever }
69325a0866cSChuck Lever 
69425a0866cSChuck Lever /*
69525a0866cSChuck Lever  * 2.2.10.  createargs
69625a0866cSChuck Lever  *
69725a0866cSChuck Lever  *	struct createargs {
69825a0866cSChuck Lever  *		diropargs where;
69925a0866cSChuck Lever  *		sattr attributes;
70025a0866cSChuck Lever  *	};
70125a0866cSChuck Lever  */
7029f06c719SChuck Lever static void nfs2_xdr_enc_createargs(struct rpc_rqst *req,
7039f06c719SChuck Lever 				    struct xdr_stream *xdr,
704fcc85819SChristoph Hellwig 				    const void *data)
70525a0866cSChuck Lever {
706fcc85819SChristoph Hellwig 	const struct nfs_createargs *args = data;
707fcc85819SChristoph Hellwig 
7089f06c719SChuck Lever 	encode_diropargs(xdr, args->fh, args->name, args->len);
7099f06c719SChuck Lever 	encode_sattr(xdr, args->sattr);
71025a0866cSChuck Lever }
71125a0866cSChuck Lever 
7129f06c719SChuck Lever static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req,
7139f06c719SChuck Lever 				    struct xdr_stream *xdr,
714fcc85819SChristoph Hellwig 				    const void *data)
71525a0866cSChuck Lever {
716fcc85819SChristoph Hellwig 	const struct nfs_removeargs *args = data;
717fcc85819SChristoph Hellwig 
7189f06c719SChuck Lever 	encode_diropargs(xdr, args->fh, args->name.name, args->name.len);
71925a0866cSChuck Lever }
72025a0866cSChuck Lever 
72125a0866cSChuck Lever /*
72225a0866cSChuck Lever  * 2.2.12.  renameargs
72325a0866cSChuck Lever  *
72425a0866cSChuck Lever  *	struct renameargs {
72525a0866cSChuck Lever  *		diropargs from;
72625a0866cSChuck Lever  *		diropargs to;
72725a0866cSChuck Lever  *	};
72825a0866cSChuck Lever  */
7299f06c719SChuck Lever static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req,
7309f06c719SChuck Lever 				    struct xdr_stream *xdr,
731fcc85819SChristoph Hellwig 				    const void *data)
73225a0866cSChuck Lever {
733fcc85819SChristoph Hellwig 	const struct nfs_renameargs *args = data;
73425a0866cSChuck Lever 	const struct qstr *old = args->old_name;
73525a0866cSChuck Lever 	const struct qstr *new = args->new_name;
73625a0866cSChuck Lever 
7379f06c719SChuck Lever 	encode_diropargs(xdr, args->old_dir, old->name, old->len);
7389f06c719SChuck Lever 	encode_diropargs(xdr, args->new_dir, new->name, new->len);
73925a0866cSChuck Lever }
74025a0866cSChuck Lever 
74125a0866cSChuck Lever /*
74225a0866cSChuck Lever  * 2.2.13.  linkargs
74325a0866cSChuck Lever  *
74425a0866cSChuck Lever  *	struct linkargs {
74525a0866cSChuck Lever  *		fhandle from;
74625a0866cSChuck Lever  *		diropargs to;
74725a0866cSChuck Lever  *	};
74825a0866cSChuck Lever  */
7499f06c719SChuck Lever static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req,
7509f06c719SChuck Lever 				  struct xdr_stream *xdr,
751fcc85819SChristoph Hellwig 				  const void *data)
75225a0866cSChuck Lever {
753fcc85819SChristoph Hellwig 	const struct nfs_linkargs *args = data;
754fcc85819SChristoph Hellwig 
7559f06c719SChuck Lever 	encode_fhandle(xdr, args->fromfh);
7569f06c719SChuck Lever 	encode_diropargs(xdr, args->tofh, args->toname, args->tolen);
75725a0866cSChuck Lever }
75825a0866cSChuck Lever 
75925a0866cSChuck Lever /*
76025a0866cSChuck Lever  * 2.2.14.  symlinkargs
76125a0866cSChuck Lever  *
76225a0866cSChuck Lever  *	struct symlinkargs {
76325a0866cSChuck Lever  *		diropargs from;
76425a0866cSChuck Lever  *		path to;
76525a0866cSChuck Lever  *		sattr attributes;
76625a0866cSChuck Lever  *	};
76725a0866cSChuck Lever  */
7689f06c719SChuck Lever static void nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req,
7699f06c719SChuck Lever 				     struct xdr_stream *xdr,
770fcc85819SChristoph Hellwig 				     const void *data)
77125a0866cSChuck Lever {
772fcc85819SChristoph Hellwig 	const struct nfs_symlinkargs *args = data;
773fcc85819SChristoph Hellwig 
7749f06c719SChuck Lever 	encode_diropargs(xdr, args->fromfh, args->fromname, args->fromlen);
7759f06c719SChuck Lever 	encode_path(xdr, args->pages, args->pathlen);
7769f06c719SChuck Lever 	encode_sattr(xdr, args->sattr);
77725a0866cSChuck Lever }
77825a0866cSChuck Lever 
77925a0866cSChuck Lever /*
78025a0866cSChuck Lever  * 2.2.17.  readdirargs
78125a0866cSChuck Lever  *
78225a0866cSChuck Lever  *	struct readdirargs {
78325a0866cSChuck Lever  *		fhandle dir;
78425a0866cSChuck Lever  *		nfscookie cookie;
78525a0866cSChuck Lever  *		unsigned count;
78625a0866cSChuck Lever  *	};
78725a0866cSChuck Lever  */
78825a0866cSChuck Lever static void encode_readdirargs(struct xdr_stream *xdr,
78925a0866cSChuck Lever 			       const struct nfs_readdirargs *args)
79025a0866cSChuck Lever {
79125a0866cSChuck Lever 	__be32 *p;
79225a0866cSChuck Lever 
79325a0866cSChuck Lever 	encode_fhandle(xdr, args->fh);
79425a0866cSChuck Lever 
79525a0866cSChuck Lever 	p = xdr_reserve_space(xdr, 4 + 4);
79625a0866cSChuck Lever 	*p++ = cpu_to_be32(args->cookie);
79725a0866cSChuck Lever 	*p = cpu_to_be32(args->count);
79825a0866cSChuck Lever }
79925a0866cSChuck Lever 
8009f06c719SChuck Lever static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req,
8019f06c719SChuck Lever 				     struct xdr_stream *xdr,
802fcc85819SChristoph Hellwig 				     const void *data)
80325a0866cSChuck Lever {
804fcc85819SChristoph Hellwig 	const struct nfs_readdirargs *args = data;
805fcc85819SChristoph Hellwig 
8069f06c719SChuck Lever 	encode_readdirargs(xdr, args);
80725a0866cSChuck Lever 	prepare_reply_buffer(req, args->pages, 0,
80825a0866cSChuck Lever 					args->count, NFS_readdirres_sz);
80925a0866cSChuck Lever }
81025a0866cSChuck Lever 
81125a0866cSChuck Lever /*
812661ad423SChuck Lever  * NFSv2 XDR decode functions
813661ad423SChuck Lever  *
814661ad423SChuck Lever  * NFSv2 result types are defined in section 2.2 of RFC 1094:
815661ad423SChuck Lever  * "NFS: Network File System Protocol Specification".
8161da177e4SLinus Torvalds  */
8171da177e4SLinus Torvalds 
818bf269551SChuck Lever static int nfs2_xdr_dec_stat(struct rpc_rqst *req, struct xdr_stream *xdr,
819f796f8b3SChuck Lever 			     void *__unused)
820f796f8b3SChuck Lever {
821f796f8b3SChuck Lever 	enum nfs_stat status;
822f796f8b3SChuck Lever 	int error;
823f796f8b3SChuck Lever 
824bf269551SChuck Lever 	error = decode_stat(xdr, &status);
825f796f8b3SChuck Lever 	if (unlikely(error))
826f796f8b3SChuck Lever 		goto out;
827f796f8b3SChuck Lever 	if (status != NFS_OK)
828f796f8b3SChuck Lever 		goto out_default;
829f796f8b3SChuck Lever out:
830f796f8b3SChuck Lever 	return error;
831f796f8b3SChuck Lever out_default:
832f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
833f796f8b3SChuck Lever }
834f796f8b3SChuck Lever 
835bf269551SChuck Lever static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, struct xdr_stream *xdr,
836fc016483SChristoph Hellwig 				 void *result)
837f796f8b3SChuck Lever {
838aabff4ddSPeng Tao 	return decode_attrstat(xdr, result, NULL);
839f796f8b3SChuck Lever }
840f796f8b3SChuck Lever 
841bf269551SChuck Lever static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, struct xdr_stream *xdr,
842fc016483SChristoph Hellwig 				 void *result)
843f796f8b3SChuck Lever {
844bf269551SChuck Lever 	return decode_diropres(xdr, result);
845f796f8b3SChuck Lever }
846f796f8b3SChuck Lever 
8471da177e4SLinus Torvalds /*
848f796f8b3SChuck Lever  * 2.2.6.  readlinkres
849f796f8b3SChuck Lever  *
850f796f8b3SChuck Lever  *	union readlinkres switch (stat status) {
851f796f8b3SChuck Lever  *	case NFS_OK:
852f796f8b3SChuck Lever  *		path data;
853f796f8b3SChuck Lever  *	default:
854f796f8b3SChuck Lever  *		void;
855f796f8b3SChuck Lever  *	};
856f796f8b3SChuck Lever  */
857bf269551SChuck Lever static int nfs2_xdr_dec_readlinkres(struct rpc_rqst *req,
858bf269551SChuck Lever 				    struct xdr_stream *xdr, void *__unused)
859f796f8b3SChuck Lever {
860f796f8b3SChuck Lever 	enum nfs_stat status;
861f796f8b3SChuck Lever 	int error;
862f796f8b3SChuck Lever 
863bf269551SChuck Lever 	error = decode_stat(xdr, &status);
864f796f8b3SChuck Lever 	if (unlikely(error))
865f796f8b3SChuck Lever 		goto out;
866f796f8b3SChuck Lever 	if (status != NFS_OK)
867f796f8b3SChuck Lever 		goto out_default;
868bf269551SChuck Lever 	error = decode_path(xdr);
869f796f8b3SChuck Lever out:
870f796f8b3SChuck Lever 	return error;
871f796f8b3SChuck Lever out_default:
872f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
873f796f8b3SChuck Lever }
874f796f8b3SChuck Lever 
875f796f8b3SChuck Lever /*
876f796f8b3SChuck Lever  * 2.2.7.  readres
877f796f8b3SChuck Lever  *
878f796f8b3SChuck Lever  *	union readres switch (stat status) {
879f796f8b3SChuck Lever  *	case NFS_OK:
880f796f8b3SChuck Lever  *		fattr attributes;
881f796f8b3SChuck Lever  *		nfsdata data;
882f796f8b3SChuck Lever  *	default:
883f796f8b3SChuck Lever  *		void;
884f796f8b3SChuck Lever  *	};
885f796f8b3SChuck Lever  */
886bf269551SChuck Lever static int nfs2_xdr_dec_readres(struct rpc_rqst *req, struct xdr_stream *xdr,
887fc016483SChristoph Hellwig 				void *data)
888f796f8b3SChuck Lever {
889fc016483SChristoph Hellwig 	struct nfs_pgio_res *result = data;
890f796f8b3SChuck Lever 	enum nfs_stat status;
891f796f8b3SChuck Lever 	int error;
892f796f8b3SChuck Lever 
893bf269551SChuck Lever 	error = decode_stat(xdr, &status);
894f796f8b3SChuck Lever 	if (unlikely(error))
895f796f8b3SChuck Lever 		goto out;
896aabff4ddSPeng Tao 	result->op_status = status;
897f796f8b3SChuck Lever 	if (status != NFS_OK)
898f796f8b3SChuck Lever 		goto out_default;
899bf269551SChuck Lever 	error = decode_fattr(xdr, result->fattr);
900f796f8b3SChuck Lever 	if (unlikely(error))
901f796f8b3SChuck Lever 		goto out;
902bf269551SChuck Lever 	error = decode_nfsdata(xdr, result);
903f796f8b3SChuck Lever out:
904f796f8b3SChuck Lever 	return error;
905f796f8b3SChuck Lever out_default:
906f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
907f796f8b3SChuck Lever }
908f796f8b3SChuck Lever 
909bf269551SChuck Lever static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, struct xdr_stream *xdr,
910fc016483SChristoph Hellwig 				 void *data)
911f796f8b3SChuck Lever {
912fc016483SChristoph Hellwig 	struct nfs_pgio_res *result = data;
913fc016483SChristoph Hellwig 
914f796f8b3SChuck Lever 	/* All NFSv2 writes are "file sync" writes */
915f796f8b3SChuck Lever 	result->verf->committed = NFS_FILE_SYNC;
916aabff4ddSPeng Tao 	return decode_attrstat(xdr, result->fattr, &result->op_status);
917f796f8b3SChuck Lever }
918f796f8b3SChuck Lever 
919f796f8b3SChuck Lever /**
920f796f8b3SChuck Lever  * nfs2_decode_dirent - Decode a single NFSv2 directory entry stored in
921f796f8b3SChuck Lever  *                      the local page cache.
922f796f8b3SChuck Lever  * @xdr: XDR stream where entry resides
923f796f8b3SChuck Lever  * @entry: buffer to fill in with entry data
924f796f8b3SChuck Lever  * @plus: boolean indicating whether this should be a readdirplus entry
925f796f8b3SChuck Lever  *
926573c4e1eSChuck Lever  * Returns zero if successful, otherwise a negative errno value is
927573c4e1eSChuck Lever  * returned.
928f796f8b3SChuck Lever  *
929f796f8b3SChuck Lever  * This function is not invoked during READDIR reply decoding, but
930f796f8b3SChuck Lever  * rather whenever an application invokes the getdents(2) system call
931f796f8b3SChuck Lever  * on a directory already in our cache.
932f796f8b3SChuck Lever  *
933f796f8b3SChuck Lever  * 2.2.17.  entry
934f796f8b3SChuck Lever  *
935f796f8b3SChuck Lever  *	struct entry {
936f796f8b3SChuck Lever  *		unsigned	fileid;
937f796f8b3SChuck Lever  *		filename	name;
938f796f8b3SChuck Lever  *		nfscookie	cookie;
939f796f8b3SChuck Lever  *		entry		*nextentry;
940f796f8b3SChuck Lever  *	};
941f796f8b3SChuck Lever  */
942573c4e1eSChuck Lever int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
943a7a3b1e9SBenjamin Coddington 		       bool plus)
944f796f8b3SChuck Lever {
945f796f8b3SChuck Lever 	__be32 *p;
946f796f8b3SChuck Lever 	int error;
947f796f8b3SChuck Lever 
948f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
949f796f8b3SChuck Lever 	if (unlikely(p == NULL))
950f796f8b3SChuck Lever 		goto out_overflow;
951f796f8b3SChuck Lever 	if (*p++ == xdr_zero) {
952f796f8b3SChuck Lever 		p = xdr_inline_decode(xdr, 4);
953f796f8b3SChuck Lever 		if (unlikely(p == NULL))
954f796f8b3SChuck Lever 			goto out_overflow;
955f796f8b3SChuck Lever 		if (*p++ == xdr_zero)
956573c4e1eSChuck Lever 			return -EAGAIN;
957f796f8b3SChuck Lever 		entry->eof = 1;
958573c4e1eSChuck Lever 		return -EBADCOOKIE;
959f796f8b3SChuck Lever 	}
960f796f8b3SChuck Lever 
961f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
962f796f8b3SChuck Lever 	if (unlikely(p == NULL))
963f796f8b3SChuck Lever 		goto out_overflow;
964f796f8b3SChuck Lever 	entry->ino = be32_to_cpup(p);
965f796f8b3SChuck Lever 
966f796f8b3SChuck Lever 	error = decode_filename_inline(xdr, &entry->name, &entry->len);
967f796f8b3SChuck Lever 	if (unlikely(error))
968573c4e1eSChuck Lever 		return error;
969f796f8b3SChuck Lever 
970f796f8b3SChuck Lever 	/*
971f796f8b3SChuck Lever 	 * The type (size and byte order) of nfscookie isn't defined in
972f796f8b3SChuck Lever 	 * RFC 1094.  This implementation assumes that it's an XDR uint32.
973f796f8b3SChuck Lever 	 */
974f796f8b3SChuck Lever 	entry->prev_cookie = entry->cookie;
975f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, 4);
976f796f8b3SChuck Lever 	if (unlikely(p == NULL))
977f796f8b3SChuck Lever 		goto out_overflow;
978f796f8b3SChuck Lever 	entry->cookie = be32_to_cpup(p);
979f796f8b3SChuck Lever 
980f796f8b3SChuck Lever 	entry->d_type = DT_UNKNOWN;
981f796f8b3SChuck Lever 
982573c4e1eSChuck Lever 	return 0;
983f796f8b3SChuck Lever 
984f796f8b3SChuck Lever out_overflow:
985f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
986573c4e1eSChuck Lever 	return -EAGAIN;
987f796f8b3SChuck Lever }
988f796f8b3SChuck Lever 
989f796f8b3SChuck Lever /*
990f796f8b3SChuck Lever  * 2.2.17.  readdirres
991f796f8b3SChuck Lever  *
992f796f8b3SChuck Lever  *	union readdirres switch (stat status) {
993f796f8b3SChuck Lever  *	case NFS_OK:
994f796f8b3SChuck Lever  *		struct {
995f796f8b3SChuck Lever  *			entry *entries;
996f796f8b3SChuck Lever  *			bool eof;
997f796f8b3SChuck Lever  *		} readdirok;
998f796f8b3SChuck Lever  *	default:
999f796f8b3SChuck Lever  *		void;
1000f796f8b3SChuck Lever  *	};
1001f796f8b3SChuck Lever  *
1002f796f8b3SChuck Lever  * Read the directory contents into the page cache, but don't
1003f796f8b3SChuck Lever  * touch them.  The actual decoding is done by nfs2_decode_dirent()
1004f796f8b3SChuck Lever  * during subsequent nfs_readdir() calls.
1005f796f8b3SChuck Lever  */
1006f796f8b3SChuck Lever static int decode_readdirok(struct xdr_stream *xdr)
1007f796f8b3SChuck Lever {
100864bd577eSTrond Myklebust 	return xdr_read_pages(xdr, xdr->buf->page_len);
1009f796f8b3SChuck Lever }
1010f796f8b3SChuck Lever 
1011bf269551SChuck Lever static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req,
1012bf269551SChuck Lever 				   struct xdr_stream *xdr, void *__unused)
1013f796f8b3SChuck Lever {
1014f796f8b3SChuck Lever 	enum nfs_stat status;
1015f796f8b3SChuck Lever 	int error;
1016f796f8b3SChuck Lever 
1017bf269551SChuck Lever 	error = decode_stat(xdr, &status);
1018f796f8b3SChuck Lever 	if (unlikely(error))
1019f796f8b3SChuck Lever 		goto out;
1020f796f8b3SChuck Lever 	if (status != NFS_OK)
1021f796f8b3SChuck Lever 		goto out_default;
1022bf269551SChuck Lever 	error = decode_readdirok(xdr);
1023f796f8b3SChuck Lever out:
1024f796f8b3SChuck Lever 	return error;
1025f796f8b3SChuck Lever out_default:
1026f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
1027f796f8b3SChuck Lever }
1028f796f8b3SChuck Lever 
10291da177e4SLinus Torvalds /*
1030f796f8b3SChuck Lever  * 2.2.18.  statfsres
1031f796f8b3SChuck Lever  *
1032f796f8b3SChuck Lever  *	union statfsres (stat status) {
1033f796f8b3SChuck Lever  *	case NFS_OK:
1034f796f8b3SChuck Lever  *		struct {
1035f796f8b3SChuck Lever  *			unsigned tsize;
1036f796f8b3SChuck Lever  *			unsigned bsize;
1037f796f8b3SChuck Lever  *			unsigned blocks;
1038f796f8b3SChuck Lever  *			unsigned bfree;
1039f796f8b3SChuck Lever  *			unsigned bavail;
1040f796f8b3SChuck Lever  *		} info;
1041f796f8b3SChuck Lever  *	default:
1042f796f8b3SChuck Lever  *		void;
1043f796f8b3SChuck Lever  *	};
1044f796f8b3SChuck Lever  */
1045f796f8b3SChuck Lever static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result)
1046f796f8b3SChuck Lever {
1047f796f8b3SChuck Lever 	__be32 *p;
1048f796f8b3SChuck Lever 
1049f796f8b3SChuck Lever 	p = xdr_inline_decode(xdr, NFS_info_sz << 2);
1050f796f8b3SChuck Lever 	if (unlikely(p == NULL))
1051f796f8b3SChuck Lever 		goto out_overflow;
1052f796f8b3SChuck Lever 	result->tsize  = be32_to_cpup(p++);
1053f796f8b3SChuck Lever 	result->bsize  = be32_to_cpup(p++);
1054f796f8b3SChuck Lever 	result->blocks = be32_to_cpup(p++);
1055f796f8b3SChuck Lever 	result->bfree  = be32_to_cpup(p++);
1056f796f8b3SChuck Lever 	result->bavail = be32_to_cpup(p);
1057f796f8b3SChuck Lever 	return 0;
1058f796f8b3SChuck Lever out_overflow:
1059f796f8b3SChuck Lever 	print_overflow_msg(__func__, xdr);
1060f796f8b3SChuck Lever 	return -EIO;
1061f796f8b3SChuck Lever }
1062f796f8b3SChuck Lever 
1063bf269551SChuck Lever static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr,
1064fc016483SChristoph Hellwig 				  void *result)
1065f796f8b3SChuck Lever {
1066f796f8b3SChuck Lever 	enum nfs_stat status;
1067f796f8b3SChuck Lever 	int error;
1068f796f8b3SChuck Lever 
1069bf269551SChuck Lever 	error = decode_stat(xdr, &status);
1070f796f8b3SChuck Lever 	if (unlikely(error))
1071f796f8b3SChuck Lever 		goto out;
1072f796f8b3SChuck Lever 	if (status != NFS_OK)
1073f796f8b3SChuck Lever 		goto out_default;
1074bf269551SChuck Lever 	error = decode_info(xdr, result);
1075f796f8b3SChuck Lever out:
1076f796f8b3SChuck Lever 	return error;
1077f796f8b3SChuck Lever out_default:
1078f796f8b3SChuck Lever 	return nfs_stat_to_errno(status);
1079f796f8b3SChuck Lever }
1080f796f8b3SChuck Lever 
1081f796f8b3SChuck Lever 
1082f796f8b3SChuck Lever /*
10831da177e4SLinus Torvalds  * We need to translate between nfs status return values and
10841da177e4SLinus Torvalds  * the local errno values which may not be the same.
10851da177e4SLinus Torvalds  */
108685828493SChuck Lever static const struct {
10871da177e4SLinus Torvalds 	int stat;
10881da177e4SLinus Torvalds 	int errno;
10891da177e4SLinus Torvalds } nfs_errtbl[] = {
10901da177e4SLinus Torvalds 	{ NFS_OK,		0		},
1091856dff3dSBenny Halevy 	{ NFSERR_PERM,		-EPERM		},
1092856dff3dSBenny Halevy 	{ NFSERR_NOENT,		-ENOENT		},
1093856dff3dSBenny Halevy 	{ NFSERR_IO,		-errno_NFSERR_IO},
1094856dff3dSBenny Halevy 	{ NFSERR_NXIO,		-ENXIO		},
1095856dff3dSBenny Halevy /*	{ NFSERR_EAGAIN,	-EAGAIN		}, */
1096856dff3dSBenny Halevy 	{ NFSERR_ACCES,		-EACCES		},
1097856dff3dSBenny Halevy 	{ NFSERR_EXIST,		-EEXIST		},
1098856dff3dSBenny Halevy 	{ NFSERR_XDEV,		-EXDEV		},
1099856dff3dSBenny Halevy 	{ NFSERR_NODEV,		-ENODEV		},
1100856dff3dSBenny Halevy 	{ NFSERR_NOTDIR,	-ENOTDIR	},
1101856dff3dSBenny Halevy 	{ NFSERR_ISDIR,		-EISDIR		},
1102856dff3dSBenny Halevy 	{ NFSERR_INVAL,		-EINVAL		},
1103856dff3dSBenny Halevy 	{ NFSERR_FBIG,		-EFBIG		},
1104856dff3dSBenny Halevy 	{ NFSERR_NOSPC,		-ENOSPC		},
1105856dff3dSBenny Halevy 	{ NFSERR_ROFS,		-EROFS		},
1106856dff3dSBenny Halevy 	{ NFSERR_MLINK,		-EMLINK		},
1107856dff3dSBenny Halevy 	{ NFSERR_NAMETOOLONG,	-ENAMETOOLONG	},
1108856dff3dSBenny Halevy 	{ NFSERR_NOTEMPTY,	-ENOTEMPTY	},
1109856dff3dSBenny Halevy 	{ NFSERR_DQUOT,		-EDQUOT		},
1110856dff3dSBenny Halevy 	{ NFSERR_STALE,		-ESTALE		},
1111856dff3dSBenny Halevy 	{ NFSERR_REMOTE,	-EREMOTE	},
11121da177e4SLinus Torvalds #ifdef EWFLUSH
1113856dff3dSBenny Halevy 	{ NFSERR_WFLUSH,	-EWFLUSH	},
11141da177e4SLinus Torvalds #endif
1115856dff3dSBenny Halevy 	{ NFSERR_BADHANDLE,	-EBADHANDLE	},
1116856dff3dSBenny Halevy 	{ NFSERR_NOT_SYNC,	-ENOTSYNC	},
1117856dff3dSBenny Halevy 	{ NFSERR_BAD_COOKIE,	-EBADCOOKIE	},
1118856dff3dSBenny Halevy 	{ NFSERR_NOTSUPP,	-ENOTSUPP	},
1119856dff3dSBenny Halevy 	{ NFSERR_TOOSMALL,	-ETOOSMALL	},
1120fdcb4577STrond Myklebust 	{ NFSERR_SERVERFAULT,	-EREMOTEIO	},
1121856dff3dSBenny Halevy 	{ NFSERR_BADTYPE,	-EBADTYPE	},
1122856dff3dSBenny Halevy 	{ NFSERR_JUKEBOX,	-EJUKEBOX	},
1123856dff3dSBenny Halevy 	{ -1,			-EIO		}
11241da177e4SLinus Torvalds };
11251da177e4SLinus Torvalds 
112685828493SChuck Lever /**
112785828493SChuck Lever  * nfs_stat_to_errno - convert an NFS status code to a local errno
112885828493SChuck Lever  * @status: NFS status code to convert
112985828493SChuck Lever  *
113085828493SChuck Lever  * Returns a local errno value, or -EIO if the NFS status code is
113185828493SChuck Lever  * not recognized.  This function is used jointly by NFSv2 and NFSv3.
11321da177e4SLinus Torvalds  */
11335e7e5a0dSBryan Schumaker static int nfs_stat_to_errno(enum nfs_stat status)
11341da177e4SLinus Torvalds {
11351da177e4SLinus Torvalds 	int i;
11361da177e4SLinus Torvalds 
11371da177e4SLinus Torvalds 	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
113885828493SChuck Lever 		if (nfs_errtbl[i].stat == (int)status)
11391da177e4SLinus Torvalds 			return nfs_errtbl[i].errno;
11401da177e4SLinus Torvalds 	}
114185828493SChuck Lever 	dprintk("NFS: Unrecognized nfs status value: %u\n", status);
11421da177e4SLinus Torvalds 	return nfs_errtbl[i].errno;
11431da177e4SLinus Torvalds }
11441da177e4SLinus Torvalds 
11451da177e4SLinus Torvalds #define PROC(proc, argtype, restype, timer)				\
11461da177e4SLinus Torvalds [NFSPROC_##proc] = {							\
11471da177e4SLinus Torvalds 	.p_proc	    =  NFSPROC_##proc,					\
1148fcc85819SChristoph Hellwig 	.p_encode   =  nfs2_xdr_enc_##argtype,				\
1149fc016483SChristoph Hellwig 	.p_decode   =  nfs2_xdr_dec_##restype,				\
11502bea90d4SChuck Lever 	.p_arglen   =  NFS_##argtype##_sz,				\
11512bea90d4SChuck Lever 	.p_replen   =  NFS_##restype##_sz,				\
1152cc0175c1SChuck Lever 	.p_timer    =  timer,						\
1153cc0175c1SChuck Lever 	.p_statidx  =  NFSPROC_##proc,					\
1154cc0175c1SChuck Lever 	.p_name     =  #proc,						\
11551da177e4SLinus Torvalds 	}
1156511e936bSChristoph Hellwig const struct rpc_procinfo nfs_procedures[] = {
11571da177e4SLinus Torvalds 	PROC(GETATTR,	fhandle,	attrstat,	1),
11581da177e4SLinus Torvalds 	PROC(SETATTR,	sattrargs,	attrstat,	0),
11591da177e4SLinus Torvalds 	PROC(LOOKUP,	diropargs,	diropres,	2),
11601da177e4SLinus Torvalds 	PROC(READLINK,	readlinkargs,	readlinkres,	3),
11611da177e4SLinus Torvalds 	PROC(READ,	readargs,	readres,	3),
11621da177e4SLinus Torvalds 	PROC(WRITE,	writeargs,	writeres,	4),
11631da177e4SLinus Torvalds 	PROC(CREATE,	createargs,	diropres,	0),
11644fdc17b2STrond Myklebust 	PROC(REMOVE,	removeargs,	stat,		0),
11651da177e4SLinus Torvalds 	PROC(RENAME,	renameargs,	stat,		0),
11661da177e4SLinus Torvalds 	PROC(LINK,	linkargs,	stat,		0),
11671da177e4SLinus Torvalds 	PROC(SYMLINK,	symlinkargs,	stat,		0),
11681da177e4SLinus Torvalds 	PROC(MKDIR,	createargs,	diropres,	0),
11691da177e4SLinus Torvalds 	PROC(RMDIR,	diropargs,	stat,		0),
11701da177e4SLinus Torvalds 	PROC(READDIR,	readdirargs,	readdirres,	3),
11711da177e4SLinus Torvalds 	PROC(STATFS,	fhandle,	statfsres,	0),
11721da177e4SLinus Torvalds };
11731da177e4SLinus Torvalds 
1174c551858aSChristoph Hellwig static unsigned int nfs_version2_counts[ARRAY_SIZE(nfs_procedures)];
1175a613fa16STrond Myklebust const struct rpc_version nfs_version2 = {
11761da177e4SLinus Torvalds 	.number			= 2,
1177e8c96f8cSTobias Klauser 	.nrprocs		= ARRAY_SIZE(nfs_procedures),
1178c551858aSChristoph Hellwig 	.procs			= nfs_procedures,
1179c551858aSChristoph Hellwig 	.counts			= nfs_version2_counts,
11801da177e4SLinus Torvalds };
1181