nfsd4: teach encoders to handle reserve_space failures
[deliverable/linux.git] / fs / nfsd / nfs4xdr.c
index 63f2395c57ed72bcb55cab62e1234d3c27bea0be..799a90479487922b54a7218ebd4632ab1db75869 100644 (file)
@@ -294,7 +294,7 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
                READ32(nace);
 
                if (nace > NFS4_ACL_MAX)
-                       return nfserr_resource;
+                       return nfserr_fbig;
 
                *acl = nfs4_acl_new(nace);
                if (*acl == NULL)
@@ -1222,7 +1222,6 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
        }
        write->wr_head.iov_base = p;
        write->wr_head.iov_len = avail;
-       WARN_ON(avail != (XDR_QUADLEN(avail) << 2));
        write->wr_pagelist = argp->pagelist;
 
        len = XDR_QUADLEN(write->wr_buflen) << 2;
@@ -1678,11 +1677,6 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                        op->opnum = OP_ILLEGAL;
                        op->status = nfserr_op_illegal;
                }
-
-               if (op->status) {
-                       argp->opcnt = i+1;
-                       break;
-               }
                /*
                 * We'll try to cache the result in the DRC if any one
                 * op in the compound wants to be cached:
@@ -1690,6 +1684,11 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                cachethis |= nfsd4_cache_this_op(op);
 
                max_reply = max(max_reply, nfsd4_max_reply(op->opnum));
+
+               if (op->status) {
+                       argp->opcnt = i+1;
+                       break;
+               }
        }
        /* Sessions make the DRC unnecessary: */
        if (argp->minorversion)
@@ -1747,28 +1746,27 @@ static void write_cinfo(__be32 **p, struct nfsd4_change_info *c)
        }
 }
 
-#define RESERVE_SPACE(nbytes)  do {                            \
-       p = resp->p;                                            \
-       BUG_ON(p + XDR_QUADLEN(nbytes) > resp->end);            \
-} while (0)
-#define ADJUST_ARGS()          resp->p = p
-
 /* Encode as an array of strings the string given with components
  * separated @sep, escaped with esc_enter and esc_exit.
  */
