udf: Refuse RW mount of the filesystem instead of making it RO
[linux-3.10.git] / fs / nfs / nfs3xdr.c
index ae1b1a4..fa6d721 100644 (file)
@@ -86,6 +86,8 @@
                                XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
 #define ACL3_setaclres_sz      (1+NFS3_post_op_attr_sz)
 
+static int nfs3_stat_to_errno(enum nfs_stat);
+
 /*
  * Map file type to S_IFMT bits
  */
@@ -196,7 +198,7 @@ static void encode_filename3(struct xdr_stream *xdr,
 {
        __be32 *p;
 
-       BUG_ON(length > NFS3_MAXNAMLEN);
+       WARN_ON_ONCE(length > NFS3_MAXNAMLEN);
        p = xdr_reserve_space(xdr, 4 + length);
        xdr_encode_opaque(p, name, length);
 }
@@ -236,7 +238,6 @@ out_overflow:
 static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages,
                            const u32 length)
 {
-       BUG_ON(length > NFS3_MAXPATHLEN);
        encode_uint32(xdr, length);
        xdr_write_pages(xdr, pages, 0, length);
 }
@@ -244,7 +245,6 @@ static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages,
 static int decode_nfspath3(struct xdr_stream *xdr)
 {
        u32 recvd, count;
-       size_t hdrlen;
        __be32 *p;
 
        p = xdr_inline_decode(xdr, 4);
@@ -253,12 +253,9 @@ static int decode_nfspath3(struct xdr_stream *xdr)
        count = be32_to_cpup(p);
        if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN))
                goto out_nametoolong;
-       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
-       recvd = xdr->buf->len - hdrlen;
+       recvd = xdr_read_pages(xdr, count);
        if (unlikely(count > recvd))
                goto out_cheating;
-
-       xdr_read_pages(xdr, count);
        xdr_terminate_string(xdr->buf, count);
        return 0;
 
@@ -327,14 +324,14 @@ static void encode_createverf3(struct xdr_stream *xdr, const __be32 *verifier)
        memcpy(p, verifier, NFS3_CREATEVERFSIZE);
 }
 
-static int decode_writeverf3(struct xdr_stream *xdr, __be32 *verifier)
+static int decode_writeverf3(struct xdr_stream *xdr, struct nfs_write_verifier *verifier)
 {
        __be32 *p;
 
        p = xdr_inline_decode(xdr, NFS3_WRITEVERFSIZE);
        if (unlikely(p == NULL))
                goto out_overflow;
-       memcpy(verifier, p, NFS3_WRITEVERFSIZE);
+       memcpy(verifier->data, p, NFS3_WRITEVERFSIZE);
        return 0;
 out_overflow:
        print_overflow_msg(__func__, xdr);
@@ -390,7 +387,6 @@ out_overflow:
  */
 static void encode_ftype3(struct xdr_stream *xdr, const u32 type)
 {
-       BUG_ON(type > NF3FIFO);
        encode_uint32(xdr, type);
 }
 
@@ -445,7 +441,7 @@ static void encode_nfs_fh3(struct xdr_stream *xdr, const struct nfs_fh *fh)
 {
        __be32 *p;
 
-       BUG_ON(fh->size > NFS3_FHSIZE);
+       WARN_ON_ONCE(fh->size > NFS3_FHSIZE);
        p = xdr_reserve_space(xdr, 4 + fh->size);
        xdr_encode_opaque(p, fh->data, fh->size);
 }
@@ -596,13 +592,13 @@ static void encode_sattr3(struct xdr_stream *xdr, const struct iattr *attr)
 
        if (attr->ia_valid & ATTR_UID) {
                *p++ = xdr_one;
-               *p++ = cpu_to_be32(attr->ia_uid);
+               *p++ = cpu_to_be32(from_kuid(&init_user_ns, attr->ia_uid));
        } else
                *p++ = xdr_zero;
 
        if (attr->ia_valid & ATTR_GID) {
                *p++ = xdr_one;
-               *p++ = cpu_to_be32(attr->ia_gid);
+               *p++ = cpu_to_be32(from_kgid(&init_user_ns, attr->ia_gid));
        } else
                *p++ = xdr_zero;
 
@@ -661,8 +657,12 @@ static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr)
 
        fattr->mode = (be32_to_cpup(p++) & ~S_IFMT) | fmode;
        fattr->nlink = be32_to_cpup(p++);
