Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / fs / nfsd / nfs4xdr.c
index b98b5fe..67d6800 100644 (file)
 #include <linux/nfsd/state.h>
 #include <linux/nfsd/xdr4.h>
 #include <linux/nfsd_idmap.h>
+#include <linux/nfs4.h>
+#include <linux/nfs4_acl.h>
+#include <linux/vserver/xid.h>
 
 #define NFSDDBG_FACILITY               NFSDDBG_XDR
 
-static const char utf8_byte_len[256] = {
-       1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-       1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-       1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-       1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-       0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-       3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,5,5,5,5,6,6,0,0
-};
-
-static inline int
-is_legal_utf8_sequence(unsigned char *source, int length)
-{
-       unsigned char *ptr;
-       unsigned char c;
-
-       if (length==1) return 1;
-
-       /* Check for overlong sequence, and check second byte */
-       c = *(source + 1);
-       switch (*source) {
-       case 0xE0: /* 3 bytes */
-               if ( c < 0xA0 ) return 0;
-               break;
-       case 0xF0: /* 4 bytes */
-               if ( c < 0x90 ) return 0;
-               break;
-       case 0xF8: /* 5 bytes */
-               if ( c < 0xC8 ) return 0;
-               break;
-       case 0xFC: /* 6 bytes */
-               if ( c < 0x84 ) return 0;
-               break;
-       default:
-               if ( (c & 0xC0) != 0x80) return 0;
-       }
-
-       /* Check that trailing bytes look like 10xxxxxx */
-       for (ptr = source++ + length - 1; ptr>source; ptr--)
-               if ( ((*ptr) & 0xC0) != 0x80 ) return 0;
-       return 1;
-}
-
-/* This does some screening on disallowed unicode characters.  It is NOT
- * comprehensive.
- */
-static int
-is_allowed_utf8_char(unsigned char *source, int length)
-{
-       /* We assume length and source point to a valid utf8 sequence */
-       unsigned char c;
-
-       /* Disallow F0000 and up (in utf8, F3B08080) */
-       if (*source > 0xF3 ) return 0;
-       c = *(source + 1);
-       switch (*source) {
-       case 0xF3:
-               if (c >= 0xB0) return 0;
-               break;
-       /* Disallow D800-F8FF (in utf8, EDA080-EFA3BF */
-       case 0xED:
-               if (c >= 0xA0) return 0;
-               break;
-       case 0xEE:
-               return 0;
-               break;
-       case 0xEF:
-               if (c <= 0xA3) return 0;
-       /* Disallow FFF9-FFFF (EFBFB9-EFBFBF) */
-               if (c==0xBF)
-                       /* Don't need to check <=0xBF, since valid utf8 */
-                       if ( *(source+2) >= 0xB9) return 0;
-               break;
-       }
-       return 1;
-}
-
-/* This routine should really check to see that the proper stringprep
- * mappings have been applied.  Instead, we do a simple screen of some
- * of the more obvious illegal values by calling is_allowed_utf8_char.
- * This will allow many illegal strings through, but if a client behaves,
- * it will get full functionality.  The other option (apart from full
- * stringprep checking) is to limit everything to an easily handled subset,
- * such as 7-bit ascii.
- *
- * Note - currently calling routines ignore return value except as boolean.
- */
-static int
-check_utf8(char *str, int len)
-{
-       unsigned char *chunk, *sourceend;
-       int chunklen;
-
-       chunk = str;
-       sourceend = str + len;
-
-       while (chunk < sourceend) {
-               chunklen = utf8_byte_len[*chunk];
-               if (!chunklen)
-                       return nfserr_inval;
-               if (chunk + chunklen > sourceend)
-                       return nfserr_inval;
-               if (!is_legal_utf8_sequence(chunk, chunklen))
-                       return nfserr_inval;
-               if (!is_allowed_utf8_char(chunk, chunklen))
-                       return nfserr_inval;
-               if ( (chunklen==1) && (!*chunk) )
-                       return nfserr_inval; /* Disallow embedded nulls */
-               chunk += chunklen;
-       }
-
-       return 0;
-}
-
 static int
 check_filename(char *str, int len, int err)
 {
@@ -185,7 +73,7 @@ check_filename(char *str, int len, int err)
        for (i = 0; i < len; i++)
                if (str[i] == '/')
                        return err;
-       return check_utf8(str, len);
+       return 0;
 }
 
 /*
@@ -249,7 +137,7 @@ xdr_error:                                  \
        }                                       \
 } while (0)
 
-u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes)
+static u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes)
 {
        /* We want more bytes than seem to be available.
         * Maybe we need a new page, maybe we have just run out
@@ -264,8 +152,7 @@ u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes)
        if (nbytes <= sizeof(argp->tmp))
                p = argp->tmp;
        else {
-               if (argp->tmpp)
-                       kfree(argp->tmpp);
+               kfree(argp->tmpp);
                p = argp->tmpp = kmalloc(nbytes, GFP_KERNEL);
                if (!p)
                        return NULL;
@@ -287,27 +174,40 @@ u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes)
        return p;
 }
 
-char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
+static int
+defer_free(struct nfsd4_compoundargs *argp,
+               void (*release)(const void *), void *p)
 {
        struct tmpbuf *tb;
+
+       tb = kmalloc(sizeof(*tb), GFP_KERNEL);
+       if (!tb)
+               return -ENOMEM;
+       tb->buf = p;
+       tb->release = release;
+       tb->next = argp->to_free;
+       argp->to_free = tb;
+       return 0;
+}
+
+static char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
+{
+       void *new = NULL;
        if (p == argp->tmp) {
-               p = kmalloc(nbytes, GFP_KERNEL);
-               if (!p) return NULL;
+               new = kmalloc(nbytes, GFP_KERNEL);
+               if (!new) return NULL;
+               p = new;
                memcpy(p, argp->tmp, nbytes);
        } else {
                if (p != argp->tmpp)
                        BUG();
                argp->tmpp = NULL;
        }
-       tb = kmalloc(sizeof(*tb), GFP_KERNEL);
-       if (!tb) {
-               kfree(p);
+       if (defer_free(argp, kfree, p)) {
+               kfree(new);
                return NULL;
-       }
-       tb->buf = p;
-       tb->next = argp->to_free;
-       argp->to_free = tb;
-       return (char*)p;
+       } else
+               return (char *)p;
 }
 
 
@@ -335,7 +235,8 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
 }
 
 static int
-nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr)
+nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr,
+    struct nfs4_acl **acl)
 {
        int expected_len, len = 0;
        u32 dummy32;
@@ -364,6 +265,48 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *ia
                READ64(iattr->ia_size);
                iattr->ia_valid |= ATTR_SIZE;
        }
+       if (bmval[0] & FATTR4_WORD0_ACL) {
+               int nace, i;
+               struct nfs4_ace ace;
+
+               READ_BUF(4); len += 4;
+               READ32(nace);
+
+               *acl = nfs4_acl_new();
+               if (*acl == NULL) {
+                       status = -ENOMEM;
+                       goto out_nfserr;
+               }
+               defer_free(argp, (void (*)(const void *))nfs4_acl_free, *acl);
+
+               for (i = 0; i < nace; i++) {
+                       READ_BUF(16); len += 16;
+                       READ32(ace.type);
+                       READ32(ace.flag);
+                       READ32(ace.access_mask);
+                       READ32(dummy32);
+                       READ_BUF(dummy32);
+                       len += XDR_QUADLEN(dummy32) << 2;
+                       READMEM(buf, dummy32);
+                       ace.whotype = nfs4_acl_get_whotype(buf, dummy32);
+                       status = 0;
+                       if (ace.whotype != NFS4_ACL_WHO_NAMED)
+                               ace.who = 0;
+                       else if (ace.flag & NFS4_ACE_IDENTIFIER_GROUP)
+                               status = nfsd_map_name_to_gid(argp->rqstp,
+                                               buf, dummy32, &ace.who);
+                       else
+                               status = nfsd_map_name_to_uid(argp->rqstp,
+                                               buf, dummy32, &ace.who);
+                       if (status)
+                               goto out_nfserr;
+                       status = nfs4_acl_add_ace(*acl, ace.type, ace.flag,
+                                ace.access_mask, ace.whotype, ace.who);
+                       if (status)
+                               goto out_nfserr;
+               }
+       } else
+               *acl = NULL;
        if (bmval[1] & FATTR4_WORD1_MODE) {
                READ_BUF(4);
                len += 4;
@@ -378,8 +321,6 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *ia
                READ_BUF(dummy32);
                len += (XDR_QUADLEN(dummy32) << 2);
                READMEM(buf, dummy32);
-               if (check_utf8(buf, dummy32))
-                       return nfserr_inval;
                if ((status = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &iattr->ia_uid)))
                        goto out_nfserr;
                iattr->ia_valid |= ATTR_UID;
@@ -391,8 +332,6 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *ia
                READ_BUF(dummy32);
                len += (XDR_QUADLEN(dummy32) << 2);
                READMEM(buf, dummy32);
-               if (check_utf8(buf, dummy32))
-                       return nfserr_inval;
                if ((status = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &iattr->ia_gid)))
                        goto out_nfserr;
                iattr->ia_valid |= ATTR_GID;
@@ -484,14 +423,12 @@ nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access
        DECODE_TAIL;
 }
 
-#define NFS4_STATE_NOT_LOCKED  ((void *)-1)
-
 static int
 nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
 {
        DECODE_HEAD;
 
-       close->cl_stateowner = NFS4_STATE_NOT_LOCKED;
+       close->cl_stateowner = NULL;
        READ_BUF(4 + sizeof(stateid_t));
        READ32(close->cl_seqid);
        READ32(close->cl_stateid.si_generation);
@@ -526,8 +463,6 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
                READ32(create->cr_linklen);
                READ_BUF(create->cr_linklen);
                SAVEMEM(create->cr_linkname, create->cr_linklen);
-               if (check_utf8(create->cr_linkname, create->cr_linklen))
-                       return nfserr_inval;
                break;
        case NF4BLK:
        case NF4CHR:
@@ -538,9 +473,8 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
        case NF4SOCK:
        case NF4FIFO:
        case NF4DIR:
-               break;
        default:
-               goto xdr_error;
+               break;
        }
 
        READ_BUF(4);
@@ -550,12 +484,24 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
        if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval)))
                return status;
 
-       if ((status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr)))
+       if ((status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr, &create->cr_acl)))
                goto out;
 
        DECODE_TAIL;
 }
 
+static inline int
+nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr)
+{
+       DECODE_HEAD;
+
+       READ_BUF(sizeof(stateid_t));
+       READ32(dr->dr_stateid.si_generation);
+       COPYMEM(&dr->dr_stateid.si_opaque, sizeof(stateid_opaque_t));
+
+       DECODE_TAIL;
+}
+
 static inline int
 nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, struct nfsd4_getattr *getattr)
 {
@@ -582,7 +528,7 @@ nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
 {
        DECODE_HEAD;
 
-       lock->lk_stateowner = NFS4_STATE_NOT_LOCKED;
+       lock->lk_replay_owner = NULL;
        /*
        * type, reclaim(boolean), offset, length, new_lock_owner(boolean)
        */
