fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / nfsd / nfs4xdr.c
index 091114d..6fc7f32 100644 (file)
 #include <linux/nfsd_idmap.h>
 #include <linux/nfs4.h>
 #include <linux/nfs4_acl.h>
-#include <linux/vserver/xid.h>
+#include <linux/vs_tag.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.
+/*
+ * As per referral draft, the fsid for a referral MUST be different from the fsid of the containing
+ * directory in order to indicate to the client that a filesystem boundary is present
+ * We use a fixed fsid for a referral
  */
-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;
-       }
+#define NFS4_REFERRAL_FSID_MAJOR       0x8000000ULL
+#define NFS4_REFERRAL_FSID_MINOR       0x8000000ULL
 
-       return 0;
-}
-
-static int
-check_filename(char *str, int len, int err)
+static __be32
+check_filename(char *str, int len, __be32 err)
 {
        int i;
 
@@ -188,7 +81,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;
 }
 
 /*
@@ -202,8 +95,8 @@ check_filename(char *str, int len, int err)
  * consistent with the style used in NFSv2/v3...
  */
 #define DECODE_HEAD                            \
-       u32 *p;                                 \
-       int status
+       __be32 *p;                              \
+       __be32 status
 #define DECODE_TAIL                            \
        status = 0;                             \
 out:                                           \
@@ -252,13 +145,13 @@ xdr_error:                                        \
        }                                       \
 } while (0)
 
-u32 *read_buf(struct nfsd4_compoundargs *argp, int nbytes)
+static __be32 *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
         */
        int avail = (char*)argp->end - (char*)argp->p;
-       u32 *p;
+       __be32 *p;
        if (avail + argp->pagelen < nbytes)
                return NULL;
        if (avail + PAGE_SIZE < nbytes) /* need more than a page !! */
@@ -267,8 +160,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;
@@ -306,7 +198,7 @@ defer_free(struct nfsd4_compoundargs *argp,
        return 0;
 }
 
-char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
+static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes)
 {
        void *new = NULL;
        if (p == argp->tmp) {
@@ -315,8 +207,7 @@ char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
                p = new;
                memcpy(p, argp->tmp, nbytes);
        } else {
-               if (p != argp->tmpp)
-                       BUG();
+               BUG_ON(p != argp->tmpp);
                argp->tmpp = NULL;
        }
        if (defer_free(argp, kfree, p)) {
@@ -327,7 +218,7 @@ char *savemem(struct nfsd4_compoundargs *argp, u32 *p, int nbytes)
 }
 
 
-static int
+static __be32
 nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
 {
        u32 bmlen;
@@ -350,13 +241,14 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *iattr,
     struct nfs4_acl **acl)
 {
        int expected_len, len = 0;
        u32 dummy32;
        char *buf;
+       int host_err;
 
        DECODE_HEAD;
        iattr->ia_valid = 0;
@@ -390,7 +282,7 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *ia
 
                *acl = nfs4_acl_new();
                if (*acl == NULL) {
-                       status = -ENOMEM;
+                       host_err = -ENOMEM;
                        goto out_nfserr;
                }
                defer_free(argp, (void (*)(const void *))nfs4_acl_free, *acl);
@@ -404,25 +296,22 @@ 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;
                        ace.whotype = nfs4_acl_get_whotype(buf, dummy32);
-                       status = 0;
+                       host_err = 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,
+                               host_err = nfsd_map_name_to_gid(argp->rqstp,
                                                buf, dummy32, &ace.who);
                        else
-                               status = nfsd_map_name_to_uid(argp->rqstp,
+                               host_err = nfsd_map_name_to_uid(argp->rqstp,
                                                buf, dummy32, &ace.who);
-                       if (status)
+                       if (host_err)
                                goto out_nfserr;
-                       if (nfs4_acl_add_ace(*acl, ace.type, ace.flag,
-                                ace.access_mask, ace.whotype, ace.who) != 0) {
-                               status = -ENOMEM;
+                       host_err = nfs4_acl_add_ace(*acl, ace.type, ace.flag,
+                                ace.access_mask, ace.whotype, ace.who);
+                       if (host_err)
                                goto out_nfserr;
-                       }
                }
        } else
                *acl = NULL;
@@ -440,9 +329,7 @@ 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)))
+               if ((host_err = nfsd_map_name_to_uid(argp->rqstp, buf, dummy32, &iattr->ia_uid)))
                        goto out_nfserr;
                iattr->ia_valid |= ATTR_UID;
        }
@@ -453,9 +340,7 @@ 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)))
+               if ((host_err = nfsd_map_name_to_gid(argp->rqstp, buf, dummy32, &iattr->ia_gid)))
                        goto out_nfserr;
                iattr->ia_valid |= ATTR_GID;
        }
