/*
* 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 ==