@@ -640,7 +586,7 @@ nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku)
 {
        DECODE_HEAD;
 
-       locku->lu_stateowner = NFS4_STATE_NOT_LOCKED;
+       locku->lu_stateowner = NULL;
        READ_BUF(24 + sizeof(stateid_t));
        READ32(locku->lu_type);
        if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
@@ -676,7 +622,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
 
        memset(open->op_bmval, 0, sizeof(open->op_bmval));
        open->op_iattr.ia_valid = 0;
-       open->op_stateowner = NFS4_STATE_NOT_LOCKED;
+       open->op_stateowner = NULL;
 
        /* seqid, share_access, share_deny, clientid, ownerlen */
        READ_BUF(16 + sizeof(clientid_t));
@@ -699,7 +645,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
                switch (open->op_createmode) {
                case NFS4_CREATE_UNCHECKED:
                case NFS4_CREATE_GUARDED:
-                       if ((status = nfsd4_decode_fattr(argp, open->op_bmval, &open->op_iattr)))
+                       if ((status = nfsd4_decode_fattr(argp, open->op_bmval, &open->op_iattr, &open->op_acl)))
                                goto out;
                        break;
                case NFS4_CREATE_EXCLUSIVE:
@@ -732,8 +678,8 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
                READ32(open->op_delegate_type);
                break;
        case NFS4_OPEN_CLAIM_DELEGATE_CUR:
-               READ_BUF(sizeof(delegation_stateid_t) + 4);
-               COPYMEM(&open->op_delegate_stateid, sizeof(delegation_stateid_t));
+               READ_BUF(sizeof(stateid_t) + 4);
+               COPYMEM(&open->op_delegate_stateid, sizeof(stateid_t));
                READ32(open->op_fname.len);
                READ_BUF(open->op_fname.len);
                SAVEMEM(open->op_fname.data, open->op_fname.len);
@@ -752,7 +698,7 @@ nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_con
 {
        DECODE_HEAD;
                    
-       open_conf->oc_stateowner = NFS4_STATE_NOT_LOCKED;
+       open_conf->oc_stateowner = NULL;
        READ_BUF(4 + sizeof(stateid_t));
        READ32(open_conf->oc_req_stateid.si_generation);
        COPYMEM(&open_conf->oc_req_stateid.si_opaque, sizeof(stateid_opaque_t));
@@ -766,8 +712,8 @@ nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_d
 {
        DECODE_HEAD;
                    
-       open_down->od_stateowner = NFS4_STATE_NOT_LOCKED;
-       READ_BUF(4 + sizeof(stateid_t));
+       open_down->od_stateowner = NULL;
+       READ_BUF(12 + sizeof(stateid_t));
        READ32(open_down->od_stateid.si_generation);
        COPYMEM(&open_down->od_stateid.si_opaque, sizeof(stateid_opaque_t));
        READ32(open_down->od_seqid);
@@ -876,7 +822,7 @@ nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *seta
        READ_BUF(sizeof(stateid_t));
        READ32(setattr->sa_stateid.si_generation);
        COPYMEM(&setattr->sa_stateid.si_opaque, sizeof(stateid_opaque_t));
-       if ((status = nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr)))
+       if ((status = nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr, &setattr->sa_acl)))
                goto out;
 
        DECODE_TAIL;
@@ -1046,7 +992,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
        if (argp->opcnt > 100)
                goto xdr_error;
 
-       if (argp->opcnt > sizeof(argp->iops)/sizeof(argp->iops[0])) {
+       if (argp->opcnt > ARRAY_SIZE(argp->iops)) {
                argp->ops = kmalloc(argp->opcnt * sizeof(*argp->ops), GFP_KERNEL);
                if (!argp->ops) {
                        argp->ops = argp->iops;
@@ -1112,6 +1058,9 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                case OP_CREATE:
                        op->status = nfsd4_decode_create(argp, &op->u.create);
                        break;
+               case OP_DELEGRETURN:
+                       op->status = nfsd4_decode_delegreturn(argp, &op->u.delegreturn);
+                       break;
                case OP_GETATTR:
                        op->status = nfsd4_decode_getattr(argp, &op->u.getattr);
                        break;
@@ -1260,26 +1209,21 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
        save = resp->p;
 
 /*
- * Routine for encoding the result of a
- * "seqid-mutating" NFSv4 operation.  This is
- * where seqids are incremented, and the
- * replay cache is filled.
+ * Routine for encoding the result of a "seqid-mutating" NFSv4 operation.  This
+ * is where sequence id's are incremented, and the replay cache is filled.
+ * Note that we increment sequence id's here, at the last moment, so we're sure
+ * we know whether the error to be returned is a sequence id mutating error.
  */
 
 #define ENCODE_SEQID_OP_TAIL(stateowner) do {                  \
-       if (seqid_mutating_err(nfserr) && stateowner            \
-           && (stateowner != NFS4_STATE_NOT_LOCKED)) {         \
-               if (stateowner->so_confirmed)                   \
-                       stateowner->so_seqid++;                 \
+       if (seqid_mutating_err(nfserr) && stateowner) {         \
+               stateowner->so_seqid++;                         \
                stateowner->so_replay.rp_status = nfserr;       \
                stateowner->so_replay.rp_buflen =               \
                          (((char *)(resp)->p - (char *)save)); \
                memcpy(stateowner->so_replay.rp_buf, save,      \
                        stateowner->so_replay.rp_buflen);       \
-       }                                                       \
-       if (stateowner != NFS4_STATE_NOT_LOCKED)                \
-               nfs4_unlock_state();                            \
-       } while (0);
+       } } while (0);
 
 
 static u32 nfs4_ftypes[16] = {
@@ -1289,6 +1233,48 @@ static u32 nfs4_ftypes[16] = {
         NF4SOCK, NF4BAD,  NF4LNK, NF4BAD,
 };
 
+static int
+nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
+                       u32 **p, int *buflen)
+{
+       int status;
+
+       if (*buflen < (XDR_QUADLEN(IDMAP_NAMESZ) << 2) + 4)
+               return nfserr_resource;
+       if (whotype != NFS4_ACL_WHO_NAMED)
+               status = nfs4_acl_write_who(whotype, (u8 *)(*p + 1));
+       else if (group)
+               status = nfsd_map_gid_to_name(rqstp, id, (u8 *)(*p + 1));
+       else
+               status = nfsd_map_uid_to_name(rqstp, id, (u8 *)(*p + 1));
+       if (status < 0)
+               return nfserrno(status);
+       *p = xdr_encode_opaque(*p, NULL, status);
+       *buflen -= (XDR_QUADLEN(status) << 2) + 4;
+       BUG_ON(*buflen < 0);
+       return 0;
+}
+
+static inline int
+nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, u32 **p, int *buflen)
+{
+       return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, uid, 0, p, buflen);
+}
+
+static inline int
+nfsd4_encode_group(struct svc_rqst *rqstp, uid_t gid, u32 **p, int *buflen)
+{
+       return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, gid, 1, p, buflen);
+}
+
+static inline int
+nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
+               u32 **p, int *buflen)
+{
+       return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen);
+}
+
+
 /*
  * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
  * ourselves.
@@ -1304,10 +1290,6 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        u32 bmval0 = bmval[0];
        u32 bmval1 = bmval[1];
        struct kstat stat;
-       char owner[IDMAP_NAMESZ];
-       u32 ownerlen = 0;
-       char group[IDMAP_NAMESZ];
-       u32 grouplen = 0;
        struct svc_fh tempfh;
        struct kstatfs statfs;
        int buflen = *countp << 2;
@@ -1316,6 +1298,8 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        u64 dummy64;
        u32 *p = buffer;
        int status;
+       int aclsupport = 0;
+       struct nfs4_acl *acl = NULL;
 
        BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1);
        BUG_ON(bmval0 & ~NFSD_SUPPORTED_ATTRS_WORD0);
@@ -1331,30 +1315,27 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                if (status)
                        goto out_nfserr;
        }
-       if ((bmval0 & FATTR4_WORD0_FILEHANDLE) && !fhp) {
+       if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) {
                fh_init(&tempfh, NFS4_FHSIZE);
                status = fh_compose(&tempfh, exp, dentry, NULL);
                if (status)
                        goto out;
                fhp = &tempfh;
        }
-       if (bmval1 & FATTR4_WORD1_OWNER) {
-               int temp = nfsd_map_uid_to_name(rqstp, stat.uid, owner);
-               if (temp < 0) {
-                       status = temp;
-                       goto out_nfserr;
-               }
-               ownerlen = (unsigned) temp;
-       }
-       if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
-               int temp = nfsd_map_gid_to_name(rqstp, stat.gid, group);
-               if (temp < 0) {
-                       status = temp;
-                       goto out_nfserr;
+       if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT
+                       | FATTR4_WORD0_SUPPORTED_ATTRS)) {
+               status = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
+               aclsupport = (status == 0);
+               if (bmval0 & FATTR4_WORD0_ACL) {
+                       if (status == -EOPNOTSUPP)
+                               bmval0 &= ~FATTR4_WORD0_ACL;
+                       else if (status == -EINVAL) {
+                               status = nfserr_attrnotsupp;
+                               goto out;
+                       } else if (status != 0)
+                               goto out_nfserr;
                }
-               grouplen = (unsigned) temp;
        }
-
        if ((buflen -= 16) < 0)
                goto out_resource;
 
@@ -1367,7 +1348,9 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                if ((buflen -= 12) < 0)
                        goto out_resource;
                WRITE32(2);
-               WRITE32(NFSD_SUPPORTED_ATTRS_WORD0);
+               WRITE32(aclsupport ?
+                       NFSD_SUPPORTED_ATTRS_WORD0 :
+                       NFSD_SUPPORTED_ATTRS_WORD0 & ~FATTR4_WORD0_ACL);
                WRITE32(NFSD_SUPPORTED_ATTRS_WORD1);
        }
        if (bmval0 & FATTR4_WORD0_TYPE) {
@@ -1381,7 +1364,10 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        if (bmval0 & FATTR4_WORD0_FH_EXPIRE_TYPE) {
                if ((buflen -= 4) < 0)
                        goto out_resource;
-               WRITE32( NFS4_FH_NOEXPIRE_WITH_OPEN | NFS4_FH_VOL_RENAME );
+               if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
+                       WRITE32(NFS4_FH_PERSISTENT);
+               else
+                       WRITE32(NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME);
        }
        if (bmval0 & FATTR4_WORD0_CHANGE) {
                /*
@@ -1418,10 +1404,15 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        if (bmval0 & FATTR4_WORD0_FSID) {
                if ((buflen -= 16) < 0)
                        goto out_resource;
-               WRITE32(0);
-               WRITE32(MAJOR(stat.dev));
-               WRITE32(0);
-               WRITE32(MINOR(stat.dev));
+               if (is_fsid(fhp, rqstp->rq_reffh)) {
+                       WRITE64((u64)exp->ex_fsid);
+                       WRITE64((u64)0);
+               } else {
+                       WRITE32(0);
+                       WRITE32(MAJOR(stat.dev));
+                       WRITE32(0);
+                       WRITE32(MINOR(stat.dev));
+               }
        }
        if (bmval0 & FATTR4_WORD0_UNIQUE_HANDLES) {
                if ((buflen -= 4) < 0)
@@ -1438,10 +1429,44 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                        goto out_resource;
                WRITE32(0);
        }
+       if (bmval0 & FATTR4_WORD0_ACL) {
+               struct nfs4_ace *ace;
+               struct list_head *h;
+
+               if (acl == NULL) {
+                       if ((buflen -= 4) < 0)
+                               goto out_resource;
+
+                       WRITE32(0);
+                       goto out_acl;
+               }
+               if ((buflen -= 4) < 0)
+                       goto out_resource;
+               WRITE32(acl->naces);
+
+               list_for_each(h, &acl->ace_head) {
+                       ace = list_entry(h, struct nfs4_ace, l_ace);
+
+                       if ((buflen -= 4*3) < 0)
+                               goto out_resource;
+                       WRITE32(ace->type);
+                       WRITE32(ace->flag);
+                       WRITE32(ace->access_mask & NFS4_ACE_MASK_ALL);
+                       status = nfsd4_encode_aclname(rqstp, ace->whotype,
+                               ace->who, ace->flag & NFS4_ACE_IDENTIFIER_GROUP,
+                               &p, &buflen);
+                       if (status == nfserr_resource)
+                               goto out_resource;
+                       if (status)
+                               goto out;
+               }
+       }
+out_acl:
        if (bmval0 & FATTR4_WORD0_ACLSUPPORT) {
                if ((buflen -= 4) < 0)
                        goto out_resource;
-               WRITE32(0);
+               WRITE32(aclsupport ?
+                       ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
        }
        if (bmval0 & FATTR4_WORD0_CANSETTIME) {
                if ((buflen -= 4) < 0)
@@ -1536,18 +1561,22 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                WRITE32(stat.nlink);
        }
        if (bmval1 & FATTR4_WORD1_OWNER) {
-               buflen -= (XDR_QUADLEN(ownerlen) << 2) + 4;
-               if (buflen < 0)
+               status = nfsd4_encode_user(rqstp,
+                       XIDINO_UID(XID_TAG(dentry->d_inode),
+                       stat.uid, stat.xid), &p, &buflen);
+               if (status == nfserr_resource)
                        goto out_resource;
-               WRITE32(ownerlen);
-               WRITEMEM(owner, ownerlen);
+               if (status)
+                       goto out;
        }
        if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
-               buflen -= (XDR_QUADLEN(grouplen) << 2) + 4;
-               if (buflen < 0)
+               status = nfsd4_encode_group(rqstp,
+                       XIDINO_GID(XID_TAG(dentry->d_inode),
+                       stat.gid, stat.xid), &p, &buflen);
+               if (status == nfserr_resource)
                        goto out_resource;
-               WRITE32(grouplen);
-               WRITEMEM(group, grouplen);
+               if (status)
+                       goto out;
        }
        if (bmval1 & FATTR4_WORD1_RAWDEV) {
                if ((buflen -= 8) < 0)
@@ -1624,6 +1653,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        status = nfs_ok;
 
 out:
+       nfs4_acl_free(acl);
        if (fhp == &tempfh)
                fh_put(&tempfh);
        return status;
@@ -1639,6 +1669,58 @@ out_serverfault:
        goto out;
 }
 
+static int
+nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
+               const char *name, int namlen, u32 *p, int *buflen)
+{
+       struct svc_export *exp = cd->rd_fhp->fh_export;
+       struct dentry *dentry;
+       int nfserr;
+
+       dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen);
+       if (IS_ERR(dentry))
+               return nfserrno(PTR_ERR(dentry));
+
+       exp_get(exp);
+       if (d_mountpoint(dentry)) {
+               if (nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp)) {
+               /*
+                * -EAGAIN is the only error returned from
+                * nfsd_cross_mnt() and it indicates that an
+                * up-call has  been initiated to fill in the export
+                * options on exp.  When the answer comes back,
+                * this call will be retried.
+                */
+                       nfserr = nfserr_dropit;
+                       goto out_put;
+               }
+
+       }
+       nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval,
+                                       cd->rd_rqstp);
+out_put:
+       dput(dentry);
+       exp_put(exp);
+       return nfserr;
+}
+
+static u32 *
+nfsd4_encode_rdattr_error(u32 *p, int buflen, int nfserr)
+{
+       u32 *attrlenp;
+
+       if (buflen < 6)
+               return NULL;
+       *p++ = htonl(2);
+       *p++ = htonl(FATTR4_WORD0_RDATTR_ERROR); /* bmval0 */
+       *p++ = htonl(0);                         /* bmval1 */
+
+       attrlenp = p++;
+       *p++ = nfserr;       /* no htonl */
+       *attrlenp = htonl((char *)p - (char *)attrlenp - 4);
+       return p;
+}
+
 static int
 nfsd4_encode_dirent(struct readdir_cd *ccd, const char *name, int namlen,
                    loff_t offset, ino_t ino, unsigned int d_type)