-static __be32 nfsd4_encode_components_esc(char sep, char *components,
-                                  __be32 **pp, int *buflen,
-                                  char esc_enter, char esc_exit)
+static __be32 nfsd4_encode_components_esc(struct xdr_stream *xdr, char sep,
+                                         char *components, char esc_enter,
+                                         char esc_exit)
 {
-       __be32 *p = *pp;
-       __be32 *countp = p;
+       __be32 *p;
+       __be32 pathlen;
+       int pathlen_offset;
        int strlen, count=0;
        char *str, *end, *next;
 
        dprintk("nfsd4_encode_components(%s)\n", components);
-       if ((*buflen -= 4) < 0)
+
+       pathlen_offset = xdr->buf->len;
+       p = xdr_reserve_space(xdr, 4);
+       if (!p)
                return nfserr_resource;
-       WRITE32(0); /* We will fill this in with @count later */
+       p++; /* We will fill this in with @count later */
+
        end = str = components;
        while (*end) {
                bool found_esc = false;
@@ -1790,7 +1788,8 @@ static __be32 nfsd4_encode_components_esc(char sep, char *components,
 
                strlen = end - str;
                if (strlen) {
-                       if ((*buflen -= ((XDR_QUADLEN(strlen) << 2) + 4)) < 0)
+                       p = xdr_reserve_space(xdr, strlen + 4);
+                       if (!p)
                                return nfserr_resource;
                        WRITE32(strlen);
                        WRITEMEM(str, strlen);
@@ -1800,49 +1799,47 @@ static __be32 nfsd4_encode_components_esc(char sep, char *components,
                        end++;
                str = end;
        }
-       *pp = p;
-       p = countp;
-       WRITE32(count);
+       pathlen = htonl(xdr->buf->len - pathlen_offset);
+       write_bytes_to_xdr_buf(xdr->buf, pathlen_offset, &pathlen, 4);
        return 0;
 }
 
 /* Encode as an array of strings the string given with components
  * separated @sep.
  */
-static __be32 nfsd4_encode_components(char sep, char *components,
-                                  __be32 **pp, int *buflen)
+static __be32 nfsd4_encode_components(struct xdr_stream *xdr, char sep,
+                                     char *components)
 {
-       return nfsd4_encode_components_esc(sep, components, pp, buflen, 0, 0);
+       return nfsd4_encode_components_esc(xdr, sep, components, 0, 0);
 }
 
 /*
  * encode a location element of a fs_locations structure
  */
-static __be32 nfsd4_encode_fs_location4(struct nfsd4_fs_location *location,
-                                   __be32 **pp, int *buflen)
+static __be32 nfsd4_encode_fs_location4(struct xdr_stream *xdr,
+                                       struct nfsd4_fs_location *location)
 {
        __be32 status;
-       __be32 *p = *pp;
 
-       status = nfsd4_encode_components_esc(':', location->hosts, &p, buflen,
+       status = nfsd4_encode_components_esc(xdr, ':', location->hosts,
                                                '[', ']');
        if (status)
                return status;
-       status = nfsd4_encode_components('/', location->path, &p, buflen);
+       status = nfsd4_encode_components(xdr, '/', location->path);
        if (status)
                return status;
-       *pp = p;
        return 0;
 }
 
 /*
  * Encode a path in RFC3530 'pathname4' format
  */
-static __be32 nfsd4_encode_path(const struct path *root,
-               const struct path *path, __be32 **pp, int *buflen)
+static __be32 nfsd4_encode_path(struct xdr_stream *xdr,
+                               const struct path *root,
+                               const struct path *path)
 {
        struct path cur = *path;
-       __be32 *p = *pp;
+       __be32 *p;
        struct dentry **components = NULL;
        unsigned int ncomponents = 0;
        __be32 err = nfserr_jukebox;
@@ -1873,9 +1870,9 @@ static __be32 nfsd4_encode_path(const struct path *root,
                components[ncomponents++] = cur.dentry;
                cur.dentry = dget_parent(cur.dentry);
        }
-
-       *buflen -= 4;
-       if (*buflen < 0)
+       err = nfserr_resource;
+       p = xdr_reserve_space(xdr, 4);
+       if (!p)
                goto out_free;
        WRITE32(ncomponents);
 
@@ -1885,8 +1882,8 @@ static __be32 nfsd4_encode_path(const struct path *root,
 
                spin_lock(&dentry->d_lock);
                len = dentry->d_name.len;
-               *buflen -= 4 + (XDR_QUADLEN(len) << 2);
-               if (*buflen < 0) {
+               p = xdr_reserve_space(xdr, len + 4);
+               if (!p) {
                        spin_unlock(&dentry->d_lock);
                        goto out_free;
                }
@@ -1898,7 +1895,6 @@ static __be32 nfsd4_encode_path(const struct path *root,
                ncomponents--;
        }
 
-       *pp = p;
        err = 0;
 out_free:
        dprintk(")\n");
@@ -1909,8 +1905,8 @@ out_free:
        return err;
 }
 
-static __be32 nfsd4_encode_fsloc_fsroot(struct svc_rqst *rqstp,
-               const struct path *path, __be32 **pp, int *buflen)
+static __be32 nfsd4_encode_fsloc_fsroot(struct xdr_stream *xdr,
+                       struct svc_rqst *rqstp, const struct path *path)
 {
        struct svc_export *exp_ps;
        __be32 res;
@@ -1918,7 +1914,7 @@ static __be32 nfsd4_encode_fsloc_fsroot(struct svc_rqst *rqstp,
        exp_ps = rqst_find_fsidzero_export(rqstp);
        if (IS_ERR(exp_ps))
                return nfserrno(PTR_ERR(exp_ps));
-       res = nfsd4_encode_path(&exp_ps->ex_path, path, pp, buflen);
+       res = nfsd4_encode_path(xdr, &exp_ps->ex_path, path);
        exp_put(exp_ps);
        return res;
 }
@@ -1926,28 +1922,26 @@ static __be32 nfsd4_encode_fsloc_fsroot(struct svc_rqst *rqstp,
 /*
  *  encode a fs_locations structure
  */
-static __be32 nfsd4_encode_fs_locations(struct svc_rqst *rqstp,
-                                    struct svc_export *exp,
-                                    __be32 **pp, int *buflen)
+static __be32 nfsd4_encode_fs_locations(struct xdr_stream *xdr,
+                       struct svc_rqst *rqstp, struct svc_export *exp)
 {
        __be32 status;
        int i;
-       __be32 *p = *pp;
+       __be32 *p;
        struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
 
-       status = nfsd4_encode_fsloc_fsroot(rqstp, &exp->ex_path, &p, buflen);
+       status = nfsd4_encode_fsloc_fsroot(xdr, rqstp, &exp->ex_path);
        if (status)
                return status;
-       if ((*buflen -= 4) < 0)
+       p = xdr_reserve_space(xdr, 4);
+       if (!p)
                return nfserr_resource;
        WRITE32(fslocs->locations_count);
        for (i=0; i<fslocs->locations_count; i++) {
-               status = nfsd4_encode_fs_location4(&fslocs->locations[i],
-                                                  &p, buflen);
+               status = nfsd4_encode_fs_location4(xdr, &fslocs->locations[i]);
                if (status)
                        return status;
        }
-       *pp = p;
        return 0;
 }
 
@@ -1966,15 +1960,15 @@ static u32 nfs4_file_type(umode_t mode)
 }
 
 static inline __be32
-nfsd4_encode_aclname(struct svc_rqst *rqstp, struct nfs4_ace *ace,
-               __be32 **p, int *buflen)
+nfsd4_encode_aclname(struct xdr_stream *xdr, struct svc_rqst *rqstp,
+                    struct nfs4_ace *ace)
 {
        if (ace->whotype != NFS4_ACL_WHO_NAMED)
-               return nfs4_acl_write_who(ace->whotype, p, buflen);
+               return nfs4_acl_write_who(xdr, ace->whotype);
        else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
-               return nfsd4_encode_group(rqstp, ace->who_gid, p, buflen);
+               return nfsd4_encode_group(xdr, rqstp, ace->who_gid);
        else
-               return nfsd4_encode_user(rqstp, ace->who_uid, p, buflen);
+               return nfsd4_encode_user(xdr, rqstp, ace->who_uid);
 }
 
 #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
@@ -1983,31 +1977,28 @@ nfsd4_encode_aclname(struct svc_rqst *rqstp, struct nfs4_ace *ace,
 
 #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
 static inline __be32
-nfsd4_encode_security_label(struct svc_rqst *rqstp, void *context, int len, __be32 **pp, int *buflen)
+nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
+                           void *context, int len)
 {
-       __be32 *p = *pp;
+       __be32 *p;
 
-       if (*buflen < ((XDR_QUADLEN(len) << 2) + 4 + 4 + 4))
+       p = xdr_reserve_space(xdr, len + 4 + 4 + 4);
+       if (!p)
                return nfserr_resource;
 
        /*
         * For now we use a 0 here to indicate the null translation; in
         * the future we may place a call to translation code here.
         */
-       if ((*buflen -= 8) < 0)
-               return nfserr_resource;
-
        WRITE32(0); /* lfs */
        WRITE32(0); /* pi */
        p = xdr_encode_opaque(p, context, len);
-       *buflen -= (XDR_QUADLEN(len) << 2) + 4;
-
-       *pp = p;
        return 0;
 }
 #else
 static inline __be32
-nfsd4_encode_security_label(struct svc_rqst *rqstp, void *context, int len, __be32 **pp, int *buflen)
+nfsd4_encode_security_label(struct xdr_stream *xdr, struct svc_rqst *rqstp,
+                           void *context, int len)
 { return 0; }
 #endif
 
@@ -2046,12 +2037,11 @@ static int get_parent_attributes(struct svc_export *exp, struct kstat *stat)
 /*
  * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
  * ourselves.
- *
- * countp is the buffer size in _words_
  */
 __be32
-nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
-               struct dentry *dentry, __be32 **buffer, int count, u32 *bmval,
+nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
+               struct svc_export *exp,
+               struct dentry *dentry, u32 *bmval,
                struct svc_rqst *rqstp, int ignore_crossmnt)
 {
        u32 bmval0 = bmval[0];
@@ -2060,12 +2050,13 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        struct kstat stat;
        struct svc_fh *tempfh = NULL;
        struct kstatfs statfs;
-       int buflen = count << 2;
-       __be32 *attrlenp;
+       __be32 *p;
+       int starting_len = xdr->buf->len;
+       int attrlen_offset;
+       __be32 attrlen;
        u32 dummy;
        u64 dummy64;
        u32 rdattr_err = 0;
-       __be32 *p = *buffer;
        __be32 status;
        int err;
        int aclsupport = 0;
@@ -2146,25 +2137,33 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
 #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
 
        if (bmval2) {
-               if ((buflen -= 16) < 0)
+               p = xdr_reserve_space(xdr, 16);
+               if (!p)
                        goto out_resource;
                WRITE32(3);
                WRITE32(bmval0);
                WRITE32(bmval1);
                WRITE32(bmval2);
        } else if (bmval1) {
-               if ((buflen -= 12) < 0)
+               p = xdr_reserve_space(xdr, 12);
+               if (!p)
                        goto out_resource;
                WRITE32(2);
                WRITE32(bmval0);
                WRITE32(bmval1);
        } else {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                WRITE32(1);
                WRITE32(bmval0);
        }
-       attrlenp = p++;                /* to be backfilled later */
+
+       attrlen_offset = xdr->buf->len;
+       p = xdr_reserve_space(xdr, 4);
+       if (!p)
+               goto out_resource;
+       p++;                /* to be backfilled later */
 
        if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
                u32 word0 = nfsd_suppattrs0(minorversion);
@@ -2176,13 +2175,15 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                if (!contextsupport)
                        word2 &= ~FATTR4_WORD2_SECURITY_LABEL;
                if (!word2) {
-                       if ((buflen -= 12) < 0)
+                       p = xdr_reserve_space(xdr, 12);
+                       if (!p)
                                goto out_resource;
                        WRITE32(2);
                        WRITE32(word0);
                        WRITE32(word1);
                } else {
-                       if ((buflen -= 16) < 0)
+                       p = xdr_reserve_space(xdr, 16);
+                       if (!p)
                                goto out_resource;
                        WRITE32(3);
                        WRITE32(word0);
@@ -2191,7 +2192,8 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                }
        }
        if (bmval0 & FATTR4_WORD0_TYPE) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                dummy = nfs4_file_type(stat.mode);
                if (dummy == NF4BAD) {
@@ -2201,7 +2203,8 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                WRITE32(dummy);
        }
        if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
                        WRITE32(NFS4_FH_PERSISTENT);
@@ -2209,32 +2212,38 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                        WRITE32(NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME);
        }
        if (bmval0 & FATTR4_WORD0_CHANGE) {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                write_change(&p, &stat, dentry->d_inode);
        }
        if (bmval0 & FATTR4_WORD0_SIZE) {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                WRITE64(stat.size);
        }
        if (bmval0 & FATTR4_WORD0_LINK_SUPPORT) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(1);
        }
        if (bmval0 & FATTR4_WORD0_SYMLINK_SUPPORT) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(1);
        }
        if (bmval0 & FATTR4_WORD0_NAMED_ATTR) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(0);
        }
        if (bmval0 & FATTR4_WORD0_FSID) {
-               if ((buflen -= 16) < 0)
+               p = xdr_reserve_space(xdr, 16);
+               if (!p)
                        goto out_resource;
                if (exp->ex_fslocs.migrated) {
                        WRITE64(NFS4_REFERRAL_FSID_MAJOR);
@@ -2256,17 +2265,20 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                }
        }
        if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(0);
        }
        if (bmval0 & FATTR4_WORD0_LEASE_TIME) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(nn->nfsd4_lease);
        }
        if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(rdattr_err);
        }