-       fattr->uid = be32_to_cpup(p++);
-       fattr->gid = 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;
 
        p = xdr_decode_size3(p, &fattr->size);
        p = xdr_decode_size3(p, &fattr->du.nfs3.used);
@@ -675,9 +675,16 @@ static int decode_fattr3(struct xdr_stream *xdr, struct nfs_fattr *fattr)
        p = xdr_decode_nfstime3(p, &fattr->atime);
        p = xdr_decode_nfstime3(p, &fattr->mtime);
        xdr_decode_nfstime3(p, &fattr->ctime);
+       fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime);
 
        fattr->valid |= NFS_ATTR_FATTR_V3;
        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;
@@ -725,12 +732,14 @@ static int decode_wcc_attr(struct xdr_stream *xdr, struct nfs_fattr *fattr)
                goto out_overflow;
 
        fattr->valid |= NFS_ATTR_FATTR_PRESIZE
+               | NFS_ATTR_FATTR_PRECHANGE
                | NFS_ATTR_FATTR_PREMTIME
                | NFS_ATTR_FATTR_PRECTIME;
 
        p = xdr_decode_size3(p, &fattr->pre_size);
        p = xdr_decode_nfstime3(p, &fattr->pre_mtime);
        xdr_decode_nfstime3(p, &fattr->pre_ctime);
+       fattr->pre_change_attr = nfs_timespec_to_change_attr(&fattr->pre_ctime);
 
        return 0;
 out_overflow:
@@ -1287,7 +1296,7 @@ static void nfs3_xdr_enc_readdirplus3args(struct rpc_rqst *req,
  *     };
  */
 static void encode_commit3args(struct xdr_stream *xdr,
-                              const struct nfs_writeargs *args)
+                              const struct nfs_commitargs *args)
 {
        __be32 *p;
 
@@ -1300,7 +1309,7 @@ static void encode_commit3args(struct xdr_stream *xdr,
 
 static void nfs3_xdr_enc_commit3args(struct rpc_rqst *req,
                                     struct xdr_stream *xdr,
-                                    const struct nfs_writeargs *args)
+                                    const struct nfs_commitargs *args)
 {
        encode_commit3args(xdr, args);
 }
@@ -1328,13 +1337,17 @@ static void nfs3_xdr_enc_setacl3args(struct rpc_rqst *req,
 
        encode_nfs_fh3(xdr, NFS_FH(args->inode));
        encode_uint32(xdr, args->mask);
+
+       base = req->rq_slen;
        if (args->npages != 0)
                xdr_write_pages(xdr, args->pages, 0, args->len);
+       else
+               xdr_reserve_space(xdr, NFS_ACL_INLINE_BUFSIZE);
 
-       base = req->rq_slen;
        error = nfsacl_encode(xdr->buf, base, args->inode,
                            (args->mask & NFS_ACL) ?
                            args->acl_access : NULL, 1, 0);
+       /* FIXME: this is just broken */
        BUG_ON(error < 0);
        error = nfsacl_encode(xdr->buf, base + error, args->inode,
                            (args->mask & NFS_DFACL) ?
@@ -1366,24 +1379,23 @@ static void nfs3_xdr_enc_setacl3args(struct rpc_rqst *req,
  *             void;
  *     };
  */
-static int nfs3_xdr_dec_getattr3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_getattr3res(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr,
                                    struct nfs_fattr *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
                goto out_default;
-       error = decode_fattr3(&xdr, result);
+       error = decode_fattr3(xdr, result);
 out:
        return error;
 out_default:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1404,18 +1416,17 @@ out_default:
  *             SETATTR3resfail resfail;
  *     };
  */
-static int nfs3_xdr_dec_setattr3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_setattr3res(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr,
                                    struct nfs_fattr *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
-       error = decode_wcc_data(&xdr, result);
+       error = decode_wcc_data(xdr, result);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
@@ -1423,7 +1434,7 @@ static int nfs3_xdr_dec_setattr3res(struct rpc_rqst *req, __be32 *p,
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1446,33 +1457,32 @@ out_status:
  *             LOOKUP3resfail  resfail;
  *     };
  */
-static int nfs3_xdr_dec_lookup3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_lookup3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
                                   struct nfs3_diropres *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
                goto out_default;
-       error = decode_nfs_fh3(&xdr, result->fh);
+       error = decode_nfs_fh3(xdr, result->fh);
        if (unlikely(error))
                goto out;
-       error = decode_post_op_attr(&xdr, result->fattr);
+       error = decode_post_op_attr(xdr, result->fattr);
        if (unlikely(error))
                goto out;
-       error = decode_post_op_attr(&xdr, result->dir_attr);
+       error = decode_post_op_attr(xdr, result->dir_attr);
 out:
        return error;
 out_default:
-       error = decode_post_op_attr(&xdr, result->dir_attr);
+       error = decode_post_op_attr(xdr, result->dir_attr);
        if (unlikely(error))
                goto out;
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1494,27 +1504,26 @@ out_default:
  *             ACCESS3resfail  resfail;
  *     };
  */
-static int nfs3_xdr_dec_access3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_access3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
                                   struct nfs3_accessres *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
-       error = decode_post_op_attr(&xdr, result->fattr);
+       error = decode_post_op_attr(xdr, result->fattr);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
                goto out_default;
-       error = decode_uint32(&xdr, &result->access);
+       error = decode_uint32(xdr, &result->access);
 out:
        return error;
 out_default:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1536,27 +1545,26 @@ out_default:
  *             READLINK3resfail resfail;
  *     };
  */
-static int nfs3_xdr_dec_readlink3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_readlink3res(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
                                     struct nfs_fattr *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
-       error = decode_post_op_attr(&xdr, result);
+       error = decode_post_op_attr(xdr, result);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
                goto out_default;
-       error = decode_nfspath3(&xdr);
+       error = decode_nfspath3(xdr);
 out:
        return error;
 out_default:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1584,7 +1592,6 @@ static int decode_read3resok(struct xdr_stream *xdr,
                             struct nfs_readres *result)
 {
        u32 eof, count, ocount, recvd;
-       size_t hdrlen;
        __be32 *p;
 
        p = xdr_inline_decode(xdr, 4 + 4 + 4);
@@ -1595,13 +1602,10 @@ static int decode_read3resok(struct xdr_stream *xdr,
        ocount = be32_to_cpup(p++);
        if (unlikely(ocount != count))
                goto out_mismatch;
-       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
-       recvd = xdr->buf->len - hdrlen;
+       recvd = xdr_read_pages(xdr, count);
        if (unlikely(count > recvd))
                goto out_cheating;
-
 out:
-       xdr_read_pages(xdr, count);
        result->eof = eof;
        result->count = count;
        return count;
@@ -1620,27 +1624,25 @@ out_overflow:
        return -EIO;
 }
 
-static int nfs3_xdr_dec_read3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_read3res(struct rpc_rqst *req, struct xdr_stream *xdr,
                                 struct nfs_readres *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
-       error = decode_post_op_attr(&xdr, result->fattr);
+       error = decode_post_op_attr(xdr, result->fattr);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
                goto out_status;
-       error = decode_read3resok(&xdr, result);
+       error = decode_read3resok(xdr, result);
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1675,44 +1677,44 @@ static int decode_write3resok(struct xdr_stream *xdr,
 {
        __be32 *p;
 
-       p = xdr_inline_decode(xdr, 4 + 4 + NFS3_WRITEVERFSIZE);
+       p = xdr_inline_decode(xdr, 4 + 4);
        if (unlikely(p == NULL))
                goto out_overflow;
        result->count = be32_to_cpup(p++);
        result->verf->committed = be32_to_cpup(p++);
        if (unlikely(result->verf->committed > NFS_FILE_SYNC))
                goto out_badvalue;
-       memcpy(result->verf->verifier, p, NFS3_WRITEVERFSIZE);
+       if (decode_writeverf3(xdr, &result->verf->verifier))
+               goto out_eio;
        return result->count;
 out_badvalue:
        dprintk("NFS: bad stable_how value: %u\n", result->verf->committed);
        return -EIO;
 out_overflow:
        print_overflow_msg(__func__, xdr);
+out_eio:
        return -EIO;
 }
 
-static int nfs3_xdr_dec_write3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_write3res(struct rpc_rqst *req, struct xdr_stream *xdr,
                                  struct nfs_writeres *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
-       error = decode_wcc_data(&xdr, result->fattr);
+       error = decode_wcc_data(xdr, result->fattr);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
                goto out_status;
-       error = decode_write3resok(&xdr, result);
+       error = decode_write3resok(xdr, result);
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1757,27 +1759,26 @@ out:
        return error;
 }
 
-static int nfs3_xdr_dec_create3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_create3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
                                   struct nfs3_diropres *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
                goto out_default;
-       error = decode_create3resok(&xdr, result);
+       error = decode_create3resok(xdr, result);
 out:
        return error;
 out_default:
-       error = decode_wcc_data(&xdr, result->dir_attr);
+       error = decode_wcc_data(xdr, result->dir_attr);
        if (unlikely(error))
                goto out;
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1798,18 +1799,17 @@ out_default:
  *             REMOVE3resfail resfail;
  *     };
  */
-static int nfs3_xdr_dec_remove3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_remove3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
                                   struct nfs_removeres *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
-       error = decode_wcc_data(&xdr, result->dir_attr);
+       error = decode_wcc_data(xdr, result->dir_attr);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
@@ -1817,7 +1817,7 @@ static int nfs3_xdr_dec_remove3res(struct rpc_rqst *req, __be32 *p,
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1840,21 +1840,20 @@ out_status:
  *             RENAME3resfail resfail;
  *     };
  */
-static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
                                   struct nfs_renameres *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
-       error = decode_wcc_data(&xdr, result->old_fattr);
+       error = decode_wcc_data(xdr, result->old_fattr);
        if (unlikely(error))
                goto out;
-       error = decode_wcc_data(&xdr, result->new_fattr);
+       error = decode_wcc_data(xdr, result->new_fattr);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
@@ -1862,7 +1861,7 @@ static int nfs3_xdr_dec_rename3res(struct rpc_rqst *req, __be32 *p,
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -1885,21 +1884,19 @@ out_status:
  *             LINK3resfail    resfail;
  *     };
  */
-static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, struct xdr_stream *xdr,
                                 struct nfs3_linkres *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
-       error = decode_post_op_attr(&xdr, result->fattr);
+       error = decode_post_op_attr(xdr, result->fattr);
        if (unlikely(error))
                goto out;
-       error = decode_wcc_data(&xdr, result->dir_attr);
+       error = decode_wcc_data(xdr, result->dir_attr);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
@@ -1907,7 +1904,7 @@ static int nfs3_xdr_dec_link3res(struct rpc_rqst *req, __be32 *p,
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /**
@@ -2003,11 +2000,6 @@ int nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
                        zero_nfs_fh3(entry->fh);
        }
 
-       /* Peek at the next entry to see if we're at EOD */
-       p = xdr_inline_peek(xdr, 4 + 4);
-       entry->eof = 0;
-       if (p != NULL)
-               entry->eof = (p[0] == xdr_zero) && (p[1] != xdr_zero);
        return 0;
 
 out_overflow:
@@ -2050,22 +2042,7 @@ out_truncated:
  */
 static int decode_dirlist3(struct xdr_stream *xdr)
 {
-       u32 recvd, pglen;
-       size_t hdrlen;
-
-       pglen = xdr->buf->page_len;
-       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
-       recvd = xdr->buf->len - hdrlen;
-       if (unlikely(pglen > recvd))
-               goto out_cheating;
-out:
-       xdr_read_pages(xdr, pglen);
-       return pglen;
-out_cheating:
-       dprintk("NFS: server cheating in readdir result: "
-               "pglen %u > recvd %u\n", pglen, recvd);
-       pglen = recvd;
-       goto out;
+       return xdr_read_pages(xdr, xdr->buf->page_len);
 }
 
 static int decode_readdir3resok(struct xdr_stream *xdr,
@@ -2085,27 +2062,26 @@ out:
        return error;
 }
 
-static int nfs3_xdr_dec_readdir3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_readdir3res(struct rpc_rqst *req,
+                                   struct xdr_stream *xdr,
                                    struct nfs3_readdirres *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
                goto out_default;
-       error = decode_readdir3resok(&xdr, result);
+       error = decode_readdir3resok(xdr, result);
 out:
        return error;
 out_default:
-       error = decode_post_op_attr(&xdr, result->dir_attr);
+       error = decode_post_op_attr(xdr, result->dir_attr);
        if (unlikely(error))
                goto out;
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -2154,27 +2130,26 @@ out_overflow:
        return -EIO;
 }
 
-static int nfs3_xdr_dec_fsstat3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_fsstat3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
                                   struct nfs_fsstat *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
-       error = decode_post_op_attr(&xdr, result->fattr);
+       error = decode_post_op_attr(xdr, result->fattr);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
                goto out_status;
-       error = decode_fsstat3resok(&xdr, result);
+       error = decode_fsstat3resok(xdr, result);
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -2231,27 +2206,26 @@ out_overflow:
        return -EIO;
 }
 
-static int nfs3_xdr_dec_fsinfo3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_fsinfo3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
                                   struct nfs_fsinfo *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
-       error = decode_post_op_attr(&xdr, result->fattr);
+       error = decode_post_op_attr(xdr, result->fattr);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
                goto out_status;
-       error = decode_fsinfo3resok(&xdr, result);
+       error = decode_fsinfo3resok(xdr, result);
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -2295,27 +2269,26 @@ out_overflow:
        return -EIO;
 }
 
-static int nfs3_xdr_dec_pathconf3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_pathconf3res(struct rpc_rqst *req,
+                                    struct xdr_stream *xdr,
                                     struct nfs_pathconf *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
-       error = decode_post_op_attr(&xdr, result->fattr);
+       error = decode_post_op_attr(xdr, result->fattr);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
                goto out_status;
-       error = decode_pathconf3resok(&xdr, result);
+       error = decode_pathconf3resok(xdr, result);
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 /*
@@ -2337,27 +2310,26 @@ out_status:
  *             COMMIT3resfail  resfail;
  *     };
  */
-static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req, __be32 *p,
-                                  struct nfs_writeres *result)
+static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
+                                  struct nfs_commitres *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
-       error = decode_wcc_data(&xdr, result->fattr);
+       error = decode_wcc_data(xdr, result->fattr);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
                goto out_status;
-       error = decode_writeverf3(&xdr, result->verf->verifier);
+       error = decode_writeverf3(xdr, &result->verf->verifier);
 out:
        return error;
 out_status:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 #ifdef CONFIG_NFS_V3_ACL
@@ -2380,7 +2352,7 @@ static inline int decode_getacl3resok(struct xdr_stream *xdr,
        if (result->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
                goto out;
 
-       hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
+       hdrlen = xdr_stream_pos(xdr);
 
        acl = NULL;
        if (result->mask & NFS_ACL)
@@ -2406,53 +2378,116 @@ out:
        return error;
 }
 
-static int nfs3_xdr_dec_getacl3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_getacl3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
                                   struct nfs3_getaclres *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
                goto out_default;
-       error = decode_getacl3resok(&xdr, result);
+       error = decode_getacl3resok(xdr, result);
 out:
        return error;
 out_default:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
-static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req, __be32 *p,
+static int nfs3_xdr_dec_setacl3res(struct rpc_rqst *req,
+                                  struct xdr_stream *xdr,
                                   struct nfs_fattr *result)
 {
-       struct xdr_stream xdr;
        enum nfs_stat status;
        int error;
 
-       xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
-       error = decode_nfsstat3(&xdr, &status);
+       error = decode_nfsstat3(xdr, &status);
        if (unlikely(error))
                goto out;
        if (status != NFS3_OK)
                goto out_default;
-       error = decode_post_op_attr(&xdr, result);
+       error = decode_post_op_attr(xdr, result);
 out:
        return error;
 out_default:
-       return nfs_stat_to_errno(status);
+       return nfs3_stat_to_errno(status);
 }
 
 #endif  /* CONFIG_NFS_V3_ACL */
 
+
+/*
+ * 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            }
+};
+
+/**
+ * nfs3_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 nfs3_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)                            \
 [NFS3PROC_##proc] = {                                                  \
        .p_proc      = NFS3PROC_##proc,                                 \
        .p_encode    = (kxdreproc_t)nfs3_xdr_enc_##argtype##3args,      \
-       .p_decode    = (kxdrproc_t)nfs3_xdr_dec_##restype##3res,        \
+       .p_decode    = (kxdrdproc_t)nfs3_xdr_dec_##restype##3res,       \
        .p_arglen    = NFS3_##argtype##args_sz,                         \
        .p_replen    = NFS3_##restype##res_sz,                          \
        .p_timer     = timer,                                           \
@@ -2484,7 +2519,7 @@ struct rpc_procinfo       nfs3_procedures[] = {
        PROC(COMMIT,            commit,         commit,         5),
 };
 
-struct rpc_version             nfs_version3 = {
+const struct rpc_version nfs_version3 = {
        .number                 = 3,
        .nrprocs                = ARRAY_SIZE(nfs3_procedures),
        .procs                  = nfs3_procedures
@@ -2495,7 +2530,7 @@ static struct rpc_procinfo        nfs3_acl_procedures[] = {
        [ACLPROC3_GETACL] = {
                .p_proc = ACLPROC3_GETACL,
                .p_encode = (kxdreproc_t)nfs3_xdr_enc_getacl3args,
-               .p_decode = (kxdrproc_t)nfs3_xdr_dec_getacl3res,
+               .p_decode = (kxdrdproc_t)nfs3_xdr_dec_getacl3res,
                .p_arglen = ACL3_getaclargs_sz,
                .p_replen = ACL3_getaclres_sz,
                .p_timer = 1,
@@ -2504,7 +2539,7 @@ static struct rpc_procinfo        nfs3_acl_procedures[] = {
        [ACLPROC3_SETACL] = {
                .p_proc = ACLPROC3_SETACL,
                .p_encode = (kxdreproc_t)nfs3_xdr_enc_setacl3args,
-               .p_decode = (kxdrproc_t)nfs3_xdr_dec_setacl3res,
+               .p_decode = (kxdrdproc_t)nfs3_xdr_dec_setacl3res,
                .p_arglen = ACL3_setaclargs_sz,
                .p_replen = ACL3_setaclres_sz,
                .p_timer = 0,
@@ -2512,7 +2547,7 @@ static struct rpc_procinfo        nfs3_acl_procedures[] = {
        },
 };
 
-struct rpc_version             nfsacl_version3 = {
+const struct rpc_version nfsacl_version3 = {
        .number                 = 3,
        .nrprocs                = sizeof(nfs3_acl_procedures)/
                                  sizeof(nfs3_acl_procedures[0]),