@@ -1646,11 +1728,7 @@ nfsd4_encode_dirent(struct readdir_cd *ccd, const char *name, int namlen,
        struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
        int buflen;
        u32 *p = cd->buffer;
-       u32 *attrlenp;
-       struct dentry *dentry;
-       struct svc_export *exp = cd->rd_fhp->fh_export;
-       u32 bmval0, bmval1;
-       int nfserr = 0;
+       int nfserr = nfserr_toosmall;
 
        /* In nfsv4, "." and ".." never make it onto the wire.. */
        if (name && isdotent(name, namlen)) {
@@ -1663,102 +1741,45 @@ nfsd4_encode_dirent(struct readdir_cd *ccd, const char *name, int namlen,
 
        buflen = cd->buflen - 4 - XDR_QUADLEN(namlen);
        if (buflen < 0)
-               goto nospc;
+               goto fail;
 
        *p++ = xdr_one;                             /* mark entry present */
        cd->offset = p;                             /* remember pointer */
        p = xdr_encode_hyper(p, NFS_OFFSET_MAX);    /* offset of next entry */
        p = xdr_encode_array(p, name, namlen);      /* name length & name */
 
-       /*
-        * Now we come to the ugly part: writing the fattr for this entry.
-        */
-       bmval0 = cd->rd_bmval[0];
-       bmval1 = cd->rd_bmval[1];
-       if ((bmval0 & ~(FATTR4_WORD0_RDATTR_ERROR | FATTR4_WORD0_FILEID)) || bmval1)  {
-               /*
-                * "Heavyweight" case: we have no choice except to
-                * call nfsd4_encode_fattr(). 
-                */
-               dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen);
-               if (IS_ERR(dentry)) {
-                       nfserr = nfserrno(PTR_ERR(dentry));
-                       goto error;
-               }
-
-               if (d_mountpoint(dentry)) {
-                       if ((nfserr = nfsd_cross_mnt(cd->rd_rqstp, &dentry, 
-                                        &exp))) {      
-                       /* 
-                        * -EAGAIN is the only error returned from 
-                        * nfsd_cross_mnt() and it indicates that an 
-                        * up-call has  been initiated to fill in the export 
-                        * options on exp.  When the answer comes back,
-                        * this call will be retried.
-                        */
-                               dput(dentry);
-                               nfserr = nfserr_dropit;
-                               goto error;
-                       }
-
-               }
-
-               nfserr = nfsd4_encode_fattr(NULL, exp,
-                               dentry, p, &buflen, cd->rd_bmval,
-                               cd->rd_rqstp);
-               if (!nfserr) {
-                       p += buflen;
-                       goto out;
-               }
-               if (nfserr == nfserr_resource)
-                       goto nospc;
-
-error:
+       nfserr = nfsd4_encode_dirent_fattr(cd, name, namlen, p, &buflen);
+       switch (nfserr) {
+       case nfs_ok:
+               p += buflen;
+               break;
+       case nfserr_resource:
+               nfserr = nfserr_toosmall;
+               goto fail;
+       case nfserr_dropit:
+               goto fail;
+       default:
                /*
-                * If we get here, we experienced a miscellaneous
-                * failure while writing the attributes.  If the
-                * client requested the RDATTR_ERROR attribute,
+                * If the client requested the RDATTR_ERROR attribute,
                 * we stuff the error code into this attribute
                 * and continue.  If this attribute was not requested,
                 * then in accordance with the spec, we fail the
                 * entire READDIR operation(!)
                 */
-               if (!(bmval0 & FATTR4_WORD0_RDATTR_ERROR)) {
-                       cd->common.err = nfserr;
-                       return -EINVAL;
+               if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
+                       goto fail;
+               p = nfsd4_encode_rdattr_error(p, buflen, nfserr);
+               if (p == NULL) {
+                       nfserr = nfserr_toosmall;
+                       goto fail;
                }
-
-               bmval0 = FATTR4_WORD0_RDATTR_ERROR;
-               bmval1 = 0;
-               /* falling through here will do the right thing... */
        }
-
-       /*
-        * In the common "lightweight" case, we avoid
-        * the overhead of nfsd4_encode_fattr() by assembling
-        * a small fattr by hand.
-        */
-       if (buflen < 6)
-               goto nospc;
-       *p++ = htonl(2);
-       *p++ = htonl(bmval0);
-       *p++ = htonl(bmval1);
-
-       attrlenp = p++;
-       if (bmval0 & FATTR4_WORD0_RDATTR_ERROR)
-               *p++ = nfserr;       /* no htonl */
-       if (bmval0 & FATTR4_WORD0_FILEID)
-               p = xdr_encode_hyper(p, (u64)ino);
-       *attrlenp = htonl((char *)p - (char *)attrlenp - 4);
-
-out:
        cd->buflen -= (p - cd->buffer);
        cd->buffer = p;
        cd->common.err = nfs_ok;
        return 0;
-
-nospc:
-       cd->common.err = nfserr_toosmall;
+fail:
+       cd->common.err = nfserr;
        return -EINVAL;
 }
 
@@ -1860,20 +1881,25 @@ nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denie
 {
        ENCODE_HEAD;
 
-       RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop->so_owner.len));
+       RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop ? ld->ld_sop->so_owner.len : 0));
        WRITE64(ld->ld_start);
        WRITE64(ld->ld_length);
        WRITE32(ld->ld_type);