@@ -2274,198 +2286,229 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                struct nfs4_ace *ace;
 
                if (acl == NULL) {
-                       if ((buflen -= 4) < 0)
+                       p = xdr_reserve_space(xdr, 4);
+                       if (!p)
                                goto out_resource;
 
                        WRITE32(0);
                        goto out_acl;
                }
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(acl->naces);
 
                for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
-                       if ((buflen -= 4*3) < 0)
+                       p = xdr_reserve_space(xdr, 4*3);
+                       if (!p)
                                goto out_resource;
                        WRITE32(ace->type);
                        WRITE32(ace->flag);
                        WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL);
-                       status = nfsd4_encode_aclname(rqstp, ace, &p, &buflen);
+                       status = nfsd4_encode_aclname(xdr, rqstp, ace);
                        if (status)
                                goto out;
                }
        }
 out_acl:
        if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(aclsupport ?
                        ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
        }
        if (bmval0 & FATTR4_WORD0_CANSETTIME) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(1);
        }
        if (bmval0 & FATTR4_WORD0_CASE_INSENSITIVE) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(0);
        }
        if (bmval0 & FATTR4_WORD0_CASE_PRESERVING) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(1);
        }
        if (bmval0 & FATTR4_WORD0_CHOWN_RESTRICTED) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(1);
        }
        if (bmval0 & FATTR4_WORD0_FILEHANDLE) {
-               buflen -= (XDR_QUADLEN(fhp->fh_handle.fh_size) << 2) + 4;
-               if (buflen < 0)
+               p = xdr_reserve_space(xdr, fhp->fh_handle.fh_size + 4);
+               if (!p)
                        goto out_resource;
                WRITE32(fhp->fh_handle.fh_size);
                WRITEMEM(&fhp->fh_handle.fh_base, fhp->fh_handle.fh_size);
        }
        if (bmval0 & FATTR4_WORD0_FILEID) {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                WRITE64(stat.ino);
        }
        if (bmval0 & FATTR4_WORD0_FILES_AVAIL) {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                WRITE64((u64) statfs.f_ffree);
        }
        if (bmval0 & FATTR4_WORD0_FILES_FREE) {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                WRITE64((u64) statfs.f_ffree);
        }
        if (bmval0 & FATTR4_WORD0_FILES_TOTAL) {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                WRITE64((u64) statfs.f_files);
        }
        if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
-               status = nfsd4_encode_fs_locations(rqstp, exp, &p, &buflen);
+               status = nfsd4_encode_fs_locations(xdr, rqstp, exp);
                if (status)
                        goto out;
        }
        if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(1);
        }
        if (bmval0 & FATTR4_WORD0_MAXFILESIZE) {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                WRITE64(exp->ex_path.mnt->mnt_sb->s_maxbytes);
        }
        if (bmval0 & FATTR4_WORD0_MAXLINK) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(255);
        }
        if (bmval0 & FATTR4_WORD0_MAXNAME) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(statfs.f_namelen);
        }
        if (bmval0 & FATTR4_WORD0_MAXREAD) {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                WRITE64((u64) svc_max_payload(rqstp));
        }
        if (bmval0 & FATTR4_WORD0_MAXWRITE) {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                WRITE64((u64) svc_max_payload(rqstp));
        }
        if (bmval1 & FATTR4_WORD1_MODE) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(stat.mode & S_IALLUGO);
        }
        if (bmval1 & FATTR4_WORD1_NO_TRUNC) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(1);
        }
        if (bmval1 & FATTR4_WORD1_NUMLINKS) {
-               if ((buflen -= 4) < 0)
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
                        goto out_resource;
                WRITE32(stat.nlink);
        }
        if (bmval1 & FATTR4_WORD1_OWNER) {
-               status = nfsd4_encode_user(rqstp, stat.uid, &p, &buflen);
+               status = nfsd4_encode_user(xdr, rqstp, stat.uid);
                if (status)
                        goto out;
        }
        if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