@@ -531,11 +416,11 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, struct iattr *ia
        DECODE_TAIL;
 
 out_nfserr:
-       status = nfserrno(status);
+       status = nfserrno(host_err);
        goto out;
 }
 
-static int
+static __be32
 nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access)
 {
        DECODE_HEAD;
@@ -546,7 +431,7 @@ nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
 {
        DECODE_HEAD;
@@ -561,7 +446,7 @@ nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
 }
 
 
-static int
+static __be32
 nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit)
 {
        DECODE_HEAD;
@@ -573,7 +458,7 @@ nfsd4_decode_commit(struct nfsd4_compoundargs *argp, struct nfsd4_commit *commit
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create)
 {
        DECODE_HEAD;
@@ -586,8 +471,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:
@@ -615,7 +498,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
        DECODE_TAIL;
 }
 
-static inline int
+static inline __be32
 nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegreturn *dr)
 {
        DECODE_HEAD;
@@ -627,13 +510,13 @@ nfsd4_decode_delegreturn(struct nfsd4_compoundargs *argp, struct nfsd4_delegretu
        DECODE_TAIL;
 }
 
-static inline int
+static inline __be32
 nfsd4_decode_getattr(struct nfsd4_compoundargs *argp, struct nfsd4_getattr *getattr)
 {
        return nfsd4_decode_bitmap(argp, getattr->ga_bmval);
 }
 
-static int
+static __be32
 nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link)
 {
        DECODE_HEAD;
@@ -648,12 +531,12 @@ nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link)
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
 {
        DECODE_HEAD;
 
-       lock->lk_stateowner = NULL;
+       lock->lk_replay_owner = NULL;
        /*
        * type, reclaim(boolean), offset, length, new_lock_owner(boolean)
        */
@@ -687,7 +570,7 @@ nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, struct nfsd4_lockt *lockt)
 {
        DECODE_HEAD;
@@ -706,7 +589,7 @@ nfsd4_decode_lockt(struct nfsd4_compoundargs *argp, struct nfsd4_lockt *lockt)
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku)
 {
        DECODE_HEAD;
@@ -725,7 +608,7 @@ nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku)
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup)
 {
        DECODE_HEAD;
@@ -740,7 +623,7 @@ nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
 {
        DECODE_HEAD;
@@ -818,7 +701,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_confirm *open_conf)
 {
        DECODE_HEAD;
@@ -832,7 +715,7 @@ nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_con
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_downgrade *open_down)
 {
        DECODE_HEAD;
@@ -848,7 +731,7 @@ nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_d
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
 {
        DECODE_HEAD;
@@ -863,7 +746,7 @@ nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read)
 {
        DECODE_HEAD;
@@ -877,7 +760,7 @@ nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read)
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, struct nfsd4_readdir *readdir)
 {
        DECODE_HEAD;
@@ -893,7 +776,7 @@ nfsd4_decode_readdir(struct nfsd4_compoundargs *argp, struct nfsd4_readdir *read
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove)
 {
        DECODE_HEAD;
@@ -908,7 +791,7 @@ nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename)
 {
        DECODE_HEAD;
@@ -928,7 +811,7 @@ nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid)
 {
        DECODE_HEAD;
@@ -939,7 +822,7 @@ nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid)
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *setattr)
 {
        DECODE_HEAD;
@@ -953,7 +836,7 @@ nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *seta
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid *setclientid)
 {
        DECODE_HEAD;
@@ -978,7 +861,7 @@ nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclient
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_setclientid_confirm *scd_c)
 {
        DECODE_HEAD;
@@ -991,7 +874,7 @@ nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_s
 }
 
 /* Also used for NVERIFY */
-static int
+static __be32
 nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify)
 {
 #if 0
@@ -1027,7 +910,7 @@ nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
 {
        int avail;
@@ -1053,32 +936,32 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
                printk(KERN_NOTICE "xdr error! (%s:%d)\n", __FILE__, __LINE__); 
                goto xdr_error;
        }
-       write->wr_vec[0].iov_base = p;
-       write->wr_vec[0].iov_len = avail;
+       argp->rqstp->rq_vec[0].iov_base = p;
+       argp->rqstp->rq_vec[0].iov_len = avail;
        v = 0;
        len = write->wr_buflen;
-       while (len > write->wr_vec[v].iov_len) {
-               len -= write->wr_vec[v].iov_len;
+       while (len > argp->rqstp->rq_vec[v].iov_len) {
+               len -= argp->rqstp->rq_vec[v].iov_len;
                v++;
-               write->wr_vec[v].iov_base = page_address(argp->pagelist[0]);
+               argp->rqstp->rq_vec[v].iov_base = page_address(argp->pagelist[0]);
                argp->pagelist++;
                if (argp->pagelen >= PAGE_SIZE) {
-                       write->wr_vec[v].iov_len = PAGE_SIZE;
+                       argp->rqstp->rq_vec[v].iov_len = PAGE_SIZE;
                        argp->pagelen -= PAGE_SIZE;
                } else {
-                       write->wr_vec[v].iov_len = argp->pagelen;
+                       argp->rqstp->rq_vec[v].iov_len = argp->pagelen;
                        argp->pagelen -= len;
                }
        }
-       argp->end = (u32*) (write->wr_vec[v].iov_base + write->wr_vec[v].iov_len);
-       argp->p = (u32*)  (write->wr_vec[v].iov_base + (XDR_QUADLEN(len) << 2));
-       write->wr_vec[v].iov_len = len;
+       argp->end = (__be32*) (argp->rqstp->rq_vec[v].iov_base + argp->rqstp->rq_vec[v].iov_len);
+       argp->p = (__be32*)  (argp->rqstp->rq_vec[v].iov_base + (XDR_QUADLEN(len) << 2));
+       argp->rqstp->rq_vec[v].iov_len = len;
        write->wr_vlen = v+1;
 
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_release_lockowner *rlockowner)
 {
        DECODE_HEAD;
@@ -1092,7 +975,7 @@ nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_rel
        DECODE_TAIL;
 }
 
-static int
+static __be32
 nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
 {
        DECODE_HEAD;
@@ -1117,7 +1000,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;
@@ -1298,7 +1181,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
  * task to translate them into Linux-specific versions which are more
  * consistent with the style used in NFSv2/v3...
  */
-#define ENCODE_HEAD              u32 *p
+#define ENCODE_HEAD              __be32 *p
 
 #define WRITE32(n)               *p++ = htonl(n)
 #define WRITE64(n)               do {                          \
@@ -1328,22 +1211,21 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
  * Header routine to setup seqid operation replay cache
  */
 #define ENCODE_SEQID_OP_HEAD                                   \
-       u32 *p;                                                 \
-       u32 *save;                                              \
+       __be32 *p;                                              \
+       __be32 *save;                                           \
                                                                \
        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) {         \
-               if (stateowner->so_confirmed)                   \
-                       stateowner->so_seqid++;                 \
+               stateowner->so_seqid++;                         \
                stateowner->so_replay.rp_status = nfserr;       \
                stateowner->so_replay.rp_buflen =               \
                          (((char *)(resp)->p - (char *)save)); \
@@ -1351,6 +1233,120 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                        stateowner->so_replay.rp_buflen);       \
        } } while (0);
 