-       WRITEMEM(&ld->ld_sop->so_client->cl_clientid, 8);
-       WRITE32(ld->ld_sop->so_owner.len);
-       WRITEMEM(ld->ld_sop->so_owner.data, ld->ld_sop->so_owner.len);
+       if (ld->ld_sop) {
+               WRITEMEM(&ld->ld_clientid, 8);
+               WRITE32(ld->ld_sop->so_owner.len);
+               WRITEMEM(ld->ld_sop->so_owner.data, ld->ld_sop->so_owner.len);
+               kref_put(&ld->ld_sop->so_ref, nfs4_free_stateowner);
+       }  else {  /* non - nfsv4 lock in conflict, no clientid nor owner */
+               WRITE64((u64)0); /* clientid */
+               WRITE32(0); /* length of owner name */
+       }
        ADJUST_ARGS();
 }
 
 static void
 nfsd4_encode_lock(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_lock *lock)
 {
-
        ENCODE_SEQID_OP_HEAD;
 
        if (!nfserr) {
@@ -1884,7 +1910,7 @@ nfsd4_encode_lock(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_lock
        } else if (nfserr == nfserr_denied)
                nfsd4_encode_lock_denied(resp, &lock->lk_denied);
 
-       ENCODE_SEQID_OP_TAIL(lock->lk_stateowner);
+       ENCODE_SEQID_OP_TAIL(lock->lk_replay_owner);
 }
 
 static void
@@ -1946,9 +1972,9 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open
        case NFS4_OPEN_DELEGATE_NONE:
                break;
        case NFS4_OPEN_DELEGATE_READ:
-               RESERVE_SPACE(20 + sizeof(delegation_stateid_t));
-               WRITEMEM(&open->op_delegate_stateid, sizeof(delegation_stateid_t));
-               WRITE32(0);
+               RESERVE_SPACE(20 + sizeof(stateid_t));
+               WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t));
+               WRITE32(open->op_recall);
 
                /*
                 * TODO: ACE's in delegations
@@ -1960,8 +1986,8 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open
                ADJUST_ARGS();
                break;
        case NFS4_OPEN_DELEGATE_WRITE:
-               RESERVE_SPACE(32 + sizeof(delegation_stateid_t));
-               WRITEMEM(&open->op_delegate_stateid, sizeof(delegation_stateid_t));
+               RESERVE_SPACE(32 + sizeof(stateid_t));
+               WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t));
                WRITE32(0);
 
                /*
@@ -2050,10 +2076,10 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read
        }
        read->rd_vlen = v;
 
-       nfserr = nfsd_read(read->rd_rqstp, read->rd_fhp,
-                          read->rd_offset,
-                          read->rd_iov, read->rd_vlen,
-                          &maxcount);
+       nfserr = nfsd_read(read->rd_rqstp, read->rd_fhp, read->rd_filp,
+                       read->rd_offset, read->rd_iov, read->rd_vlen,
+                       &maxcount);
+
        if (nfserr == nfserr_symlink)
                nfserr = nfserr_inval;
        if (nfserr)
@@ -2063,27 +2089,20 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read
        WRITE32(eof);
        WRITE32(maxcount);
        ADJUST_ARGS();
-       resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base;
-
+       resp->xbuf->head[0].iov_len = (char*)p
+                                       - (char*)resp->xbuf->head[0].iov_base;
        resp->xbuf->page_len = maxcount;
 
-       /* read zero bytes -> don't set up tail */
-       if(!maxcount)
-               return 0;        
-
-       /* set up page for remaining responses */
-       svc_take_page(resp->rqstp);
-       resp->xbuf->tail[0].iov_base = 
-               page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
-       resp->rqstp->rq_restailpage = resp->rqstp->rq_resused-1;
+       /* Use rest of head for padding and remaining ops: */
+       resp->rqstp->rq_restailpage = 0;
+       resp->xbuf->tail[0].iov_base = p;
        resp->xbuf->tail[0].iov_len = 0;
