 aabff4ddca
			
		
	
	
	aabff4ddca
	
	
	
		
			
			Flexfiles layout would want to use them to report DS IO status. Signed-off-by: Peng Tao <tao.peng@primarydata.com> Signed-off-by: Tom Haynes <Thomas.Haynes@primarydata.com>
		
			
				
	
	
		
			1151 lines
		
	
	
	
		
			26 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1151 lines
		
	
	
	
		
			26 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * linux/fs/nfs/nfs2xdr.c
 | |
|  *
 | |
|  * XDR functions to encode/decode NFS RPC arguments and results.
 | |
|  *
 | |
|  * Copyright (C) 1992, 1993, 1994  Rick Sladkey
 | |
|  * Copyright (C) 1996 Olaf Kirch
 | |
|  * 04 Aug 1998  Ion Badulescu <ionut@cs.columbia.edu>
 | |
|  * 		FIFO's need special handling in NFSv2
 | |
|  */
 | |
| 
 | |
| #include <linux/param.h>
 | |
| #include <linux/time.h>
 | |
| #include <linux/mm.h>
 | |
| #include <linux/errno.h>
 | |
| #include <linux/string.h>
 | |
| #include <linux/in.h>
 | |
| #include <linux/pagemap.h>
 | |
| #include <linux/proc_fs.h>
 | |
| #include <linux/sunrpc/clnt.h>
 | |
| #include <linux/nfs.h>
 | |
| #include <linux/nfs2.h>
 | |
| #include <linux/nfs_fs.h>
 | |
| #include "internal.h"
 | |
| 
 | |
| #define NFSDBG_FACILITY		NFSDBG_XDR
 | |
| 
 | |
| /* Mapping from NFS error code to "errno" error code. */
 | |
| #define errno_NFSERR_IO		EIO
 | |
| 
 | |
| /*
 | |
|  * Declare the space requirements for NFS arguments and replies as
 | |
|  * number of 32bit-words
 | |
|  */
 | |
| #define NFS_fhandle_sz		(8)
 | |
| #define NFS_sattr_sz		(8)
 | |
| #define NFS_filename_sz		(1+(NFS2_MAXNAMLEN>>2))
 | |
| #define NFS_path_sz		(1+(NFS2_MAXPATHLEN>>2))
 | |
| #define NFS_fattr_sz		(17)
 | |
| #define NFS_info_sz		(5)
 | |
| #define NFS_entry_sz		(NFS_filename_sz+3)
 | |
| 
 | |
| #define NFS_diropargs_sz	(NFS_fhandle_sz+NFS_filename_sz)
 | |
| #define NFS_removeargs_sz	(NFS_fhandle_sz+NFS_filename_sz)
 | |
| #define NFS_sattrargs_sz	(NFS_fhandle_sz+NFS_sattr_sz)
 | |
| #define NFS_readlinkargs_sz	(NFS_fhandle_sz)
 | |
| #define NFS_readargs_sz		(NFS_fhandle_sz+3)
 | |
| #define NFS_writeargs_sz	(NFS_fhandle_sz+4)
 | |
| #define NFS_createargs_sz	(NFS_diropargs_sz+NFS_sattr_sz)
 | |
| #define NFS_renameargs_sz	(NFS_diropargs_sz+NFS_diropargs_sz)
 | |
| #define NFS_linkargs_sz		(NFS_fhandle_sz+NFS_diropargs_sz)
 | |
| #define NFS_symlinkargs_sz	(NFS_diropargs_sz+1+NFS_sattr_sz)
 | |
| #define NFS_readdirargs_sz	(NFS_fhandle_sz+2)
 | |
| 
 | |
| #define NFS_attrstat_sz		(1+NFS_fattr_sz)
 | |
| #define NFS_diropres_sz		(1+NFS_fhandle_sz+NFS_fattr_sz)
 | |
| #define NFS_readlinkres_sz	(2)
 | |
| #define NFS_readres_sz		(1+NFS_fattr_sz+1)
 | |
| #define NFS_writeres_sz         (NFS_attrstat_sz)
 | |
| #define NFS_stat_sz		(1)
 | |
| #define NFS_readdirres_sz	(1)
 | |
| #define NFS_statfsres_sz	(1+NFS_info_sz)
 | |
| 
 | |
| static int nfs_stat_to_errno(enum nfs_stat);
 | |
| 
 | |
| /*
 | |
|  * While encoding arguments, set up the reply buffer in advance to
 | |
|  * receive reply data directly into the page cache.
 | |
|  */
 | |
| static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages,
 | |
| 				 unsigned int base, unsigned int len,
 | |
| 				 unsigned int bufsize)
 | |