+/* Encode as an array of strings the string given with components
+ * seperated @sep.
+ */
+static __be32 nfsd4_encode_components(char sep, char *components,
+                                  __be32 **pp, int *buflen)
+{
+       __be32 *p = *pp;
+       __be32 *countp = p;
+       int strlen, count=0;
+       char *str, *end;
+
+       dprintk("nfsd4_encode_components(%s)\n", components);
+       if ((*buflen -= 4) < 0)
+               return nfserr_resource;
+       WRITE32(0); /* We will fill this in with @count later */
+       end = str = components;
+       while (*end) {
+               for (; *end && (*end != sep); end++)
+                       ; /* Point to end of component */
+               strlen = end - str;
+               if (strlen) {
+                       if ((*buflen -= ((XDR_QUADLEN(strlen) << 2) + 4)) < 0)
+                               return nfserr_resource;
+                       WRITE32(strlen);
+                       WRITEMEM(str, strlen);
+                       count++;
+               }
+               else
+                       end++;
+               str = end;
+       }
+       *pp = p;
+       p = countp;
+       WRITE32(count);
+       return 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)
+{
+       __be32 status;
+       __be32 *p = *pp;
+
+       status = nfsd4_encode_components(':', location->hosts, &p, buflen);
+       if (status)
+               return status;
+       status = nfsd4_encode_components('/', location->path, &p, buflen);
+       if (status)
+               return status;
+       *pp = p;
+       return 0;
+}
+
+/*
+ * Return the path to an export point in the pseudo filesystem namespace
+ * Returned string is safe to use as long as the caller holds a reference
+ * to @exp.
+ */
+static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp, __be32 *stat)
+{
+       struct svc_fh tmp_fh;
+       char *path, *rootpath;
+
+       fh_init(&tmp_fh, NFS4_FHSIZE);
+       *stat = exp_pseudoroot(rqstp->rq_client, &tmp_fh, &rqstp->rq_chandle);
+       if (*stat)
+               return NULL;
+       rootpath = tmp_fh.fh_export->ex_path;
+
+       path = exp->ex_path;
+
+       if (strncmp(path, rootpath, strlen(rootpath))) {
+               printk("nfsd: fs_locations failed;"
+                       "%s is not contained in %s\n", path, rootpath);
+               *stat = nfserr_notsupp;
+               return NULL;
+       }
+
+       return path + strlen(rootpath);
+}
+
+/*
+ *  encode a fs_locations structure
+ */
+static __be32 nfsd4_encode_fs_locations(struct svc_rqst *rqstp,
+                                    struct svc_export *exp,
+                                    __be32 **pp, int *buflen)
+{
+       __be32 status;
+       int i;
+       __be32 *p = *pp;
+       struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
+       char *root = nfsd4_path(rqstp, exp, &status);
+
+       if (status)
+               return status;
+       status = nfsd4_encode_components('/', root, &p, buflen);
+       if (status)
+               return status;
+       if ((*buflen -= 4) < 0)
+               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);
+               if (status)
+                       return status;
+       }
+       *pp = p;
+       return 0;
+}
 
 static u32 nfs4_ftypes[16] = {
         NF4BAD,  NF4FIFO, NF4CHR, NF4BAD,
@@ -1359,9 +1355,9 @@ static u32 nfs4_ftypes[16] = {
         NF4SOCK, NF4BAD,  NF4LNK, NF4BAD,
 };
 
-static int
+static __be32
 nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
-                       u32 **p, int *buflen)
+                       __be32 **p, int *buflen)
 {
        int status;
 
@@ -1381,25 +1377,44 @@ nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
        return 0;
 }
 