-       resp->p = resp->xbuf->tail[0].iov_base;
-       resp->end = resp->p + PAGE_SIZE/4;
-
        if (maxcount&3) {
-               *(resp->p)++ = 0;
+               RESERVE_SPACE(4);
+               WRITE32(0);
                resp->xbuf->tail[0].iov_base += maxcount&3;
                resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
+               ADJUST_ARGS();
        }
        return 0;
 }
@@ -2120,21 +2139,20 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_r
 
        WRITE32(maxcount);
        ADJUST_ARGS();
-       resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base;
+       resp->xbuf->head[0].iov_len = (char*)p
+                               - (char*)resp->xbuf->head[0].iov_base;
+       resp->xbuf->page_len = maxcount;
 
-       svc_take_page(resp->rqstp);
-       resp->xbuf->tail[0].iov_base = 
-               page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
-       resp->rqstp->rq_restailpage = resp->rqstp->rq_resused-1;
+       /* Use rest of head for padding and remaining ops: */
+       resp->rqstp->rq_restailpage = 0;
+       resp->xbuf->tail[0].iov_base = p;
        resp->xbuf->tail[0].iov_len = 0;
-       resp->p = resp->xbuf->tail[0].iov_base;
-       resp->end = resp->p + PAGE_SIZE/4;
-
-       resp->xbuf->page_len = maxcount;
        if (maxcount&3) {
-               *(resp->p)++ = 0;
+               RESERVE_SPACE(4);
+               WRITE32(0);
                resp->xbuf->tail[0].iov_base += maxcount&3;
                resp->xbuf->tail[0].iov_len = 4 - (maxcount&3);
+               ADJUST_ARGS();
        }
        return 0;
 }