| {
 | |
| 	struct rpc_auth	*auth = req->rq_cred->cr_auth;
 | |
| 	unsigned int replen;
 | |
| 
 | |
| 	replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize;
 | |
| 	xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Handle decode buffer overflows out-of-line.
 | |
|  */
 | |
| static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
 | |
| {
 | |
| 	dprintk("NFS: %s prematurely hit the end of our receive buffer. "
 | |
| 		"Remaining buffer length is %tu words.\n",
 | |
| 		func, xdr->end - xdr->p);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * Encode/decode NFSv2 basic data types
 | |
|  *
 | |
|  * Basic NFSv2 data types are defined in section 2.3 of RFC 1094:
 | |
|  * "NFS: Network File System Protocol Specification".
 | |
|  *
 | |
|  * Not all basic data types have their own encoding and decoding
 | |
|  * functions.  For run-time efficiency, some data types are encoded
 | |
|  * or decoded inline.
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  *	typedef opaque	nfsdata<>;
 | |
|  */
 | |
| static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_pgio_res *result)
 | |
| {
 | |
| 	u32 recvd, count;
 | |
| 	__be32 *p;
 | |
| 
 | |
| 	p = xdr_inline_decode(xdr, 4);
 | |
| 	if (unlikely(p == NULL))
 | |
| 		goto out_overflow;
 | |
| 	count = be32_to_cpup(p);
 | |
| 	recvd = xdr_read_pages(xdr, count);
 | |
| 	if (unlikely(count > recvd))
 | |
| 		goto out_cheating;
 | |
| out:
 | |
| 	result->eof = 0;	/* NFSv2 does not pass EOF flag on the wire. */
 | |
| 	result->count = count;
 | |
| 	return count;
 | |
| out_cheating:
 | |
| 	dprintk("NFS: server cheating in read result: "
 | |
| 		"count %u > recvd %u\n", count, recvd);
 | |
| 	count = recvd;
 | |
| 	goto out;
 | |
| out_overflow:
 | |
| 	print_overflow_msg(__func__, xdr);
 | |
| 	return -EIO;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  *	enum stat {
 | |
|  *		NFS_OK = 0,
 | |
|  *		NFSERR_PERM = 1,
 | |
|  *		NFSERR_NOENT = 2,
 | |
|  *		NFSERR_IO = 5,
 | |
|  *		NFSERR_NXIO = 6,
 | |
|  *		NFSERR_ACCES = 13,
 | |
|  *		NFSERR_EXIST = 17,
 | |
|  *		NFSERR_NODEV = 19,
 | |
|  *		NFSERR_NOTDIR = 20,
 | |
|  *		NFSERR_ISDIR = 21,
 | |
|  *		NFSERR_FBIG = 27,
 | |
|  *		NFSERR_NOSPC = 28,
 | |
|  *		NFSERR_ROFS = 30,
 | |
|  *		NFSERR_NAMETOOLONG = 63,
 | |
|  *		NFSERR_NOTEMPTY = 66,
 | |
|  *		NFSERR_DQUOT = 69,
 | |
|  *		NFSERR_STALE = 70,
 | |
|  *		NFSERR_WFLUSH = 99
 | |
|  *	};
 | |
|  */
 | |
| static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status)
 | |
| {
 | |
| 	__be32 *p;
 | |
| 
 | |
| 	p = xdr_inline_decode(xdr, 4);
 | |
| 	if (unlikely(p == NULL))
 | |
| 		goto out_overflow;
 | |
| 	*status = be32_to_cpup(p);
 | |
| 	return 0;
 | |
| out_overflow:
 | |
| 	print_overflow_msg(__func__, xdr);
 | |
| 	return -EIO;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.3.2.  ftype
 | |
|  *
 | |
|  *	enum ftype {
 | |
|  *		NFNON = 0,
 | |
|  *		NFREG = 1,
 | |
|  *		NFDIR = 2,
 | |
|  *		NFBLK = 3,
 | |
|  *		NFCHR = 4,
 | |
|  *		NFLNK = 5
 | |
|  *	};
 | |
|  *
 | |
|  */
 | |
| static __be32 *xdr_decode_ftype(__be32 *p, u32 *type)
 | |
| {
 | |
| 	*type = be32_to_cpup(p++);
 | |
| 	if (unlikely(*type > NF2FIFO))
 | |
| 		*type = NFBAD;
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.3.3.  fhandle
 | |
|  *
 | |
|  *	typedef opaque fhandle[FHSIZE];
 | |
|  */
 | |
| static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh)
 | |
| {
 | |
| 	__be32 *p;
 | |
| 
 | |
| 	p = xdr_reserve_space(xdr, NFS2_FHSIZE);
 | |
| 	memcpy(p, fh->data, NFS2_FHSIZE);
 | |
| }
 | |
| 
 | |
| static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh)
 | |
| {
 | |
| 	__be32 *p;
 | |
| 
 | |
| 	p = xdr_inline_decode(xdr, NFS2_FHSIZE);
 | |
| 	if (unlikely(p == NULL))
 | |
| 		goto out_overflow;
 | |
| 	fh->size = NFS2_FHSIZE;
 | |
| 	memcpy(fh->data, p, NFS2_FHSIZE);
 | |
| 	return 0;
 | |
| out_overflow:
 | |
| 	print_overflow_msg(__func__, xdr);
 | |
| 	return -EIO;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.3.4.  timeval
 | |
|  *
 | |
|  *	struct timeval {
 | |
|  *		unsigned int seconds;
 | |
|  *		unsigned int useconds;
 | |
|  *	};
 | |
|  */
 | |
| static __be32 *xdr_encode_time(__be32 *p, const struct timespec *timep)
 | |
| {
 | |
| 	*p++ = cpu_to_be32(timep->tv_sec);
 | |
| 	if (timep->tv_nsec != 0)
 | |
| 		*p++ = cpu_to_be32(timep->tv_nsec / NSEC_PER_USEC);
 | |
| 	else
 | |
| 		*p++ = cpu_to_be32(0);
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Passing the invalid value useconds=1000000 is a Sun convention for
 | |
|  * "set to current server time".  It's needed to make permissions checks
 | |
|  * for the "touch" program across v2 mounts to Solaris and Irix servers
 | |
|  * work correctly.  See description of sattr in section 6.1 of "NFS
 | |
|  * Illustrated" by Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5.
 | |
|  */
 | |
| static __be32 *xdr_encode_current_server_time(__be32 *p,
 | |
| 					      const struct timespec *timep)
 | |
| {
 | |
| 	*p++ = cpu_to_be32(timep->tv_sec);
 | |
| 	*p++ = cpu_to_be32(1000000);
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| static __be32 *xdr_decode_time(__be32 *p, struct timespec *timep)
 | |
| {
 | |
| 	timep->tv_sec = be32_to_cpup(p++);
 | |
| 	timep->tv_nsec = be32_to_cpup(p++) * NSEC_PER_USEC;
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.3.5.  fattr
 | |
|  *
 | |
|  *	struct fattr {
 | |
|  *		ftype		type;
 | |
|  *		unsigned int	mode;
 | |
|  *		unsigned int	nlink;
 | |
|  *		unsigned int	uid;
 | |
|  *		unsigned int	gid;
 | |
|  *		unsigned int	size;
 | |
|  *		unsigned int	blocksize;
 | |
|  *		unsigned int	rdev;
 | |
|  *		unsigned int	blocks;
 | |
|  *		unsigned int	fsid;
 | |
|  *		unsigned int	fileid;
 | |
|  *		timeval		atime;
 | |
|  *		timeval		mtime;
 | |
|  *		timeval		ctime;
 | |
|  *	};
 | |
|  *
 | |
|  */
 | |
| static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
 | |
| {
 | |
| 	u32 rdev, type;
 | |
| 	__be32 *p;
 | |
| 
 | |
| 	p = xdr_inline_decode(xdr, NFS_fattr_sz << 2);
 | |
| 	if (unlikely(p == NULL))
 | |
| 		goto out_overflow;
 | |
| 
 | |
| 	fattr->valid |= NFS_ATTR_FATTR_V2;
 | |
| 
 | |
| 	p = xdr_decode_ftype(p, &type);
 | |
| 
 | |
| 	fattr->mode = be32_to_cpup(p++);
 | |
| 	fattr->nlink = be32_to_cpup(p++);
 | |
| 	fattr->uid = make_kuid(&init_user_ns, be32_to_cpup(p++));
 | |
| 	if (!uid_valid(fattr->uid))
 | |
| 		goto out_uid;
 | |
| 	fattr->gid = make_kgid(&init_user_ns, be32_to_cpup(p++));
 | |
| 	if (!gid_valid(fattr->gid))
 | |
| 		goto out_gid;
 | |
| 		
 | |
| 	fattr->size = be32_to_cpup(p++);
 | |
| 	fattr->du.nfs2.blocksize = be32_to_cpup(p++);
 | |
| 
 | |
| 	rdev = be32_to_cpup(p++);
 | |
| 	fattr->rdev = new_decode_dev(rdev);
 | |
| 	if (type == (u32)NFCHR && rdev == (u32)NFS2_FIFO_DEV) {
 | |
| 		fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO;
 | |
| 		fattr->rdev = 0;
 | |
| 	}
 | |
| 
 | |
| 	fattr->du.nfs2.blocks = be32_to_cpup(p++);
 | |
| 	fattr->fsid.major = be32_to_cpup(p++);
 | |
| 	fattr->fsid.minor = 0;
 | |
| 	fattr->fileid = be32_to_cpup(p++);
 | |
| 
 | |
| 	p = xdr_decode_time(p, &fattr->atime);
 | |
| 	p = xdr_decode_time(p, &fattr->mtime);
 | |
| 	xdr_decode_time(p, &fattr->ctime);
 | |
| 	fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime);
 | |
| 
 | |
| 	return 0;
 | |
| out_uid:
 | |
| 	dprintk("NFS: returned invalid uid\n");
 | |
| 	return -EINVAL;
 | |
| out_gid:
 | |
| 	dprintk("NFS: returned invalid gid\n");
 | |
| 	return -EINVAL;
 | |
| out_overflow:
 | |
| 	print_overflow_msg(__func__, xdr);
 | |
| 	return -EIO;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.3.6.  sattr
 | |
|  *
 | |
|  *	struct sattr {
 | |
|  *		unsigned int	mode;
 | |
|  *		unsigned int	uid;
 | |
|  *		unsigned int	gid;
 | |
|  *		unsigned int	size;
 | |
|  *		timeval		atime;
 | |
|  *		timeval		mtime;
 | |
|  *	};
 | |
|  */
 | |
| 
 | |
| #define NFS2_SATTR_NOT_SET	(0xffffffff)
 | |
| 
 | |
| static __be32 *xdr_time_not_set(__be32 *p)
 | |
| {
 | |
| 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
 | |
| 	*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
 | |
| 	return p;
 | |
| }
 | |
| 
 | |
| static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr)
 | |
| {
 | |
| 	__be32 *p;
 | |
| 
 | |
| 	p = xdr_reserve_space(xdr, NFS_sattr_sz << 2);
 | |
| 
 | |
| 	if (attr->ia_valid & ATTR_MODE)
 | |
| 		*p++ = cpu_to_be32(attr->ia_mode);
 | |
| 	else
 | |
| 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
 | |
| 	if (attr->ia_valid & ATTR_UID)
 | |
| 		*p++ = cpu_to_be32(from_kuid(&init_user_ns, attr->ia_uid));
 | |
| 	else
 | |
| 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
 | |
| 	if (attr->ia_valid & ATTR_GID)
 | |
| 		*p++ = cpu_to_be32(from_kgid(&init_user_ns, attr->ia_gid));
 | |
| 	else
 | |
| 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
 | |
| 	if (attr->ia_valid & ATTR_SIZE)
 | |
| 		*p++ = cpu_to_be32((u32)attr->ia_size);
 | |
| 	else
 | |
| 		*p++ = cpu_to_be32(NFS2_SATTR_NOT_SET);
 | |
| 
 | |
| 	if (attr->ia_valid & ATTR_ATIME_SET)
 | |
| 		p = xdr_encode_time(p, &attr->ia_atime);
 | |
| 	else if (attr->ia_valid & ATTR_ATIME)
 | |
| 		p = xdr_encode_current_server_time(p, &attr->ia_atime);
 | |
| 	else
 | |
| 		p = xdr_time_not_set(p);
 | |
| 	if (attr->ia_valid & ATTR_MTIME_SET)
 | |
| 		xdr_encode_time(p, &attr->ia_mtime);
 | |
| 	else if (attr->ia_valid & ATTR_MTIME)
 | |
| 		xdr_encode_current_server_time(p, &attr->ia_mtime);
 | |
| 	else
 | |
| 		xdr_time_not_set(p);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.3.7.  filename
 | |
|  *
 | |
|  *	typedef string filename<MAXNAMLEN>;
 | |
|  */
 | |
| static void encode_filename(struct xdr_stream *xdr,
 | |
| 			    const char *name, u32 length)
 | |
| {
 | |
| 	__be32 *p;
 | |
| 
 | |
| 	WARN_ON_ONCE(length > NFS2_MAXNAMLEN);
 | |
| 	p = xdr_reserve_space(xdr, 4 + length);
 | |
| 	xdr_encode_opaque(p, name, length);
 | |
| }
 | |
| 
 | |
| static int decode_filename_inline(struct xdr_stream *xdr,
 | |
| 				  const char **name, u32 *length)
 | |
| {
 | |
| 	__be32 *p;
 | |
| 	u32 count;
 | |
| 
 | |
| 	p = xdr_inline_decode(xdr, 4);
 | |
| 	if (unlikely(p == NULL))
 | |
| 		goto out_overflow;
 | |
| 	count = be32_to_cpup(p);
 | |
| 	if (count > NFS3_MAXNAMLEN)
 | |
| 		goto out_nametoolong;
 | |
| 	p = xdr_inline_decode(xdr, count);
 | |
| 	if (unlikely(p == NULL))
 | |
| 		goto out_overflow;
 | |
| 	*name = (const char *)p;
 | |
| 	*length = count;
 | |
| 	return 0;
 | |
| out_nametoolong:
 | |
| 	dprintk("NFS: returned filename too long: %u\n", count);
 | |
| 	return -ENAMETOOLONG;
 | |
| out_overflow:
 | |
| 	print_overflow_msg(__func__, xdr);
 | |
| 	return -EIO;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.3.8.  path
 | |
|  *
 | |
|  *	typedef string path<MAXPATHLEN>;
 | |
|  */
 | |
| static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length)
 | |
| {
 | |
| 	__be32 *p;
 | |
| 
 | |
| 	p = xdr_reserve_space(xdr, 4);
 | |
| 	*p = cpu_to_be32(length);
 | |
| 	xdr_write_pages(xdr, pages, 0, length);
 | |
| }
 | |
| 
 | |
| static int decode_path(struct xdr_stream *xdr)
 | |
| {
 | |
| 	u32 length, recvd;
 | |
| 	__be32 *p;
 | |
| 
 | |
| 	p = xdr_inline_decode(xdr, 4);
 | |
| 	if (unlikely(p == NULL))
 | |
| 		goto out_overflow;
 | |
| 	length = be32_to_cpup(p);
 | |
| 	if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN))
 | |
| 		goto out_size;
 | |
| 	recvd = xdr_read_pages(xdr, length);
 | |
| 	if (unlikely(length > recvd))
 | |
| 		goto out_cheating;
 | |
| 	xdr_terminate_string(xdr->buf, length);
 | |
| 	return 0;
 | |
| out_size:
 | |
| 	dprintk("NFS: returned pathname too long: %u\n", length);
 | |
| 	return -ENAMETOOLONG;
 | |
| out_cheating:
 | |
| 	dprintk("NFS: server cheating in pathname result: "
 | |
| 		"length %u > received %u\n", length, recvd);
 | |
| 	return -EIO;
 | |
| out_overflow:
 | |
| 	print_overflow_msg(__func__, xdr);
 | |
| 	return -EIO;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.3.9.  attrstat
 | |
|  *
 | |
|  *	union attrstat switch (stat status) {
 | |
|  *	case NFS_OK:
 | |
|  *		fattr attributes;
 | |
|  *	default:
 | |
|  *		void;
 | |
|  *	};
 | |
|  */
 | |
| static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result,
 | |
| 			   __u32 *op_status)
 | |
| {
 | |
| 	enum nfs_stat status;
 | |
| 	int error;
 | |
| 
 | |
| 	error = decode_stat(xdr, &status);
 | |
| 	if (unlikely(error))
 | |
| 		goto out;
 | |
| 	if (op_status)
 | |
| 		*op_status = status;
 | |
| 	if (status != NFS_OK)
 | |
| 		goto out_default;
 | |
| 	error = decode_fattr(xdr, result);
 | |
| out:
 | |
| 	return error;
 | |
| out_default:
 | |
| 	return nfs_stat_to_errno(status);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.3.10.  diropargs
 | |
|  *
 | |
|  *	struct diropargs {
 | |
|  *		fhandle  dir;
 | |
|  *		filename name;
 | |
|  *	};
 | |
|  */
 | |
| static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh,
 | |
| 			     const char *name, u32 length)
 | |
| {
 | |
| 	encode_fhandle(xdr, fh);
 | |
| 	encode_filename(xdr, name, length);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.3.11.  diropres
 | |
|  *
 | |
|  *	union diropres switch (stat status) {
 | |
|  *	case NFS_OK:
 | |
|  *		struct {
 | |
|  *			fhandle file;
 | |
|  *			fattr   attributes;
 | |
|  *		} diropok;
 | |
|  *	default:
 | |
|  *		void;
 | |
|  *	};
 | |
|  */
 | |
| static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result)
 | |
| {
 | |
| 	int error;
 | |
| 
 | |
| 	error = decode_fhandle(xdr, result->fh);
 | |
| 	if (unlikely(error))
 | |
| 		goto out;
 | |
| 	error = decode_fattr(xdr, result->fattr);
 | |
| out:
 | |
| 	return error;
 | |
| }
 | |
| 
 | |
| static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result)
 | |
| {
 | |
| 	enum nfs_stat status;
 | |
| 	int error;
 | |
| 
 | |
| 	error = decode_stat(xdr, &status);
 | |
| 	if (unlikely(error))
 | |
| 		goto out;
 | |
| 	if (status != NFS_OK)
 | |
| 		goto out_default;
 | |
| 	error = decode_diropok(xdr, result);
 | |
| out:
 | |
| 	return error;
 | |
| out_default:
 | |
| 	return nfs_stat_to_errno(status);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * NFSv2 XDR encode functions
 | |
|  *
 | |
|  * NFSv2 argument types are defined in section 2.2 of RFC 1094:
 | |
|  * "NFS: Network File System Protocol Specification".
 | |
|  */
 | |
| 
 | |
| static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req,
 | |
| 				 struct xdr_stream *xdr,
 | |
| 				 const struct nfs_fh *fh)
 | |
| {
 | |
| 	encode_fhandle(xdr, fh);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.2.3.  sattrargs
 | |
|  *
 | |
|  *	struct sattrargs {
 | |
|  *		fhandle file;
 | |
|  *		sattr attributes;
 | |
|  *	};
 | |
|  */
 | |
| static void nfs2_xdr_enc_sattrargs(struct rpc_rqst *req,
 | |
| 				   struct xdr_stream *xdr,
 | |
| 				   const struct nfs_sattrargs *args)
 | |
| {
 | |
| 	encode_fhandle(xdr, args->fh);
 | |
| 	encode_sattr(xdr, args->sattr);
 | |
| }
 | |
| 
 | |
| static void nfs2_xdr_enc_diropargs(struct rpc_rqst *req,
 | |
| 				   struct xdr_stream *xdr,
 | |
| 				   const struct nfs_diropargs *args)
 | |
| {
 | |
| 	encode_diropargs(xdr, args->fh, args->name, args->len);
 | |
| }
 | |
| 
 | |
| static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req,
 | |
| 				      struct xdr_stream *xdr,
 | |
| 				      const struct nfs_readlinkargs *args)
 | |
| {
 | |
| 	encode_fhandle(xdr, args->fh);
 | |
| 	prepare_reply_buffer(req, args->pages, args->pgbase,
 | |
| 					args->pglen, NFS_readlinkres_sz);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.2.7.  readargs
 | |
|  *
 | |
|  *	struct readargs {
 | |
|  *		fhandle file;
 | |
|  *		unsigned offset;
 | |
|  *		unsigned count;
 | |
|  *		unsigned totalcount;
 | |
|  *	};
 | |
|  */
 | |
| static void encode_readargs(struct xdr_stream *xdr,
 | |
| 			    const struct nfs_pgio_args *args)
 | |
| {
 | |
| 	u32 offset = args->offset;
 | |
| 	u32 count = args->count;
 | |
| 	__be32 *p;
 | |
| 
 | |
| 	encode_fhandle(xdr, args->fh);
 | |
| 
 | |
| 	p = xdr_reserve_space(xdr, 4 + 4 + 4);
 | |
| 	*p++ = cpu_to_be32(offset);
 | |
| 	*p++ = cpu_to_be32(count);
 | |
| 	*p = cpu_to_be32(count);
 | |
| }
 | |
| 
 | |
| static void nfs2_xdr_enc_readargs(struct rpc_rqst *req,
 | |
| 				  struct xdr_stream *xdr,
 | |
| 				  const struct nfs_pgio_args *args)
 | |
| {
 | |
| 	encode_readargs(xdr, args);
 | |
| 	prepare_reply_buffer(req, args->pages, args->pgbase,
 | |
| 					args->count, NFS_readres_sz);
 | |
| 	req->rq_rcv_buf.flags |= XDRBUF_READ;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.2.9.  writeargs
 | |
|  *
 | |
|  *	struct writeargs {
 | |
|  *		fhandle file;
 | |
|  *		unsigned beginoffset;
 | |
|  *		unsigned offset;
 | |
|  *		unsigned totalcount;
 | |
|  *		nfsdata data;
 | |
|  *	};
 | |
|  */
 | |
| static void encode_writeargs(struct xdr_stream *xdr,
 | |
| 			     const struct nfs_pgio_args *args)
 | |
| {
 | |
| 	u32 offset = args->offset;
 | |
| 	u32 count = args->count;
 | |
| 	__be32 *p;
 | |
| 
 | |
| 	encode_fhandle(xdr, args->fh);
 | |
| 
 | |
| 	p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4);
 | |
| 	*p++ = cpu_to_be32(offset);
 | |
| 	*p++ = cpu_to_be32(offset);
 | |
| 	*p++ = cpu_to_be32(count);
 | |
| 
 | |
| 	/* nfsdata */
 | |
| 	*p = cpu_to_be32(count);
 | |
| 	xdr_write_pages(xdr, args->pages, args->pgbase, count);
 | |
| }
 | |
| 
 | |
| static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req,
 | |
| 				   struct xdr_stream *xdr,
 | |
| 				   const struct nfs_pgio_args *args)
 | |
| {
 | |
| 	encode_writeargs(xdr, args);
 | |
| 	xdr->buf->flags |= XDRBUF_WRITE;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.2.10.  createargs
 | |
|  *
 | |
|  *	struct createargs {
 | |
|  *		diropargs where;
 | |
|  *		sattr attributes;
 | |
|  *	};
 | |
|  */
 | |
| static void nfs2_xdr_enc_createargs(struct rpc_rqst *req,
 | |
| 				    struct xdr_stream *xdr,
 | |
| 				    const struct nfs_createargs *args)
 | |
| {
 | |
| 	encode_diropargs(xdr, args->fh, args->name, args->len);
 | |
| 	encode_sattr(xdr, args->sattr);
 | |
| }
 | |
| 
 | |
| static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req,
 | |
| 				    struct xdr_stream *xdr,
 | |
| 				    const struct nfs_removeargs *args)
 | |
| {
 | |
| 	encode_diropargs(xdr, args->fh, args->name.name, args->name.len);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.2.12.  renameargs
 | |
|  *
 | |
|  *	struct renameargs {
 | |
|  *		diropargs from;
 | |
|  *		diropargs to;
 | |
|  *	};
 | |
|  */
 | |
| static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req,
 | |
| 				    struct xdr_stream *xdr,
 | |
| 				    const struct nfs_renameargs *args)
 | |
| {
 | |
| 	const struct qstr *old = args->old_name;
 | |
| 	const struct qstr *new = args->new_name;
 | |
| 
 | |
| 	encode_diropargs(xdr, args->old_dir, old->name, old->len);
 | |
| 	encode_diropargs(xdr, args->new_dir, new->name, new->len);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.2.13.  linkargs
 | |
|  *
 | |
|  *	struct linkargs {
 | |
|  *		fhandle from;
 | |
|  *		diropargs to;
 | |
|  *	};
 | |
|  */
 | |
| static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req,
 | |
| 				  struct xdr_stream *xdr,
 | |
| 				  const struct nfs_linkargs *args)
 | |
| {
 | |
| 	encode_fhandle(xdr, args->fromfh);
 | |
| 	encode_diropargs(xdr, args->tofh, args->toname, args->tolen);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.2.14.  symlinkargs
 | |
|  *
 | |
|  *	struct symlinkargs {
 | |
|  *		diropargs from;
 | |
|  *		path to;
 | |
|  *		sattr attributes;
 | |
|  *	};
 | |
|  */
 | |
| static void nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req,
 | |
| 				     struct xdr_stream *xdr,
 | |
| 				     const struct nfs_symlinkargs *args)
 | |
| {
 | |
| 	encode_diropargs(xdr, args->fromfh, args->fromname, args->fromlen);
 | |
| 	encode_path(xdr, args->pages, args->pathlen);
 | |
| 	encode_sattr(xdr, args->sattr);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.2.17.  readdirargs
 | |
|  *
 | |
|  *	struct readdirargs {
 | |
|  *		fhandle dir;
 | |
|  *		nfscookie cookie;
 | |
|  *		unsigned count;
 | |
|  *	};
 | |
|  */
 | |
| static void encode_readdirargs(struct xdr_stream *xdr,
 | |
| 			       const struct nfs_readdirargs *args)
 | |
| {
 | |
| 	__be32 *p;
 | |
| 
 | |
| 	encode_fhandle(xdr, args->fh);
 | |
| 
 | |
| 	p = xdr_reserve_space(xdr, 4 + 4);
 | |
| 	*p++ = cpu_to_be32(args->cookie);
 | |
| 	*p = cpu_to_be32(args->count);
 | |
| }
 | |
| 
 | |
| static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req,
 | |
| 				     struct xdr_stream *xdr,
 | |
| 				     const struct nfs_readdirargs *args)
 | |
| {
 | |
| 	encode_readdirargs(xdr, args);
 | |
| 	prepare_reply_buffer(req, args->pages, 0,
 | |
| 					args->count, NFS_readdirres_sz);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * NFSv2 XDR decode functions
 | |
|  *
 | |
|  * NFSv2 result types are defined in section 2.2 of RFC 1094:
 | |
|  * "NFS: Network File System Protocol Specification".
 | |
|  */
 | |
| 
 | |
| static int nfs2_xdr_dec_stat(struct rpc_rqst *req, struct xdr_stream *xdr,
 | |
| 			     void *__unused)
 | |
| {
 | |
| 	enum nfs_stat status;
 | |
| 	int error;
 | |
| 
 | |
| 	error = decode_stat(xdr, &status);
 | |
| 	if (unlikely(error))
 | |
| 		goto out;
 | |
| 	if (status != NFS_OK)
 | |
| 		goto out_default;
 | |
| out:
 | |
| 	return error;
 | |
| out_default:
 | |
| 	return nfs_stat_to_errno(status);
 | |
| }
 | |
| 
 | |
| static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, struct xdr_stream *xdr,
 | |
| 				 struct nfs_fattr *result)
 | |
| {
 | |
| 	return decode_attrstat(xdr, result, NULL);
 | |
| }
 | |
| 
 | |
| static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, struct xdr_stream *xdr,
 | |
| 				 struct nfs_diropok *result)
 | |
| {
 | |
| 	return decode_diropres(xdr, result);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.2.6.  readlinkres
 | |
|  *
 | |
|  *	union readlinkres switch (stat status) {
 | |
|  *	case NFS_OK:
 | |
|  *		path data;
 | |
|  *	default:
 | |
|  *		void;
 | |
|  *	};
 | |
|  */
 | |
| static int nfs2_xdr_dec_readlinkres(struct rpc_rqst *req,
 | |
| 				    struct xdr_stream *xdr, void *__unused)
 | |
| {
 | |
| 	enum nfs_stat status;
 | |
| 	int error;
 | |
| 
 | |
| 	error = decode_stat(xdr, &status);
 | |
| 	if (unlikely(error))
 | |
| 		goto out;
 | |
| 	if (status != NFS_OK)
 | |
| 		goto out_default;
 | |
| 	error = decode_path(xdr);
 | |
| out:
 | |
| 	return error;
 | |
| out_default:
 | |
| 	return nfs_stat_to_errno(status);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.2.7.  readres
 | |
|  *
 | |
|  *	union readres switch (stat status) {
 | |
|  *	case NFS_OK:
 | |
|  *		fattr attributes;
 | |
|  *		nfsdata data;
 | |
|  *	default:
 | |
|  *		void;
 | |
|  *	};
 | |
|  */
 | |
| static int nfs2_xdr_dec_readres(struct rpc_rqst *req, struct xdr_stream *xdr,
 | |
| 				struct nfs_pgio_res *result)
 | |
| {
 | |
| 	enum nfs_stat status;
 | |
| 	int error;
 | |
| 
 | |
| 	error = decode_stat(xdr, &status);
 | |
| 	if (unlikely(error))
 | |
| 		goto out;
 | |
| 	result->op_status = status;
 | |
| 	if (status != NFS_OK)
 | |
| 		goto out_default;
 | |
| 	error = decode_fattr(xdr, result->fattr);
 | |
| 	if (unlikely(error))
 | |
| 		goto out;
 | |
| 	error = decode_nfsdata(xdr, result);
 | |
| out:
 | |
| 	return error;
 | |
| out_default:
 | |
| 	return nfs_stat_to_errno(status);
 | |
| }
 | |
| 
 | |
| static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, struct xdr_stream *xdr,
 | |
| 				 struct nfs_pgio_res *result)
 | |
| {
 | |
| 	/* All NFSv2 writes are "file sync" writes */
 | |
| 	result->verf->committed = NFS_FILE_SYNC;
 | |
| 	return decode_attrstat(xdr, result->fattr, &result->op_status);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * nfs2_decode_dirent - Decode a single NFSv2 directory entry stored in
 | |
|  *                      the local page cache.
 | |
|  * @xdr: XDR stream where entry resides
 | |
|  * @entry: buffer to fill in with entry data
 | |
|  * @plus: boolean indicating whether this should be a readdirplus entry
 | |
|  *
 | |
|  * Returns zero if successful, otherwise a negative errno value is
 | |
|  * returned.
 | |
|  *
 | |
|  * This function is not invoked during READDIR reply decoding, but
 | |
|  * rather whenever an application invokes the getdents(2) system call
 | |
|  * on a directory already in our cache.
 | |
|  *
 | |
|  * 2.2.17.  entry
 | |
|  *
 | |
|  *	struct entry {
 | |
|  *		unsigned	fileid;
 | |
|  *		filename	name;
 | |
|  *		nfscookie	cookie;
 | |
|  *		entry		*nextentry;
 | |
|  *	};
 | |
|  */
 | |
| int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
 | |
| 		       int plus)
 | |
| {
 | |
| 	__be32 *p;
 | |
| 	int error;
 | |
| 
 | |
| 	p = xdr_inline_decode(xdr, 4);
 | |
| 	if (unlikely(p == NULL))
 | |
| 		goto out_overflow;
 | |
| 	if (*p++ == xdr_zero) {
 | |
| 		p = xdr_inline_decode(xdr, 4);
 | |
| 		if (unlikely(p == NULL))
 | |
| 			goto out_overflow;
 | |
| 		if (*p++ == xdr_zero)
 | |
| 			return -EAGAIN;
 | |
| 		entry->eof = 1;
 | |
| 		return -EBADCOOKIE;
 | |
| 	}
 | |
| 
 | |
| 	p = xdr_inline_decode(xdr, 4);
 | |
| 	if (unlikely(p == NULL))
 | |
| 		goto out_overflow;
 | |
| 	entry->ino = be32_to_cpup(p);
 | |
| 
 | |
| 	error = decode_filename_inline(xdr, &entry->name, &entry->len);
 | |
| 	if (unlikely(error))
 | |
| 		return error;
 | |
| 
 | |
| 	/*
 | |
| 	 * The type (size and byte order) of nfscookie isn't defined in
 | |
| 	 * RFC 1094.  This implementation assumes that it's an XDR uint32.
 | |
| 	 */
 | |
| 	entry->prev_cookie = entry->cookie;
 | |
| 	p = xdr_inline_decode(xdr, 4);
 | |
| 	if (unlikely(p == NULL))
 | |
| 		goto out_overflow;
 | |
| 	entry->cookie = be32_to_cpup(p);
 | |
| 
 | |
| 	entry->d_type = DT_UNKNOWN;
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| out_overflow:
 | |
| 	print_overflow_msg(__func__, xdr);
 | |
| 	return -EAGAIN;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.2.17.  readdirres
 | |
|  *
 | |
|  *	union readdirres switch (stat status) {
 | |
|  *	case NFS_OK:
 | |
|  *		struct {
 | |
|  *			entry *entries;
 | |
|  *			bool eof;
 | |
|  *		} readdirok;
 | |
|  *	default:
 | |
|  *		void;
 | |
|  *	};
 | |
|  *
 | |
|  * Read the directory contents into the page cache, but don't
 | |
|  * touch them.  The actual decoding is done by nfs2_decode_dirent()
 | |
|  * during subsequent nfs_readdir() calls.
 | |
|  */
 | |
| static int decode_readdirok(struct xdr_stream *xdr)
 | |
| {
 | |
| 	return xdr_read_pages(xdr, xdr->buf->page_len);
 | |
| }
 | |
| 
 | |
| static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req,
 | |
| 				   struct xdr_stream *xdr, void *__unused)
 | |
| {
 | |
| 	enum nfs_stat status;
 | |
| 	int error;
 | |
| 
 | |
| 	error = decode_stat(xdr, &status);
 | |
| 	if (unlikely(error))
 | |
| 		goto out;
 | |
| 	if (status != NFS_OK)
 | |
| 		goto out_default;
 | |
| 	error = decode_readdirok(xdr);
 | |
| out:
 | |
| 	return error;
 | |
| out_default:
 | |
| 	return nfs_stat_to_errno(status);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 2.2.18.  statfsres
 | |
|  *
 | |
|  *	union statfsres (stat status) {
 | |
|  *	case NFS_OK:
 | |
|  *		struct {
 | |
|  *			unsigned tsize;
 | |
|  *			unsigned bsize;
 | |
|  *			unsigned blocks;
 | |
|  *			unsigned bfree;
 | |
|  *			unsigned bavail;
 | |
|  *		} info;
 | |
|  *	default:
 | |
|  *		void;
 | |
|  *	};
 | |
|  */
 | |
| static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result)
 | |
| {
 | |
| 	__be32 *p;
 | |
| 
 | |
| 	p = xdr_inline_decode(xdr, NFS_info_sz << 2);
 | |
| 	if (unlikely(p == NULL))
 | |
| 		goto out_overflow;
 | |
| 	result->tsize  = be32_to_cpup(p++);
 | |
| 	result->bsize  = be32_to_cpup(p++);
 | |
| 	result->blocks = be32_to_cpup(p++);
 | |
| 	result->bfree  = be32_to_cpup(p++);
 | |
| 	result->bavail = be32_to_cpup(p);
 | |
| 	return 0;
 | |
| out_overflow:
 | |
| 	print_overflow_msg(__func__, xdr);
 | |
| 	return -EIO;
 | |
| }
 | |
| 
 | |
| static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr,
 | |
| 				  struct nfs2_fsstat *result)
 | |
| {
 | |
| 	enum nfs_stat status;
 | |
| 	int error;
 | |
| 
 | |
| 	error = decode_stat(xdr, &status);
 | |
| 	if (unlikely(error))
 | |
| 		goto out;
 | |
| 	if (status != NFS_OK)
 | |
| 		goto out_default;
 | |
| 	error = decode_info(xdr, result);
 | |
| out:
 | |
| 	return error;
 | |
| out_default:
 | |
| 	return nfs_stat_to_errno(status);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * We need to translate between nfs status return values and
 | |
|  * the local errno values which may not be the same.
 | |
|  */
 | |
| static const struct {
 | |
| 	int stat;
 | |
| 	int errno;
 | |
| } nfs_errtbl[] = {
 | |
| 	{ NFS_OK,		0		},
 | |
| 	{ NFSERR_PERM,		-EPERM		},
 | |
| 	{ NFSERR_NOENT,		-ENOENT		},
 | |
| 	{ NFSERR_IO,		-errno_NFSERR_IO},
 | |
| 	{ NFSERR_NXIO,		-ENXIO		},
 | |
| /*	{ NFSERR_EAGAIN,	-EAGAIN		}, */
 | |
| 	{ NFSERR_ACCES,		-EACCES		},
 | |
| 	{ NFSERR_EXIST,		-EEXIST		},
 | |
| 	{ NFSERR_XDEV,		-EXDEV		},
 | |
| 	{ NFSERR_NODEV,		-ENODEV		},
 | |
| 	{ NFSERR_NOTDIR,	-ENOTDIR	},
 | |
| 	{ NFSERR_ISDIR,		-EISDIR		},
 | |
| 	{ NFSERR_INVAL,		-EINVAL		},
 | |
| 	{ NFSERR_FBIG,		-EFBIG		},
 | |
| 	{ NFSERR_NOSPC,		-ENOSPC		},
 | |
| 	{ NFSERR_ROFS,		-EROFS		},
 | |
| 	{ NFSERR_MLINK,		-EMLINK		},
 | |
| 	{ NFSERR_NAMETOOLONG,	-ENAMETOOLONG	},
 | |
| 	{ NFSERR_NOTEMPTY,	-ENOTEMPTY	},
 | |
| 	{ NFSERR_DQUOT,		-EDQUOT		},
 | |
| 	{ NFSERR_STALE,		-ESTALE		},
 | |
| 	{ NFSERR_REMOTE,	-EREMOTE	},
 | |
| #ifdef EWFLUSH
 | |
| 	{ NFSERR_WFLUSH,	-EWFLUSH	},
 | |
| #endif
 | |
| 	{ NFSERR_BADHANDLE,	-EBADHANDLE	},
 | |
| 	{ NFSERR_NOT_SYNC,	-ENOTSYNC	},
 | |
| 	{ NFSERR_BAD_COOKIE,	-EBADCOOKIE	},
 | |
| 	{ NFSERR_NOTSUPP,	-ENOTSUPP	},
 | |
| 	{ NFSERR_TOOSMALL,	-ETOOSMALL	},
 | |
| 	{ NFSERR_SERVERFAULT,	-EREMOTEIO	},
 | |
| 	{ NFSERR_BADTYPE,	-EBADTYPE	},
 | |
| 	{ NFSERR_JUKEBOX,	-EJUKEBOX	},
 | |
| 	{ -1,			-EIO		}
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * nfs_stat_to_errno - convert an NFS status code to a local errno
 | |
|  * @status: NFS status code to convert
 | |
|  *
 | |
|  * Returns a local errno value, or -EIO if the NFS status code is
 | |
|  * not recognized.  This function is used jointly by NFSv2 and NFSv3.
 | |
|  */
 | |
| static int nfs_stat_to_errno(enum nfs_stat status)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; nfs_errtbl[i].stat != -1; i++) {
 | |
| 		if (nfs_errtbl[i].stat == (int)status)
 | |
| 			return nfs_errtbl[i].errno;
 | |
| 	}
 | |
| 	dprintk("NFS: Unrecognized nfs status value: %u\n", status);
 | |
| 	return nfs_errtbl[i].errno;
 | |
| }
 | |
| 
 | |
| #define PROC(proc, argtype, restype, timer)				\
 | |
| [NFSPROC_##proc] = {							\
 | |
| 	.p_proc	    =  NFSPROC_##proc,					\
 | |
| 	.p_encode   =  (kxdreproc_t)nfs2_xdr_enc_##argtype,		\
 | |
| 	.p_decode   =  (kxdrdproc_t)nfs2_xdr_dec_##restype,		\
 | |
| 	.p_arglen   =  NFS_##argtype##_sz,				\
 | |
| 	.p_replen   =  NFS_##restype##_sz,				\
 | |
| 	.p_timer    =  timer,						\
 | |
| 	.p_statidx  =  NFSPROC_##proc,					\
 | |
| 	.p_name     =  #proc,						\
 | |
| 	}
 | |
| struct rpc_procinfo	nfs_procedures[] = {
 | |
| 	PROC(GETATTR,	fhandle,	attrstat,	1),
 | |
| 	PROC(SETATTR,	sattrargs,	attrstat,	0),
 | |
| 	PROC(LOOKUP,	diropargs,	diropres,	2),
 | |
| 	PROC(READLINK,	readlinkargs,	readlinkres,	3),
 | |
| 	PROC(READ,	readargs,	readres,	3),
 | |
| 	PROC(WRITE,	writeargs,	writeres,	4),
 | |
| 	PROC(CREATE,	createargs,	diropres,	0),
 | |
| 	PROC(REMOVE,	removeargs,	stat,		0),
 | |
| 	PROC(RENAME,	renameargs,	stat,		0),
 | |
| 	PROC(LINK,	linkargs,	stat,		0),
 | |
| 	PROC(SYMLINK,	symlinkargs,	stat,		0),
 | |
| 	PROC(MKDIR,	createargs,	diropres,	0),
 | |
| 	PROC(RMDIR,	diropargs,	stat,		0),
 | |
| 	PROC(READDIR,	readdirargs,	readdirres,	3),
 | |
| 	PROC(STATFS,	fhandle,	statfsres,	0),
 | |
| };
 | |
| 
 | |
| const struct rpc_version nfs_version2 = {
 | |
| 	.number			= 2,
 | |
| 	.nrprocs		= ARRAY_SIZE(nfs_procedures),
 | |
| 	.procs			= nfs_procedures
 | |
| };
 |