-static inline int
-nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, u32 **p, int *buflen)
+static inline __be32
+nfsd4_encode_user(struct svc_rqst *rqstp, uid_t uid, __be32 **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)
+static inline __be32
+nfsd4_encode_group(struct svc_rqst *rqstp, uid_t gid, __be32 **p, int *buflen)
 {
        return nfsd4_encode_name(rqstp, NFS4_ACL_WHO_NAMED, gid, 1, p, buflen);
 }
 
-static inline int
+static inline __be32
 nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
-               u32 **p, int *buflen)
+               __be32 **p, int *buflen)
 {
        return nfsd4_encode_name(rqstp, whotype, id, group, p, buflen);
 }
 
+#define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
+                             FATTR4_WORD0_RDATTR_ERROR)
+#define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
+
+static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err)
+{
+       /* As per referral draft:  */
+       if (*bmval0 & ~WORD0_ABSENT_FS_ATTRS ||
+           *bmval1 & ~WORD1_ABSENT_FS_ATTRS) {
+               if (*bmval0 & FATTR4_WORD0_RDATTR_ERROR ||
+                   *bmval0 & FATTR4_WORD0_FS_LOCATIONS)
+                       *rdattr_err = NFSERR_MOVED;
+               else
+                       return nfserr_moved;
+       }
+       *bmval0 &= WORD0_ABSENT_FS_ATTRS;
+       *bmval1 &= WORD1_ABSENT_FS_ATTRS;
+       return 0;
+}
 
 /*
  * Note: @fhp can be NULL; in this case, we might have to compose the filehandle
@@ -1408,9 +1423,9 @@ nfsd4_encode_aclname(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
  * @countp is the buffer size in _words_; upon successful return this becomes
  * replaced with the number of words written.
  */
-int
+__be32
 nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