@@ -2144,7 +2162,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re
 {
        int maxcount;
        loff_t offset;
-       u32 *page, *savep;
+       u32 *page, *savep, *tailbase;
        ENCODE_HEAD;
 
        if (nfserr)
@@ -2160,6 +2178,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re
        WRITE32(0);
        ADJUST_ARGS();
        resp->xbuf->head[0].iov_len = ((char*)resp->p) - (char*)resp->xbuf->head[0].iov_base;
+       tailbase = p;
 
        maxcount = PAGE_SIZE;
        if (maxcount > readdir->rd_maxcount)
@@ -2204,14 +2223,12 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re
        *p++ = htonl(readdir->common.err == nfserr_eof);
        resp->xbuf->page_len = ((char*)p) - (char*)page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
 
-       /* allocate a page for the tail */
-       svc_take_page(resp->rqstp);
-       resp->xbuf->tail[0].iov_base = 
-               page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
-       resp->rqstp->rq_restailpage = resp->rqstp->rq_resused-1;
+       /* Use rest of head for padding and remaining ops: */
+       resp->rqstp->rq_restailpage = 0;
+       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/4;
+       resp->end = resp->p + (PAGE_SIZE - resp->xbuf->head[0].iov_len)/4;
 
        return 0;
 err_no_verf:
@@ -2325,6 +2342,8 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
        case OP_CREATE:
                nfsd4_encode_create(resp, op->status, &op->u.create);
                break;
+       case OP_DELEGRETURN:
+               break;
        case OP_GETATTR:
                op->status = nfsd4_encode_getattr(resp, op->status, &op->u.getattr);
                break;
@@ -2433,7 +2452,6 @@ nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
        RESERVE_SPACE(rp->rp_buflen);
        WRITEMEM(rp->rp_buf, rp->rp_buflen);
        ADJUST_ARGS();
-       nfs4_unlock_state();
 }
 
 /*
@@ -2446,6 +2464,22 @@ nfs4svc_encode_voidres(struct svc_rqst *rqstp, u32 *p, void *dummy)
         return xdr_ressize_check(rqstp, p);
 }
 
+void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args)
+{
+       if (args->ops != args->iops) {
+               kfree(args->ops);
+               args->ops = args->iops;
+       }
+       kfree(args->tmpp);
+       args->tmpp = NULL;
+       while (args->to_free) {
+               struct tmpbuf *tb = args->to_free;
+               args->to_free = tb->next;
+               tb->release(tb->buf);
+               kfree(tb);
+       }
+}
+
 int
 nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoundargs *args)
 {
@@ -2462,20 +2496,7 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoun
 
        status = nfsd4_decode_compound(args);
        if (status) {
-               if (args->ops != args->iops) {
-                       kfree(args->ops);
-                       args->ops = args->iops;
-               }
-               if (args->tmpp) {
-                       kfree(args->tmpp);
-                       args->tmpp = NULL;
-               }
-               while (args->to_free) {
-                       struct tmpbuf *tb = args->to_free;
-                       args->to_free = tb->next;
-                       kfree(tb->buf);
-                       kfree(tb);
-               }
+               nfsd4_release_compoundargs(args);
        }
        return !status;
 }
@@ -2486,7 +2507,7 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compound
        /*
         * All that remains is to write the tag and operation count...
         */
-       struct iovec *iov;
+       struct kvec *iov;
        p = resp->tagp;
        *p++ = htonl(resp->taglen);
        memcpy(p, resp->tag, resp->taglen);