-               status = nfsd4_encode_group(rqstp, stat.gid, &p, &buflen);
+               status = nfsd4_encode_group(xdr, rqstp, stat.gid);
                if (status)
                        goto out;
        }
        if (bmval1 & FATTR4_WORD1_RAWDEV) {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                WRITE32((u32) MAJOR(stat.rdev));
                WRITE32((u32) MINOR(stat.rdev));
        }
        if (bmval1 & FATTR4_WORD1_SPACE_AVAIL) {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                dummy64 = (u64)statfs.f_bavail * (u64)statfs.f_bsize;
                WRITE64(dummy64);
        }
        if (bmval1 & FATTR4_WORD1_SPACE_FREE) {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                dummy64 = (u64)statfs.f_bfree * (u64)statfs.f_bsize;
                WRITE64(dummy64);
        }
        if (bmval1 & FATTR4_WORD1_SPACE_TOTAL) {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                dummy64 = (u64)statfs.f_blocks * (u64)statfs.f_bsize;
                WRITE64(dummy64);
        }
        if (bmval1 & FATTR4_WORD1_SPACE_USED) {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                dummy64 = (u64)stat.blocks << 9;
                WRITE64(dummy64);
        }
        if (bmval1 & FATTR4_WORD1_TIME_ACCESS) {
-               if ((buflen -= 12) < 0)
+               p = xdr_reserve_space(xdr, 12);
+               if (!p)
                        goto out_resource;
                WRITE64((s64)stat.atime.tv_sec);
                WRITE32(stat.atime.tv_nsec);
        }
        if (bmval1 & FATTR4_WORD1_TIME_DELTA) {
-               if ((buflen -= 12) < 0)
+               p = xdr_reserve_space(xdr, 12);
+               if (!p)
                        goto out_resource;
                WRITE32(0);
                WRITE32(1);
                WRITE32(0);
        }
        if (bmval1 & FATTR4_WORD1_TIME_METADATA) {
-               if ((buflen -= 12) < 0)
+               p = xdr_reserve_space(xdr, 12);
+               if (!p)
                        goto out_resource;
                WRITE64((s64)stat.ctime.tv_sec);
                WRITE32(stat.ctime.tv_nsec);
        }
        if (bmval1 & FATTR4_WORD1_TIME_MODIFY) {
-               if ((buflen -= 12) < 0)
+               p = xdr_reserve_space(xdr, 12);
+               if (!p)
                        goto out_resource;
                WRITE64((s64)stat.mtime.tv_sec);
                WRITE32(stat.mtime.tv_nsec);
        }
        if (bmval1 & FATTR4_WORD1_MOUNTED_ON_FILEID) {
-               if ((buflen -= 8) < 0)
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
                        goto out_resource;
                /*
                 * Get parent's attributes if not ignoring crossmount
@@ -2477,20 +2520,23 @@ out_acl:
                WRITE64(stat.ino);
        }
        if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
-               status = nfsd4_encode_security_label(rqstp, context,
-                               contextlen, &p, &buflen);
+               status = nfsd4_encode_security_label(xdr, rqstp, context,
+                                                               contextlen);
                if (status)
                        goto out;
        }
        if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) {
+               p = xdr_reserve_space(xdr, 16);
+               if (!p)
+                       goto out_resource;
                WRITE32(3);
                WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD0);
                WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD1);
                WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD2);
        }
 
-       *attrlenp = htonl((char *)p - (char *)attrlenp - 4);
-       *buffer = p;
+       attrlen = htonl(xdr->buf->len - attrlen_offset - 4);
+       write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, 4);
        status = nfs_ok;
 
 out:
@@ -2499,8 +2545,12 @@ out:
                security_release_secctx(context, contextlen);
 #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
        kfree(acl);
-       if (tempfh)
+       if (tempfh) {
                fh_put(tempfh);
+               kfree(tempfh);
+       }
+       if (status)
+               xdr_truncate_encode(xdr, starting_len);
        return status;
 out_nfserr:
        status = nfserrno(err);
@@ -2510,6 +2560,27 @@ out_resource:
        goto out;
 }
 
+__be32 nfsd4_encode_fattr_to_buf(__be32 **p, int words,
+                       struct svc_fh *fhp, struct svc_export *exp,
+                       struct dentry *dentry, u32 *bmval,
+                       struct svc_rqst *rqstp, int ignore_crossmnt)
+{
+       struct xdr_buf dummy = {
+                       .head[0] = {
+                               .iov_base = *p,
+                       },
+                       .buflen = words << 2,
+               };
+       struct xdr_stream xdr;
+       __be32 ret;
+
+       xdr_init_encode(&xdr, &dummy, NULL);
+       ret = nfsd4_encode_fattr(&xdr, fhp, exp, dentry, bmval, rqstp,
+                                                       ignore_crossmnt);
+       *p = xdr.p;
+       return ret;
+}
+
 static inline int attributes_need_mount(u32 *bmval)
 {
        if (bmval[0] & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_LEASE_TIME))
@@ -2573,7 +2644,8 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
 
        }
 out_encode:
-       nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval,
+       nfserr = nfsd4_encode_fattr_to_buf(p, buflen, NULL, exp, dentry,
+                                       cd->rd_bmval,
                                        cd->rd_rqstp, ignore_crossmnt);
 out_put:
        dput(dentry);
@@ -2660,42 +2732,48 @@ fail:
        return -EINVAL;
 }
 
-static void
-nfsd4_encode_stateid(struct nfsd4_compoundres *resp, stateid_t *sid)
+static __be32
+nfsd4_encode_stateid(struct xdr_stream *xdr, stateid_t *sid)
 {
        __be32 *p;
 
-       RESERVE_SPACE(sizeof(stateid_t));
+       p = xdr_reserve_space(xdr, sizeof(stateid_t));
+       if (!p)
+               return nfserr_resource;
        WRITE32(sid->si_generation);
        WRITEMEM(&sid->si_opaque, sizeof(stateid_opaque_t));
-       ADJUST_ARGS();
+       return 0;
 }
 
 static __be32
 nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        __be32 *p;
 
        if (!nfserr) {
-               RESERVE_SPACE(8);
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
+                       return nfserr_resource;
                WRITE32(access->ac_supported);
                WRITE32(access->ac_resp_access);
-               ADJUST_ARGS();
        }
        return nfserr;
 }
 
 static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_bind_conn_to_session *bcts)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        __be32 *p;
 
        if (!nfserr) {
-               RESERVE_SPACE(NFS4_MAX_SESSIONID_LEN + 8);
+               p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 8);
+               if (!p)
+                       return nfserr_resource;
                WRITEMEM(bcts->sessionid.data, NFS4_MAX_SESSIONID_LEN);
                WRITE32(bcts->dir);
                /* Sorry, we do not yet support RDMA over 4.1: */
                WRITE32(0);
-               ADJUST_ARGS();
        }
        return nfserr;
 }
@@ -2703,8 +2781,10 @@ static __be32 nfsd4_encode_bind_conn_to_session(struct nfsd4_compoundres *resp,
 static __be32
 nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_close *close)
 {
+       struct xdr_stream *xdr = &resp->xdr;
+
        if (!nfserr)
-               nfsd4_encode_stateid(resp, &close->cl_stateid);
+               nfserr = nfsd4_encode_stateid(xdr, &close->cl_stateid);
 
        return nfserr;
 }
@@ -2713,12 +2793,14 @@ nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_c
 static __be32
 nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_commit *commit)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        __be32 *p;
 
        if (!nfserr) {
-               RESERVE_SPACE(NFS4_VERIFIER_SIZE);
+               p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
+               if (!p)
+                       return nfserr_resource;
                WRITEMEM(commit->co_verf.data, NFS4_VERIFIER_SIZE);
-               ADJUST_ARGS();
        }
        return nfserr;
 }
@@ -2726,15 +2808,17 @@ nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
 static __be32
 nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_create *create)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        __be32 *p;
 
        if (!nfserr) {
-               RESERVE_SPACE(32);
+               p = xdr_reserve_space(xdr, 32);
+               if (!p)
+                       return nfserr_resource;
                write_cinfo(&p, &create->cr_cinfo);
                WRITE32(2);
                WRITE32(create->cr_bmval[0]);
                WRITE32(create->cr_bmval[1]);
-               ADJUST_ARGS();
        }
        return nfserr;
 }
@@ -2743,14 +2827,13 @@ static __be32
 nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_getattr *getattr)
 {
        struct svc_fh *fhp = getattr->ga_fhp;
-       int buflen;
+       struct xdr_stream *xdr = &resp->xdr;
 
        if (nfserr)
                return nfserr;
 
-       buflen = resp->end - resp->p - (COMPOUND_ERR_SLACK_SPACE >> 2);
-       nfserr = nfsd4_encode_fattr(fhp, fhp->fh_export, fhp->fh_dentry,
-                                   &resp->p, buflen, getattr->ga_bmval,
+       nfserr = nfsd4_encode_fattr(xdr, fhp, fhp->fh_export, fhp->fh_dentry,
+                                   getattr->ga_bmval,
                                    resp->rqstp, 0);
        return nfserr;
 }
@@ -2758,16 +2841,18 @@ nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
 static __be32
 nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh **fhpp)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        struct svc_fh *fhp = *fhpp;
        unsigned int len;
        __be32 *p;
 
        if (!nfserr) {
                len = fhp->fh_handle.fh_size;
-               RESERVE_SPACE(len + 4);
+               p = xdr_reserve_space(xdr, len + 4);
+               if (!p)
+                       return nfserr_resource;
                WRITE32(len);
                WRITEMEM(&fhp->fh_handle.fh_base, len);
-               ADJUST_ARGS();
        }
        return nfserr;
 }
@@ -2776,13 +2861,15 @@ nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh
 * Including all fields other than the name, a LOCK4denied structure requires
 *   8(clientid) + 4(namelen) + 8(offset) + 8(length) + 4(type) = 32 bytes.
 */
-static void
-nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld)
+static __be32
+nfsd4_encode_lock_denied(struct xdr_stream *xdr, struct nfsd4_lock_denied *ld)
 {
        struct xdr_netobj *conf = &ld->ld_owner;
        __be32 *p;
 
-       RESERVE_SPACE(32 + XDR_LEN(conf->len));
+       p = xdr_reserve_space(xdr, 32 + XDR_LEN(conf->len));
+       if (!p)
+               return nfserr_resource;
        WRITE64(ld->ld_start);
        WRITE64(ld->ld_length);
        WRITE32(ld->ld_type);
@@ -2795,16 +2882,18 @@ nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denie
                WRITE64((u64)0); /* clientid */
                WRITE32(0); /* length of owner name */
        }
-       ADJUST_ARGS();
+       return nfserr_denied;
 }
 
 static __be32
 nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lock *lock)
 {
+       struct xdr_stream *xdr = &resp->xdr;
+
        if (!nfserr)
-               nfsd4_encode_stateid(resp, &lock->lk_resp_stateid);
+               nfserr = nfsd4_encode_stateid(xdr, &lock->lk_resp_stateid);
        else if (nfserr == nfserr_denied)
-               nfsd4_encode_lock_denied(resp, &lock->lk_denied);
+               nfserr = nfsd4_encode_lock_denied(xdr, &lock->lk_denied);
 
        return nfserr;
 }