-               struct dentry *dentry, u32 *buffer, int *countp, u32 *bmval,
+               struct dentry *dentry, __be32 *buffer, int *countp, u32 *bmval,
                struct svc_rqst *rqstp)
 {
        u32 bmval0 = bmval[0];
@@ -1419,11 +1434,13 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        struct svc_fh tempfh;
        struct kstatfs statfs;
        int buflen = *countp << 2;
-       u32 *attrlenp;
+       __be32 *attrlenp;
        u32 dummy;
        u64 dummy64;
-       u32 *p = buffer;
-       int status;
+       u32 rdattr_err = 0;
+       __be32 *p = buffer;
+       __be32 status;
+       int err;
        int aclsupport = 0;
        struct nfs4_acl *acl = NULL;
 
@@ -1431,14 +1448,20 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        BUG_ON(bmval0 & ~NFSD_SUPPORTED_ATTRS_WORD0);
        BUG_ON(bmval1 & ~NFSD_SUPPORTED_ATTRS_WORD1);
 
-       status = vfs_getattr(exp->ex_mnt, dentry, &stat);
-       if (status)
+       if (exp->ex_fslocs.migrated) {
+               status = fattr_handle_absent_fs(&bmval0, &bmval1, &rdattr_err);
+               if (status)
+                       goto out;
+       }
+
+       err = vfs_getattr(exp->ex_mnt, dentry, &stat);
+       if (err)
                goto out_nfserr;
        if ((bmval0 & (FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL)) ||
            (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
                       FATTR4_WORD1_SPACE_TOTAL))) {
-               status = vfs_statfs(dentry->d_inode->i_sb, &statfs);
-               if (status)
+               err = vfs_statfs(dentry, &statfs);
+               if (err)
                        goto out_nfserr;
        }
        if ((bmval0 & (FATTR4_WORD0_FILEHANDLE | FATTR4_WORD0_FSID)) && !fhp) {
@@ -1450,15 +1473,23 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        }
        if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT
                        | FATTR4_WORD0_SUPPORTED_ATTRS)) {
-               status = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
-               aclsupport = (status == 0);
+               err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
+               aclsupport = (err == 0);
                if (bmval0 & FATTR4_WORD0_ACL) {
-                       if (status == -EOPNOTSUPP)
+                       if (err == -EOPNOTSUPP)
                                bmval0 &= ~FATTR4_WORD0_ACL;
-                       else if (status != 0)
+                       else if (err == -EINVAL) {
+                               status = nfserr_attrnotsupp;
+                               goto out;
+                       } else if (err != 0)
                                goto out_nfserr;
                }
        }
+       if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
+               if (exp->ex_fslocs.locations == NULL) {
+                       bmval0 &= ~FATTR4_WORD0_FS_LOCATIONS;
+               }
+       }
        if ((buflen -= 16) < 0)
                goto out_resource;
 
