Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / fs / nfs / nfs2xdr.c
diff --git a/kernel/fs/nfs/nfs2xdr.c b/kernel/fs/nfs/nfs2xdr.c
new file mode 100644 (file)
index 0000000..b4e03ed
--- /dev/null
@@ -0,0 +1,1151 @@
+/*
+ * 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
+};