@@ -2812,16 +2901,20 @@ nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lo
 static __be32
 nfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lockt *lockt)
 {
+       struct xdr_stream *xdr = &resp->xdr;
+
        if (nfserr == nfserr_denied)
-               nfsd4_encode_lock_denied(resp, &lockt->lt_denied);
+               nfsd4_encode_lock_denied(xdr, &lockt->lt_denied);
        return nfserr;
 }
 
 static __be32
 nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_locku *locku)
 {
+       struct xdr_stream *xdr = &resp->xdr;
+
        if (!nfserr)
-               nfsd4_encode_stateid(resp, &locku->lu_stateid);
+               nfserr = nfsd4_encode_stateid(xdr, &locku->lu_stateid);
 
        return nfserr;
 }
@@ -2830,12 +2923,14 @@ nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_l
 static __be32
 nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_link *link)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        __be32 *p;
 
        if (!nfserr) {
-               RESERVE_SPACE(20);
+               p = xdr_reserve_space(xdr, 20);
+               if (!p)
+                       return nfserr_resource;
                write_cinfo(&p, &link->li_cinfo);
-               ADJUST_ARGS();
        }
        return nfserr;
 }
@@ -2844,27 +2939,35 @@ nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_li
 static __be32
 nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        __be32 *p;
 
        if (nfserr)
                goto out;
 
-       nfsd4_encode_stateid(resp, &open->op_stateid);
-       RESERVE_SPACE(40);
+       nfserr = nfsd4_encode_stateid(xdr, &open->op_stateid);
+       if (nfserr)
+               goto out;
+       p = xdr_reserve_space(xdr, 40);
+       if (!p)
+               return nfserr_resource;
        write_cinfo(&p, &open->op_cinfo);
        WRITE32(open->op_rflags);
        WRITE32(2);
        WRITE32(open->op_bmval[0]);
        WRITE32(open->op_bmval[1]);
        WRITE32(open->op_delegate_type);
-       ADJUST_ARGS();
 
        switch (open->op_delegate_type) {
        case NFS4_OPEN_DELEGATE_NONE:
                break;
        case NFS4_OPEN_DELEGATE_READ:
-               nfsd4_encode_stateid(resp, &open->op_delegate_stateid);
-               RESERVE_SPACE(20);
+               nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid);
+               if (nfserr)
+                       return nfserr;
+               p = xdr_reserve_space(xdr, 20);
+               if (!p)
+                       return nfserr_resource;
                WRITE32(open->op_recall);
 
                /*
@@ -2874,11 +2977,14 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op
                WRITE32(0);
                WRITE32(0);
                WRITE32(0);   /* XXX: is NULL principal ok? */
-               ADJUST_ARGS();
                break;
        case NFS4_OPEN_DELEGATE_WRITE:
-               nfsd4_encode_stateid(resp, &open->op_delegate_stateid);
-               RESERVE_SPACE(32);
+               nfserr = nfsd4_encode_stateid(xdr, &open->op_delegate_stateid);
+               if (nfserr)
+                       return nfserr;
+               p = xdr_reserve_space(xdr, 32);
+               if (!p)
+                       return nfserr_resource;
                WRITE32(0);
 
                /*
@@ -2895,21 +3001,23 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op
                WRITE32(0);
                WRITE32(0);
                WRITE32(0);   /* XXX: is NULL principal ok? */
-               ADJUST_ARGS();
                break;
        case NFS4_OPEN_DELEGATE_NONE_EXT: /* 4.1 */
                switch (open->op_why_no_deleg) {
                case WND4_CONTENTION:
                case WND4_RESOURCE:
-                       RESERVE_SPACE(8);
+                       p = xdr_reserve_space(xdr, 8);
+                       if (!p)
+                               return nfserr_resource;
                        WRITE32(open->op_why_no_deleg);
                        WRITE32(0);     /* deleg signaling not supported yet */
                        break;
                default:
-                       RESERVE_SPACE(4);
+                       p = xdr_reserve_space(xdr, 4);
+                       if (!p)
+                               return nfserr_resource;
                        WRITE32(open->op_why_no_deleg);
                }
-               ADJUST_ARGS();
                break;
        default:
                BUG();
@@ -2922,8 +3030,10 @@ out:
 static __be32
 nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_confirm *oc)
 {
+       struct xdr_stream *xdr = &resp->xdr;
+
        if (!nfserr)
-               nfsd4_encode_stateid(resp, &oc->oc_resp_stateid);
+               nfserr = nfsd4_encode_stateid(xdr, &oc->oc_resp_stateid);
 
        return nfserr;
 }
@@ -2931,8 +3041,10 @@ nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct
 static __be32
 nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_downgrade *od)
 {
+       struct xdr_stream *xdr = &resp->xdr;
+
        if (!nfserr)
-               nfsd4_encode_stateid(resp, &od->od_stateid);
+               nfserr = nfsd4_encode_stateid(xdr, &od->od_stateid);
 
        return nfserr;
 }
@@ -2945,15 +3057,19 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
        int v;
        struct page *page;
        unsigned long maxcount; 
+       struct xdr_stream *xdr = &resp->xdr;
+       int starting_len = xdr->buf->len;
        long len;
        __be32 *p;
 
        if (nfserr)
                return nfserr;
-       if (resp->xbuf->page_len)
+       if (resp->xdr.buf->page_len)
                return nfserr_resource;
 
-       RESERVE_SPACE(8); /* eof flag and byte count */
+       p = xdr_reserve_space(xdr, 8); /* eof flag and byte count */
+       if (!p)
+               return nfserr_resource;
 
        maxcount = svc_max_payload(resp->rqstp);
        if (maxcount > read->rd_length)
@@ -2980,27 +3096,38 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
                        read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen,
                        &maxcount);
 
-       if (nfserr)
+       if (nfserr) {
+               /*
+                * nfsd_splice_actor may have already messed with the
+                * page length; reset it so as not to confuse
+                * xdr_truncate_encode:
+                */
+               xdr->buf->page_len = 0;
+               xdr_truncate_encode(xdr, starting_len);
                return nfserr;
+       }
        eof = (read->rd_offset + maxcount >=
               read->rd_fhp->fh_dentry->d_inode->i_size);
 
        WRITE32(eof);
        WRITE32(maxcount);
-       ADJUST_ARGS();
-       resp->xbuf->head[0].iov_len = (char*)p
-                                       - (char*)resp->xbuf->head[0].iov_base;
-       resp->xbuf->page_len = maxcount;
+       WARN_ON_ONCE(resp->xdr.buf->head[0].iov_len != (char *)p
+                               - (char *)resp->xdr.buf->head[0].iov_base);
+       resp->xdr.buf->page_len = maxcount;
+       xdr->buf->len += maxcount;
+       xdr->iov = xdr->buf->tail;
 
        /* Use rest of head for padding and remaining ops: */
-       resp->xbuf->tail[0].iov_base = p;
-       resp->xbuf->tail[0].iov_len = 0;
+       resp->xdr.buf->tail[0].iov_base = p;
+       resp->xdr.buf->tail[0].iov_len = 0;
        if (maxcount&3) {
-               RESERVE_SPACE(4);
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
+                       return nfserr_resource;
                WRITE32(0);
-               resp->xbuf->tail[0].iov_base += maxcount&3;
-               resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
-               ADJUST_ARGS();
+               resp->xdr.buf->tail[0].iov_base += maxcount&3;
+               resp->xdr.buf->tail[0].iov_len = 4 - (maxcount&3);
+               xdr->buf->len -= (maxcount&3);
        }
        return 0;
 }