@@ -1468,12 +1499,15 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        attrlenp = p++;                /* to be backfilled later */
 
        if (bmval0 & FATTR4_WORD0_SUPPORTED_ATTRS) {
+               u32 word0 = NFSD_SUPPORTED_ATTRS_WORD0;
                if ((buflen -= 12) < 0)
                        goto out_resource;
+               if (!aclsupport)
+                       word0 &= ~FATTR4_WORD0_ACL;
+               if (!exp->ex_fslocs.locations)
+                       word0 &= ~FATTR4_WORD0_FS_LOCATIONS;
                WRITE32(2);
-               WRITE32(aclsupport ?
-                       NFSD_SUPPORTED_ATTRS_WORD0 :
-                       NFSD_SUPPORTED_ATTRS_WORD0 & ~FATTR4_WORD0_ACL);
+               WRITE32(word0);
                WRITE32(NFSD_SUPPORTED_ATTRS_WORD1);
        }
        if (bmval0 & FATTR4_WORD0_TYPE) {
@@ -1487,7 +1521,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) {
                /*
@@ -1524,7 +1561,10 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        if (bmval0 & FATTR4_WORD0_FSID) {
                if ((buflen -= 16) < 0)
                        goto out_resource;
-               if (is_fsid(fhp, rqstp->rq_reffh)) {
+               if (exp->ex_fslocs.migrated) {
+                       WRITE64(NFS4_REFERRAL_FSID_MAJOR);
+                       WRITE64(NFS4_REFERRAL_FSID_MINOR);
+               } else if (is_fsid(fhp, rqstp->rq_reffh)) {
                        WRITE64((u64)exp->ex_fsid);
                        WRITE64((u64)0);
                } else {
@@ -1547,7 +1587,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) {
                if ((buflen -= 4) < 0)
                        goto out_resource;
-               WRITE32(0);
+               WRITE32(rdattr_err);
        }
        if (bmval0 & FATTR4_WORD0_ACL) {
                struct nfs4_ace *ace;
@@ -1635,6 +1675,13 @@ out_acl:
                        goto out_resource;
                WRITE64((u64) statfs.f_files);
        }
+       if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
+               status = nfsd4_encode_fs_locations(rqstp, exp, &p, &buflen);
+               if (status == nfserr_resource)
+                       goto out_resource;
+               if (status)
+                       goto out;
+       }
        if (bmval0 & FATTR4_WORD0_HOMOGENEOUS) {
                if ((buflen -= 4) < 0)
                        goto out_resource;
@@ -1658,12 +1705,12 @@ out_acl:
        if (bmval0 & FATTR4_WORD0_MAXREAD) {
                if ((buflen -= 8) < 0)
                        goto out_resource;
-               WRITE64((u64) NFSSVC_MAXBLKSIZE);
+               WRITE64((u64) svc_max_payload(rqstp));
        }
        if (bmval0 & FATTR4_WORD0_MAXWRITE) {
                if ((buflen -= 8) < 0)
                        goto out_resource;
-               WRITE64((u64) NFSSVC_MAXBLKSIZE);
+               WRITE64((u64) svc_max_payload(rqstp));
        }
        if (bmval1 & FATTR4_WORD1_MODE) {
                if ((buflen -= 4) < 0)
@@ -1682,8 +1729,8 @@ out_acl:
        }
        if (bmval1 & FATTR4_WORD1_OWNER) {
                status = nfsd4_encode_user(rqstp,
-                       XIDINO_UID(XID_TAG(dentry->d_inode),
-                       stat.uid, stat.xid), &p, &buflen);
+                       TAGINO_UID(DX_TAG(dentry->d_inode),
+                       stat.uid, stat.tag), &p, &buflen);
                if (status == nfserr_resource)
                        goto out_resource;
                if (status)
@@ -1691,8 +1738,8 @@ out_acl:
        }
        if (bmval1 & FATTR4_WORD1_OWNER_GROUP) {
                status = nfsd4_encode_group(rqstp,
-                       XIDINO_GID(XID_TAG(dentry->d_inode),
-                       stat.gid, stat.xid), &p, &buflen);
+                       TAGINO_GID(DX_TAG(dentry->d_inode),
+                       stat.gid, stat.tag), &p, &buflen);
                if (status == nfserr_resource)
                        goto out_resource;
                if (status)
@@ -1778,7 +1825,7 @@ out:
                fh_put(&tempfh);
        return status;
 out_nfserr:
-       status = nfserrno(status);
+       status = nfserrno(err);
        goto out;
 out_resource:
        *countp = 0;
@@ -1789,13 +1836,13 @@ out_serverfault:
        goto out;
 }
 
-static int
+static __be32
 nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
-               const char *name, int namlen, u32 *p, int *buflen)
+               const char *name, int namlen, __be32 *p, int *buflen)
 {
        struct svc_export *exp = cd->rd_fhp->fh_export;
        struct dentry *dentry;
-       int nfserr;
+       __be32 nfserr;
 
        dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen);
        if (IS_ERR(dentry))
@@ -1803,15 +1850,11 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
 
        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;
+               int err;
+
+               err = nfsd_cross_mnt(cd->rd_rqstp, &dentry, &exp);
+               if (err) {
+                       nfserr = nfserrno(err);
                        goto out_put;
                }
 
@@ -1824,10 +1867,10 @@ out_put:
        return nfserr;
 }
 
-static u32 *
-nfsd4_encode_rdattr_error(u32 *p, int buflen, int nfserr)
+static __be32 *
+nfsd4_encode_rdattr_error(__be32 *p, int buflen, __be32 nfserr)
 {
-       u32 *attrlenp;
+       __be32 *attrlenp;
 
        if (buflen < 6)
                return NULL;
@@ -1842,13 +1885,14 @@ nfsd4_encode_rdattr_error(u32 *p, int buflen, int nfserr)
 }
 
 static int
-nfsd4_encode_dirent(struct readdir_cd *ccd, const char *name, int namlen,
-                   loff_t offset, ino_t ino, unsigned int d_type)
+nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
+                   loff_t offset, u64 ino, unsigned int d_type)
 {
+       struct readdir_cd *ccd = ccdv;
        struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
        int buflen;
-       u32 *p = cd->buffer;
-       int nfserr = nfserr_toosmall;
+       __be32 *p = cd->buffer;
+       __be32 nfserr = nfserr_toosmall;
 
        /* In nfsv4, "." and ".." never make it onto the wire.. */
        if (name && isdotent(name, namlen)) {
@@ -1888,10 +1932,11 @@ nfsd4_encode_dirent(struct readdir_cd *ccd, const char *name, int namlen,
                 */
                if (!(cd->rd_bmval[0] & FATTR4_WORD0_RDATTR_ERROR))
                        goto fail;
-               nfserr = nfserr_toosmall;
                p = nfsd4_encode_rdattr_error(p, buflen, nfserr);
-               if (p == NULL)
+               if (p == NULL) {
+                       nfserr = nfserr_toosmall;
                        goto fail;
+               }
        }
        cd->buflen -= (p - cd->buffer);
        cd->buffer = p;
@@ -1903,7 +1948,7 @@ fail:
 }
 
 static void
-nfsd4_encode_access(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_access *access)
+nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access)
 {
        ENCODE_HEAD;
 
@@ -1916,7 +1961,7 @@ nfsd4_encode_access(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_acc
 }
 
 static void
-nfsd4_encode_close(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_close *close)
+nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_close *close)
 {
        ENCODE_SEQID_OP_HEAD;
 
@@ -1931,7 +1976,7 @@ nfsd4_encode_close(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_clos
 
 
 static void
-nfsd4_encode_commit(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_commit *commit)
+nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_commit *commit)
 {
        ENCODE_HEAD;
 
@@ -1943,7 +1988,7 @@ nfsd4_encode_commit(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_com
 }
 
 static void
-nfsd4_encode_create(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_create *create)
+nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_create *create)
 {
        ENCODE_HEAD;
 
@@ -1957,8 +2002,8 @@ nfsd4_encode_create(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_cre
        }
 }
 
-static int
-nfsd4_encode_getattr(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_getattr *getattr)
+static __be32
+nfsd4_encode_getattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_getattr *getattr)
 {
        struct svc_fh *fhp = getattr->ga_fhp;
        int buflen;
@@ -1970,14 +2015,13 @@ nfsd4_encode_getattr(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_ge
        nfserr = nfsd4_encode_fattr(fhp, fhp->fh_export, fhp->fh_dentry,
                                    resp->p, &buflen, getattr->ga_bmval,
                                    resp->rqstp);
-
        if (!nfserr)
                resp->p += buflen;
        return nfserr;
 }
 
 static void
-nfsd4_encode_getfh(struct nfsd4_compoundres *resp, int nfserr, struct svc_fh *fhp)
+nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh *fhp)
 {
        unsigned int len;
        ENCODE_HEAD;
@@ -2017,9 +2061,8 @@ nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denie
 }
 
 static void
-nfsd4_encode_lock(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_lock *lock)
+nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lock *lock)
 {
-
        ENCODE_SEQID_OP_HEAD;
 
        if (!nfserr) {
@@ -2030,18 +2073,18 @@ 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
-nfsd4_encode_lockt(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_lockt *lockt)
+nfsd4_encode_lockt(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lockt *lockt)
 {
        if (nfserr == nfserr_denied)
                nfsd4_encode_lock_denied(resp, &lockt->lt_denied);
 }
 
 static void
-nfsd4_encode_locku(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_locku *locku)
+nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_locku *locku)
 {
        ENCODE_SEQID_OP_HEAD;
 
@@ -2057,7 +2100,7 @@ nfsd4_encode_locku(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_lock
 
 
 static void
-nfsd4_encode_link(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_link *link)
+nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_link *link)
 {
        ENCODE_HEAD;
 
@@ -2070,7 +2113,7 @@ nfsd4_encode_link(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_link
 
 
 static void
-nfsd4_encode_open(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open *open)
+nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open)
 {
        ENCODE_SEQID_OP_HEAD;
 
@@ -2094,7 +2137,7 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open
        case NFS4_OPEN_DELEGATE_READ:
                RESERVE_SPACE(20 + sizeof(stateid_t));
                WRITEMEM(&open->op_delegate_stateid, sizeof(stateid_t));
-               WRITE32(0);
+               WRITE32(open->op_recall);
 
                /*
                 * TODO: ACE's in delegations
@@ -2135,7 +2178,7 @@ out:
 }
 
 static void
-nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open_confirm *oc)
+nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_confirm *oc)
 {
        ENCODE_SEQID_OP_HEAD;
                                        
@@ -2150,7 +2193,7 @@ nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, int nfserr, struct nfs
 }
 
 static void
-nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_open_downgrade *od)
+nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open_downgrade *od)
 {
        ENCODE_SEQID_OP_HEAD;
                                        
@@ -2164,8 +2207,9 @@ nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, int nfserr, struct n
        ENCODE_SEQID_OP_TAIL(od->od_stateowner);
 }
 
-static int
-nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read *read)
+static __be32
+nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
+                 struct nfsd4_read *read)
 {
        u32 eof;
        int v, pn;
@@ -2180,62 +2224,56 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_read
 
        RESERVE_SPACE(8); /* eof flag and byte count */
 
-       maxcount = NFSSVC_MAXBLKSIZE;
+       maxcount = svc_max_payload(resp->rqstp);
        if (maxcount > read->rd_length)
                maxcount = read->rd_length;
 
        len = maxcount;
        v = 0;
        while (len > 0) {
-               pn = resp->rqstp->rq_resused;
-               svc_take_page(resp->rqstp);
-               read->rd_iov[v].iov_base = page_address(resp->rqstp->rq_respages[pn]);
-               read->rd_iov[v].iov_len = len < PAGE_SIZE ? len : PAGE_SIZE;
+               pn = resp->rqstp->rq_resused++;
+               resp->rqstp->rq_vec[v].iov_base =
+                       page_address(resp->rqstp->rq_respages[pn]);
+               resp->rqstp->rq_vec[v].iov_len =
+                       len < PAGE_SIZE ? len : PAGE_SIZE;
                v++;
                len -= PAGE_SIZE;
        }
        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, resp->rqstp->rq_vec, read->rd_vlen,
+                       &maxcount);
+
        if (nfserr == nfserr_symlink)
                nfserr = nfserr_inval;
        if (nfserr)
                return nfserr;
-       eof = (read->rd_offset + maxcount >= read->rd_fhp->fh_dentry->d_inode->i_size);
+       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*)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->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;
 }
 
-static int
-nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_readlink *readlink)
+static __be32
+nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readlink *readlink)
 {
        int maxcount;
        char *page;
@@ -2246,8 +2284,7 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_r
        if (resp->xbuf->page_len)
                return nfserr_resource;
 
-       svc_take_page(resp->rqstp);
-       page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
+       page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]);
 
        maxcount = PAGE_SIZE;
        RESERVE_SPACE(4);