@@ -3009,12 +3136,14 @@ static __be32
 nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readlink *readlink)
 {
        int maxcount;
+       struct xdr_stream *xdr = &resp->xdr;
        char *page;
+       int length_offset = xdr->buf->len;
        __be32 *p;
 
        if (nfserr)
                return nfserr;
-       if (resp->xbuf->page_len)
+       if (resp->xdr.buf->page_len)
                return nfserr_resource;
        if (!*resp->rqstp->rq_next_page)
                return nfserr_resource;
@@ -3022,7 +3151,10 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd
        page = page_address(*(resp->rqstp->rq_next_page++));
 
        maxcount = PAGE_SIZE;
-       RESERVE_SPACE(4);
+
+       p = xdr_reserve_space(xdr, 4);
+       if (!p)
+               return nfserr_resource;
 
        /*
         * XXX: By default, the ->readlink() VFS op will truncate symlinks
@@ -3032,25 +3164,29 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd
         */
        nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, page, &maxcount);
        if (nfserr == nfserr_isdir)
-               return nfserr_inval;
-       if (nfserr)
+               nfserr = nfserr_inval;
+       if (nfserr) {
+               xdr_truncate_encode(xdr, length_offset);
                return nfserr;
+       }
 
        WRITE32(maxcount);
-       ADJUST_ARGS();
-       resp->xbuf->head[0].iov_len = (char*)p
-                               - (char*)resp->xbuf->head[0].iov_base;
-       resp->xbuf->page_len = maxcount;
+       resp->xdr.buf->head[0].iov_len = (char *)p
+                               - (char *)resp->xdr.buf->head[0].iov_base;
+       resp->xdr.buf->page_len = maxcount;
+       xdr->buf->len += maxcount;
+       xdr->iov = xdr->buf->tail;
 
        /* Use rest of head for padding and remaining ops: */
-       resp->xbuf->tail[0].iov_base = p;
-       resp->xbuf->tail[0].iov_len = 0;
+       resp->xdr.buf->tail[0].iov_base = p;
+       resp->xdr.buf->tail[0].iov_len = 0;
        if (maxcount&3) {
-               RESERVE_SPACE(4);
+               p = xdr_reserve_space(xdr, 4);
+               if  (!p)
+                       return nfserr_resource;
                WRITE32(0);
-               resp->xbuf->tail[0].iov_base += maxcount&3;
-               resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
-               ADJUST_ARGS();
+               resp->xdr.buf->tail[0].iov_base += maxcount&3;
+               resp->xdr.buf->tail[0].iov_len = 4 - (maxcount&3);
        }
        return 0;
 }
@@ -3060,24 +3196,27 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
 {
        int maxcount;
        loff_t offset;
-       __be32 *page, *savep, *tailbase;
+       struct xdr_stream *xdr = &resp->xdr;
+       int starting_len = xdr->buf->len;
+       __be32 *page, *tailbase;
        __be32 *p;
 
        if (nfserr)
                return nfserr;
-       if (resp->xbuf->page_len)
+       if (resp->xdr.buf->page_len)
                return nfserr_resource;
        if (!*resp->rqstp->rq_next_page)
                return nfserr_resource;
 
-       RESERVE_SPACE(NFS4_VERIFIER_SIZE);
-       savep = p;
+       p = xdr_reserve_space(xdr, NFS4_VERIFIER_SIZE);
+       if (!p)
+               return nfserr_resource;
 
        /* XXX: Following NFSv3, we ignore the READDIR verifier for now. */
        WRITE32(0);
        WRITE32(0);
-       ADJUST_ARGS();
-       resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base;
+       resp->xdr.buf->head[0].iov_len = ((char *)resp->xdr.p)
+                               - (char *)resp->xdr.buf->head[0].iov_base;
        tailbase = p;
 
        maxcount = PAGE_SIZE;
@@ -3118,31 +3257,36 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
        p = readdir->buffer;
        *p++ = 0;       /* no more entries */
        *p++ = htonl(readdir->common.err == nfserr_eof);
-       resp->xbuf->page_len = ((char*)p) -
+       resp->xdr.buf->page_len = ((char *)p) -
                (char*)page_address(*(resp->rqstp->rq_next_page-1));
+       xdr->buf->len += xdr->buf->page_len;
+
+       xdr->iov = xdr->buf->tail;
 
        /* Use rest of head for padding and remaining ops: */
-       resp->xbuf->tail[0].iov_base = tailbase;
-       resp->xbuf->tail[0].iov_len = 0;
-       resp->p = resp->xbuf->tail[0].iov_base;
-       resp->end = resp->p + (PAGE_SIZE - resp->xbuf->head[0].iov_len)/4;
+       resp->xdr.buf->tail[0].iov_base = tailbase;
+       resp->xdr.buf->tail[0].iov_len = 0;
+       resp->xdr.p = resp->xdr.buf->tail[0].iov_base;
+       resp->xdr.end = resp->xdr.p +
+                       (PAGE_SIZE - resp->xdr.buf->head[0].iov_len)/4;
 
        return 0;
 err_no_verf:
-       p = savep;
-       ADJUST_ARGS();
+       xdr_truncate_encode(xdr, starting_len);
        return nfserr;
 }
 
 static __be32
 nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_remove *remove)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        __be32 *p;
 
        if (!nfserr) {
-               RESERVE_SPACE(20);
+               p = xdr_reserve_space(xdr, 20);
+               if (!p)
+                       return nfserr_resource;
                write_cinfo(&p, &remove->rm_cinfo);
-               ADJUST_ARGS();
        }
        return nfserr;
 }
@@ -3150,19 +3294,21 @@ nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
 static __be32
 nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_rename *rename)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        __be32 *p;
 
        if (!nfserr) {
-               RESERVE_SPACE(40);
+               p = xdr_reserve_space(xdr, 40);
+               if (!p)
+                       return nfserr_resource;
                write_cinfo(&p, &rename->rn_sinfo);
                write_cinfo(&p, &rename->rn_tinfo);
-               ADJUST_ARGS();
        }
        return nfserr;
 }
 
 static __be32
-nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
+nfsd4_do_encode_secinfo(struct xdr_stream *xdr,
                         __be32 nfserr, struct svc_export *exp)
 {
        u32 i, nflavs, supported;
@@ -3173,6 +3319,7 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
 
        if (nfserr)
                goto out;
+       nfserr = nfserr_resource;
        if (exp->ex_nflavors) {
                flavs = exp->ex_flavors;
                nflavs = exp->ex_nflavors;
@@ -3194,9 +3341,10 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
        }
 
        supported = 0;
-       RESERVE_SPACE(4);
+       p = xdr_reserve_space(xdr, 4);
+       if (!p)
+               goto out;
        flavorsp = p++;         /* to be backfilled later */
-       ADJUST_ARGS();
 
        for (i = 0; i < nflavs; i++) {
                rpc_authflavor_t pf = flavs[i].pseudoflavor;
@@ -3204,18 +3352,21 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
 
                if (rpcauth_get_gssinfo(pf, &info) == 0) {
                        supported++;
-                       RESERVE_SPACE(4 + 4 + XDR_LEN(info.oid.len) + 4 + 4);
+                       p = xdr_reserve_space(xdr, 4 + 4 +
+                                             XDR_LEN(info.oid.len) + 4 + 4);
+                       if (!p)
+                               goto out;
                        WRITE32(RPC_AUTH_GSS);
                        WRITE32(info.oid.len);
                        WRITEMEM(info.oid.data, info.oid.len);
                        WRITE32(info.qop);
                        WRITE32(info.service);
-                       ADJUST_ARGS();
                } else if (pf < RPC_AUTH_MAXFLAVOR) {
                        supported++;
-                       RESERVE_SPACE(4);
+                       p = xdr_reserve_space(xdr, 4);
+                       if (!p)
+                               goto out;
                        WRITE32(pf);
-                       ADJUST_ARGS();
                } else {
                        if (report)
                                pr_warn("NFS: SECINFO: security flavor %u "
@@ -3226,7 +3377,7 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
        if (nflavs != supported)
                report = false;
        *flavorsp = htonl(supported);
-
+       nfserr = 0;
 out:
        if (exp)
                exp_put(exp);
@@ -3237,14 +3388,18 @@ static __be32
 nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
                     struct nfsd4_secinfo *secinfo)
 {
-       return nfsd4_do_encode_secinfo(resp, nfserr, secinfo->si_exp);
+       struct xdr_stream *xdr = &resp->xdr;
+
+       return nfsd4_do_encode_secinfo(xdr, nfserr, secinfo->si_exp);
 }
 
 static __be32
 nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr,
                     struct nfsd4_secinfo_no_name *secinfo)
 {
-       return nfsd4_do_encode_secinfo(resp, nfserr, secinfo->sin_exp);
+       struct xdr_stream *xdr = &resp->xdr;
+
+       return nfsd4_do_encode_secinfo(xdr, nfserr, secinfo->sin_exp);
 }
 
 /*
@@ -3254,9 +3409,12 @@ nfsd4_encode_secinfo_no_name(struct nfsd4_compoundres *resp, __be32 nfserr,
 static __be32
 nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setattr *setattr)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        __be32 *p;
 
-       RESERVE_SPACE(16);
+       p = xdr_reserve_space(xdr, 16);
+       if (!p)
+               return nfserr_resource;
        if (nfserr) {
                WRITE32(3);
                WRITE32(0);
@@ -3269,26 +3427,28 @@ nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
                WRITE32(setattr->sa_bmval[1]);
                WRITE32(setattr->sa_bmval[2]);
        }
-       ADJUST_ARGS();
        return nfserr;
 }
 
 static __be32
 nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setclientid *scd)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        __be32 *p;
 
        if (!nfserr) {
-               RESERVE_SPACE(8 + NFS4_VERIFIER_SIZE);
+               p = xdr_reserve_space(xdr, 8 + NFS4_VERIFIER_SIZE);
+               if (!p)
+                       return nfserr_resource;
                WRITEMEM(&scd->se_clientid, 8);
                WRITEMEM(&scd->se_confirm, NFS4_VERIFIER_SIZE);
-               ADJUST_ARGS();
        }
        else if (nfserr == nfserr_clid_inuse) {
-               RESERVE_SPACE(8);
+               p = xdr_reserve_space(xdr, 8);
+               if (!p)
+                       return nfserr_resource;
                WRITE32(0);
                WRITE32(0);
-               ADJUST_ARGS();
        }
        return nfserr;
 }
@@ -3296,14 +3456,16 @@ nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct n
 static __be32
 nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_write *write)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        __be32 *p;
 
        if (!nfserr) {
-               RESERVE_SPACE(16);
+               p = xdr_reserve_space(xdr, 16);
+               if (!p)
+                       return nfserr_resource;
                WRITE32(write->wr_bytes_written);
                WRITE32(write->wr_how_written);
                WRITEMEM(write->wr_verifier.data, NFS4_VERIFIER_SIZE);
-               ADJUST_ARGS();
        }
        return nfserr;
 }
@@ -3320,6 +3482,7 @@ static __be32
 nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
                         struct nfsd4_exchange_id *exid)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        __be32 *p;
        char *major_id;
        char *server_scope;
@@ -3335,25 +3498,28 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
        server_scope = utsname()->nodename;
        server_scope_sz = strlen(server_scope);
 
-       RESERVE_SPACE(
+       p = xdr_reserve_space(xdr,
                8 /* eir_clientid */ +
                4 /* eir_sequenceid */ +
                4 /* eir_flags */ +
                4 /* spr_how */);
+       if (!p)
+               return nfserr_resource;
 
        WRITEMEM(&exid->clientid, 8);
        WRITE32(exid->seqid);
        WRITE32(exid->flags);
 
        WRITE32(exid->spa_how);
-       ADJUST_ARGS();
 
        switch (exid->spa_how) {
        case SP4_NONE:
                break;
        case SP4_MACH_CRED:
                /* spo_must_enforce, spo_must_allow */
-               RESERVE_SPACE(16);
+               p = xdr_reserve_space(xdr, 16);
+               if (!p)
+                       return nfserr_resource;
 
                /* spo_must_enforce bitmap: */
                WRITE32(2);
@@ -3362,19 +3528,20 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
                /* empty spo_must_allow bitmap: */
                WRITE32(0);
 
-               ADJUST_ARGS();
                break;
        default:
                WARN_ON_ONCE(1);
        }
 
-       RESERVE_SPACE(
+       p = xdr_reserve_space(xdr,
                8 /* so_minor_id */ +
                4 /* so_major_id.len */ +
                (XDR_QUADLEN(major_id_sz) * 4) +
                4 /* eir_server_scope.len */ +
                (XDR_QUADLEN(server_scope_sz) * 4) +
                4 /* eir_server_impl_id.count (0) */);
+       if (!p)
+               return nfserr_resource;
 
        /* The server_owner struct */
        WRITE64(minor_id);      /* Minor id */
@@ -3388,7 +3555,6 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
 
        /* Implementation id */
        WRITE32(0);     /* zero length nfs_impl_id4 array */
-       ADJUST_ARGS();
        return 0;
 }
 
@@ -3396,18 +3562,22 @@ static __be32
 nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr,
                            struct nfsd4_create_session *sess)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        __be32 *p;
 
        if (nfserr)
                return nfserr;
 
-       RESERVE_SPACE(24);
+       p = xdr_reserve_space(xdr, 24);
+       if (!p)
+               return nfserr_resource;
        WRITEMEM(sess->sessionid.data, NFS4_MAX_SESSIONID_LEN);
        WRITE32(sess->seqid);
        WRITE32(sess->flags);
-       ADJUST_ARGS();
 
-       RESERVE_SPACE(28);
+       p = xdr_reserve_space(xdr, 28);
+       if (!p)
+               return nfserr_resource;
        WRITE32(0); /* headerpadsz */
        WRITE32(sess->fore_channel.maxreq_sz);
        WRITE32(sess->fore_channel.maxresp_sz);
@@ -3415,15 +3585,17 @@ nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr,
        WRITE32(sess->fore_channel.maxops);
        WRITE32(sess->fore_channel.maxreqs);
        WRITE32(sess->fore_channel.nr_rdma_attrs);
-       ADJUST_ARGS();
 
        if (sess->fore_channel.nr_rdma_attrs) {
-               RESERVE_SPACE(4);
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
+                       return nfserr_resource;
                WRITE32(sess->fore_channel.rdma_attrs);
-               ADJUST_ARGS();
        }
 
-       RESERVE_SPACE(28);
+       p = xdr_reserve_space(xdr, 28);
+       if (!p)
+               return nfserr_resource;
        WRITE32(0); /* headerpadsz */
        WRITE32(sess->back_channel.maxreq_sz);
        WRITE32(sess->back_channel.maxresp_sz);
@@ -3431,12 +3603,12 @@ nfsd4_encode_create_session(struct nfsd4_compoundres *resp, __be32 nfserr,
        WRITE32(sess->back_channel.maxops);
        WRITE32(sess->back_channel.maxreqs);
        WRITE32(sess->back_channel.nr_rdma_attrs);
-       ADJUST_ARGS();
 
        if (sess->back_channel.nr_rdma_attrs) {
-               RESERVE_SPACE(4);
+               p = xdr_reserve_space(xdr, 4);
+               if (!p)
+                       return nfserr_resource;
                WRITE32(sess->back_channel.rdma_attrs);
-               ADJUST_ARGS();
        }
        return 0;
 }