@@ -2266,31 +2303,29 @@ 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->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;
 }
 
-static int
-nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_readdir *readdir)
+static __be32
+nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readdir *readdir)
 {
        int maxcount;
        loff_t offset;
-       u32 *page, *savep;
+       __be32 *page, *savep, *tailbase;
        ENCODE_HEAD;
 
        if (nfserr)
@@ -2306,6 +2341,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)
@@ -2322,8 +2358,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re
                goto err_no_verf;
        }
 
-       svc_take_page(resp->rqstp);
-       page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]);
+       page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]);
        readdir->common.err = 0;
        readdir->buflen = maxcount;
        readdir->buffer = page;
@@ -2348,16 +2383,14 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_re
        p = readdir->buffer;
        *p++ = 0;       /* no more entries */
        *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]);
+       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->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:
@@ -2367,7 +2400,7 @@ err_no_verf:
 }
 
 static void
-nfsd4_encode_remove(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_remove *remove)
+nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_remove *remove)
 {
        ENCODE_HEAD;
 
@@ -2379,7 +2412,7 @@ nfsd4_encode_remove(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_rem
 }
 
 static void
-nfsd4_encode_rename(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_rename *rename)
+nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_rename *rename)
 {
        ENCODE_HEAD;
 
@@ -2396,7 +2429,7 @@ nfsd4_encode_rename(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_ren
  * regardless of the error status.
  */
 static void
-nfsd4_encode_setattr(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_setattr *setattr)
+nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setattr *setattr)
 {
        ENCODE_HEAD;
 
@@ -2415,7 +2448,7 @@ nfsd4_encode_setattr(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_se
 }
 
 static void
-nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_setclientid *scd)
+nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setclientid *scd)
 {
        ENCODE_HEAD;
 
@@ -2434,7 +2467,7 @@ nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, int nfserr, struct nfsd
 }
 
 static void
-nfsd4_encode_write(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_write *write)
+nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_write *write)
 {
        ENCODE_HEAD;
 
@@ -2450,7 +2483,7 @@ nfsd4_encode_write(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_writ
 void
 nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
 {
-       u32 *statp;
+       __be32 *statp;
        ENCODE_HEAD;
 
        RESERVE_SPACE(8);
@@ -2588,7 +2621,7 @@ nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
  */
 
 int
-nfs4svc_encode_voidres(struct svc_rqst *rqstp, u32 *p, void *dummy)
+nfs4svc_encode_voidres(struct svc_rqst *rqstp, __be32 *p, void *dummy)
 {
         return xdr_ressize_check(rqstp, p);
 }
@@ -2599,10 +2632,8 @@ void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args)
                kfree(args->ops);
                args->ops = args->iops;
        }
-       if (args->tmpp) {
-               kfree(args->tmpp);
-               args->tmpp = NULL;
-       }
+       kfree(args->tmpp);
+       args->tmpp = NULL;
        while (args->to_free) {
                struct tmpbuf *tb = args->to_free;
                args->to_free = tb->next;
@@ -2612,9 +2643,9 @@ void nfsd4_release_compoundargs(struct nfsd4_compoundargs *args)
 }
 
 int
-nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoundargs *args)
+nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundargs *args)
 {
-       int status;
+       __be32 status;
 
        args->p = p;
        args->end = rqstp->rq_arg.head[0].iov_base + rqstp->rq_arg.head[0].iov_len;
@@ -2633,7 +2664,7 @@ nfs4svc_decode_compoundargs(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoun
 }
 
 int
-nfs4svc_encode_compoundres(struct svc_rqst *rqstp, u32 *p, struct nfsd4_compoundres *resp)
+nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compoundres *resp)
 {
        /*
         * All that remains is to write the tag and operation count...