@@ -3445,12 +3617,15 @@ static __be32
 nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr,
                      struct nfsd4_sequence *seq)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        __be32 *p;
 
        if (nfserr)
                return nfserr;
 
-       RESERVE_SPACE(NFS4_MAX_SESSIONID_LEN + 20);
+       p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN + 20);
+       if (!p)
+               return nfserr_resource;
        WRITEMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN);
        WRITE32(seq->seqid);
        WRITE32(seq->slotid);
@@ -3459,7 +3634,6 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, __be32 nfserr,
        WRITE32(seq->maxslots - 1); /* sr_target_highest_slotid */
        WRITE32(seq->status_flags);
 
-       ADJUST_ARGS();
        resp->cstate.datap = p; /* DRC cache data pointer */
        return 0;
 }
@@ -3468,17 +3642,22 @@ static __be32
 nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr,
                          struct nfsd4_test_stateid *test_stateid)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        struct nfsd4_test_stateid_id *stateid, *next;
        __be32 *p;
 
-       RESERVE_SPACE(4 + (4 * test_stateid->ts_num_ids));
+       if (nfserr)
+               return nfserr;
+
+       p = xdr_reserve_space(xdr, 4 + (4 * test_stateid->ts_num_ids));
+       if (!p)
+               return nfserr_resource;
        *p++ = htonl(test_stateid->ts_num_ids);
 
        list_for_each_entry_safe(stateid, next, &test_stateid->ts_stateid_list, ts_id_list) {
                *p++ = stateid->ts_id_status;
        }
 
-       ADJUST_ARGS();
        return nfserr;
 }
 
@@ -3579,14 +3758,12 @@ __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 pad)
                return 0;
 
        session = resp->cstate.session;
-       if (session == NULL)
-               return 0;
 
        if (xb->page_len == 0) {
-               length = (char *)resp->p - (char *)xb->head[0].iov_base + pad;
+               length = (char *)resp->xdr.p - (char *)xb->head[0].iov_base + pad;
        } else {
                if (xb->tail[0].iov_base && xb->tail[0].iov_len > 0)
-                       tlen = (char *)resp->p - (char *)xb->tail[0].iov_base;
+                       tlen = (char *)resp->xdr.p - (char *)xb->tail[0].iov_base;
 
                length = xb->head[0].iov_len + xb->page_len + tlen + pad;
        }
@@ -3606,34 +3783,58 @@ __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 pad)
 void
 nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
 {
+       struct xdr_stream *xdr = &resp->xdr;
        struct nfs4_stateowner *so = resp->cstate.replay_owner;
-       __be32 *statp;
+       struct svc_rqst *rqstp = resp->rqstp;
+       int post_err_offset;
+       nfsd4_enc encoder;
        __be32 *p;
 
-       RESERVE_SPACE(8);
+       p = xdr_reserve_space(xdr, 8);
+       if (!p) {
+               WARN_ON_ONCE(1);
+               return;
+       }
        WRITE32(op->opnum);
-       statp = p++;    /* to be backfilled at the end */
-       ADJUST_ARGS();
+       post_err_offset = xdr->buf->len;
 
        if (op->opnum == OP_ILLEGAL)
                goto status;
        BUG_ON(op->opnum < 0 || op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) ||
               !nfsd4_enc_ops[op->opnum]);
-       op->status = nfsd4_enc_ops[op->opnum](resp, op->status, &op->u);
-       /* nfsd4_check_drc_limit guarantees enough room for error status */
-       if (!op->status)
-               op->status = nfsd4_check_resp_size(resp, 0);
+       encoder = nfsd4_enc_ops[op->opnum];
+       op->status = encoder(resp, op->status, &op->u);
+       /* nfsd4_check_resp_size guarantees enough room for error status */
+       if (!op->status) {
+               int space_needed = 0;
+               if (!nfsd4_last_compound_op(rqstp))
+                       space_needed = COMPOUND_ERR_SLACK_SPACE;
+               op->status = nfsd4_check_resp_size(resp, space_needed);
+       }
+       if (op->status == nfserr_resource ||
+           op->status == nfserr_rep_too_big ||
+           op->status == nfserr_rep_too_big_to_cache) {
+               /*
+                * The operation may have already been encoded or
+                * partially encoded.  No op returns anything additional
+                * in the case of one of these three errors, so we can
+                * just truncate back to after the status.  But it's a
+                * bug if we had to do this on a non-idempotent op:
+                */
+               warn_on_nonidempotent_op(op);
+               xdr_truncate_encode(xdr, post_err_offset);
+       }
        if (so) {
+               int len = xdr->buf->len - post_err_offset;
+
                so->so_replay.rp_status = op->status;
-               so->so_replay.rp_buflen = (char *)resp->p - (char *)(statp+1);
-               memcpy(so->so_replay.rp_buf, statp+1, so->so_replay.rp_buflen);
+               so->so_replay.rp_buflen = len;
+               read_bytes_from_xdr_buf(xdr->buf, post_err_offset,
+                                               so->so_replay.rp_buf, len);
        }
 status:
-       /*
-        * Note: We write the status directly, instead of using WRITE32(),
-        * since it is already in network byte order.
-        */
-       *statp = op->status;
+       /* Note that op->status is already in network byte order: */
+       write_bytes_to_xdr_buf(xdr->buf, post_err_offset - 4, &op->status, 4);
 }
 
 /* 
@@ -3645,21 +3846,22 @@ status:
  * called with nfs4_lock_state() held
  */
 void
-nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
+nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op)
 {
        __be32 *p;
        struct nfs4_replay *rp = op->replay;
 
        BUG_ON(!rp);
 
-       RESERVE_SPACE(8);
+       p = xdr_reserve_space(xdr, 8 + rp->rp_buflen);
+       if (!p) {
+               WARN_ON_ONCE(1);
+               return;
+       }
        WRITE32(op->opnum);
        *p++ = rp->rp_status;  /* already xdr'ed */
-       ADJUST_ARGS();
 
-       RESERVE_SPACE(rp->rp_buflen);
        WRITEMEM(rp->rp_buf, rp->rp_buflen);
-       ADJUST_ARGS();
 }
 
 int
@@ -3691,6 +3893,12 @@ int nfsd4_release_compoundargs(void *rq, __be32 *p, void *resp)
 int
 nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundargs *args)
 {
+       if (rqstp->rq_arg.head[0].iov_len % 4) {
+               /* client is nuts */
+               dprintk("%s: compound not properly padded! (peeraddr=%pISc xid=0x%x)",
+                       __func__, svc_addr(rqstp), be32_to_cpu(rqstp->rq_xid));
+               return 0;
+       }
        args->p = p;
        args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len;
        args->pagelist = rqstp->rq_arg.pages;
@@ -3710,19 +3918,17 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
         * All that remains is to write the tag and operation count...
         */
        struct nfsd4_compound_state *cs = &resp->cstate;
-       struct kvec *iov;
+       struct xdr_buf *buf = resp->xdr.buf;
+
+       WARN_ON_ONCE(buf->len != buf->head[0].iov_len + buf->page_len +
+                                buf->tail[0].iov_len);
+
        p = resp->tagp;
        *p++ = htonl(resp->taglen);
        memcpy(p, resp->tag, resp->taglen);
        p += XDR_QUADLEN(resp->taglen);
        *p++ = htonl(resp->opcnt);
 
-       if (rqstp->rq_res.page_len) 
-               iov = &rqstp->rq_res.tail[0];
-       else
-               iov = &rqstp->rq_res.head[0];
-       iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
-       BUG_ON(iov->iov_len > PAGE_SIZE);
        if (nfsd4_has_session(cs)) {
                struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
                struct nfs4_client *clp = cs->session->se_client;
This page took 0.048115 seconds and 5 git commands to generate.