NFS: Prevent another deadlock in nfs_release_page()
[safe/jmp/linux-2.6] / fs / nfs / nfs3xdr.c
index 16556fa..5fe5492 100644 (file)
@@ -10,7 +10,6 @@
 #include <linux/time.h>
 #include <linux/mm.h>
 #include <linux/slab.h>
-#include <linux/utsname.h>
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/in.h>
@@ -50,6 +49,7 @@
 
 #define NFS3_sattrargs_sz      (NFS3_fh_sz+NFS3_sattr_sz+3)
 #define NFS3_diropargs_sz      (NFS3_fh_sz+NFS3_filename_sz)
+#define NFS3_removeargs_sz     (NFS3_fh_sz+NFS3_filename_sz)
 #define NFS3_accessargs_sz     (NFS3_fh_sz+1)
 #define NFS3_readlinkargs_sz   (NFS3_fh_sz)
 #define NFS3_readargs_sz       (NFS3_fh_sz+3)
@@ -65,6 +65,7 @@
 
 #define NFS3_attrstat_sz       (1+NFS3_fattr_sz)
 #define NFS3_wccstat_sz                (1+NFS3_wcc_data_sz)
+#define NFS3_removeres_sz      (NFS3_wccstat_sz)
 #define NFS3_lookupres_sz      (1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz))
 #define NFS3_accessres_sz      (1+NFS3_post_op_attr_sz+1)
 #define NFS3_readlinkres_sz    (1+NFS3_post_op_attr_sz+1)
 #define NFS3_commitres_sz      (1+NFS3_wcc_data_sz+2)
 
 #define ACL3_getaclargs_sz     (NFS3_fh_sz+1)
-#define ACL3_setaclargs_sz     (NFS3_fh_sz+1+2*(2+5*3))
-#define ACL3_getaclres_sz      (1+NFS3_post_op_attr_sz+1+2*(2+5*3))
+#define ACL3_setaclargs_sz     (NFS3_fh_sz+1+ \
+                               XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
+#define ACL3_getaclres_sz      (1+NFS3_post_op_attr_sz+1+ \
+                               XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE))
 #define ACL3_setaclres_sz      (1+NFS3_post_op_attr_sz)
 
 /*
  * Map file type to S_IFMT bits
  */
-static struct {
-       unsigned int    mode;
-       unsigned int    nfs2type;
-} nfs_type2fmt[] = {
-      { 0,             NFNON   },
-      { S_IFREG,       NFREG   },
-      { S_IFDIR,       NFDIR   },
-      { S_IFBLK,       NFBLK   },
-      { S_IFCHR,       NFCHR   },
-      { S_IFLNK,       NFLNK   },
-      { S_IFSOCK,      NFSOCK  },
-      { S_IFIFO,       NFFIFO  },
-      { 0,             NFBAD   }
+static const umode_t nfs_type2fmt[] = {
+       [NF3BAD] = 0,
+       [NF3REG] = S_IFREG,
+       [NF3DIR] = S_IFDIR,
+       [NF3BLK] = S_IFBLK,
+       [NF3CHR] = S_IFCHR,
+       [NF3LNK] = S_IFLNK,
+       [NF3SOCK] = S_IFSOCK,
+       [NF3FIFO] = S_IFIFO,
 };
 
 /*
  * Common NFS XDR functions as inlines
  */
-static inline u32 *
-xdr_encode_fhandle(u32 *p, struct nfs_fh *fh)
+static inline __be32 *
+xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fh)
 {
        return xdr_encode_array(p, fh->data, fh->size);
 }
 
-static inline u32 *
-xdr_decode_fhandle(u32 *p, struct nfs_fh *fh)
+static inline __be32 *
+xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
 {
        if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) {
                memcpy(fh->data, p, fh->size);
@@ -124,33 +123,32 @@ xdr_decode_fhandle(u32 *p, struct nfs_fh *fh)
 /*
  * Encode/decode time.
  */
-static inline u32 *
-xdr_encode_time3(u32 *p, struct timespec *timep)
+static inline __be32 *
+xdr_encode_time3(__be32 *p, struct timespec *timep)
 {
        *p++ = htonl(timep->tv_sec);
        *p++ = htonl(timep->tv_nsec);
        return p;
 }
 
-static inline u32 *
-xdr_decode_time3(u32 *p, struct timespec *timep)
+static inline __be32 *
+xdr_decode_time3(__be32 *p, struct timespec *timep)
 {
        timep->tv_sec = ntohl(*p++);
        timep->tv_nsec = ntohl(*p++);
        return p;
 }
 
-static u32 *
-xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
+static __be32 *
+xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr)
 {
        unsigned int    type, major, minor;
-       int             fmode;
+       umode_t         fmode;
 
        type = ntohl(*p++);
-       if (type >= NF3BAD)
-               type = NF3BAD;
-       fmode = nfs_type2fmt[type].mode;
-       fattr->type = nfs_type2fmt[type].nfs2type;
+       if (type > NF3FIFO)
+               type = NF3NON;
+       fmode = nfs_type2fmt[type];
        fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode;
        fattr->nlink = ntohl(*p++);
        fattr->uid = ntohl(*p++);
@@ -173,12 +171,12 @@ xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr)
        p = xdr_decode_time3(p, &fattr->ctime);
 
        /* Update the mode bits */
-       fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3);
+       fattr->valid |= NFS_ATTR_FATTR_V3;
        return p;
 }
 
-static inline u32 *
-xdr_encode_sattr(u32 *p, struct iattr *attr)
+static inline __be32 *
+xdr_encode_sattr(__be32 *p, struct iattr *attr)
 {
        if (attr->ia_valid & ATTR_MODE) {
                *p++ = xdr_one;
@@ -223,26 +221,28 @@ xdr_encode_sattr(u32 *p, struct iattr *attr)
        return p;
 }
 
-static inline u32 *
-xdr_decode_wcc_attr(u32 *p, struct nfs_fattr *fattr)
+static inline __be32 *
+xdr_decode_wcc_attr(__be32 *p, struct nfs_fattr *fattr)
 {
        p = xdr_decode_hyper(p, &fattr->pre_size);
        p = xdr_decode_time3(p, &fattr->pre_mtime);
        p = xdr_decode_time3(p, &fattr->pre_ctime);
-       fattr->valid |= NFS_ATTR_WCC;
+       fattr->valid |= NFS_ATTR_FATTR_PRESIZE
+               | NFS_ATTR_FATTR_PREMTIME
+               | NFS_ATTR_FATTR_PRECTIME;
        return p;
 }
 
-static inline u32 *
-xdr_decode_post_op_attr(u32 *p, struct nfs_fattr *fattr)
+static inline __be32 *
+xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
 {
        if (*p++)
                p = xdr_decode_fattr(p, fattr);
        return p;
 }
 
-static inline u32 *
-xdr_decode_pre_op_attr(u32 *p, struct nfs_fattr *fattr)
+static inline __be32 *
+xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
 {
        if (*p++)
                return xdr_decode_wcc_attr(p, fattr);
@@ -250,8 +250,8 @@ xdr_decode_pre_op_attr(u32 *p, struct nfs_fattr *fattr)
 }
 
 
-static inline u32 *
-xdr_decode_wcc_data(u32 *p, struct nfs_fattr *fattr)
+static inline __be32 *
+xdr_decode_wcc_data(__be32 *p, struct nfs_fattr *fattr)
 {
        p = xdr_decode_pre_op_attr(p, fattr);
        return xdr_decode_post_op_attr(p, fattr);
@@ -265,7 +265,7 @@ xdr_decode_wcc_data(u32 *p, struct nfs_fattr *fattr)
  * Encode file handle argument
  */
 static int
-nfs3_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs_fh *fh)
+nfs3_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh)
 {
        p = xdr_encode_fhandle(p, fh);
        req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
@@ -276,7 +276,7 @@ nfs3_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs_fh *fh)
  * Encode SETATTR arguments
  */
 static int
-nfs3_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs3_sattrargs *args)
+nfs3_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs3_sattrargs *args)
 {
        p = xdr_encode_fhandle(p, args->fh);
        p = xdr_encode_sattr(p, args->sattr);
@@ -291,7 +291,7 @@ nfs3_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs3_sattrargs *args)
  * Encode directory ops argument
  */
 static int
-nfs3_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs3_diropargs *args)
+nfs3_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs3_diropargs *args)
 {
        p = xdr_encode_fhandle(p, args->fh);
        p = xdr_encode_array(p, args->name, args->len);
@@ -300,10 +300,22 @@ nfs3_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs3_diropargs *args)
 }
 
 /*
+ * Encode REMOVE argument
+ */
+static int
+nfs3_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args)
+{
+       p = xdr_encode_fhandle(p, args->fh);
+       p = xdr_encode_array(p, args->name.name, args->name.len);
+       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+       return 0;
+}
+
+/*
  * Encode access() argument
  */
 static int
-nfs3_xdr_accessargs(struct rpc_rqst *req, u32 *p, struct nfs3_accessargs *args)
+nfs3_xdr_accessargs(struct rpc_rqst *req, __be32 *p, struct nfs3_accessargs *args)
 {
        p = xdr_encode_fhandle(p, args->fh);
        *p++ = htonl(args->access);
@@ -317,9 +329,9 @@ nfs3_xdr_accessargs(struct rpc_rqst *req, u32 *p, struct nfs3_accessargs *args)
  * exactly to the page we want to fetch.
  */
 static int
-nfs3_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
+nfs3_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args)
 {
-       struct rpc_auth *auth = req->rq_task->tk_auth;
+       struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
        unsigned int replen;
        u32 count = args->count;
 
@@ -332,6 +344,7 @@ nfs3_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
        replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2;
        xdr_inline_pages(&req->rq_rcv_buf, replen,
                         args->pages, args->pgbase, count);
+       req->rq_rcv_buf.flags |= XDRBUF_READ;
        return 0;
 }
 
@@ -339,7 +352,7 @@ nfs3_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
  * Write arguments. Splice the buffer to be written into the iovec.
  */
 static int
-nfs3_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
+nfs3_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
 {
        struct xdr_buf *sndbuf = &req->rq_snd_buf;
        u32 count = args->count;
@@ -353,6 +366,7 @@ nfs3_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
 
        /* Copy the page array */
        xdr_encode_pages(sndbuf, args->pages, args->pgbase, count);
+       sndbuf->flags |= XDRBUF_WRITE;
        return 0;
 }
 
@@ -360,7 +374,7 @@ nfs3_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
  * Encode CREATE arguments
  */
 static int
-nfs3_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs3_createargs *args)
+nfs3_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs3_createargs *args)
 {
        p = xdr_encode_fhandle(p, args->fh);
        p = xdr_encode_array(p, args->name, args->len);
@@ -380,7 +394,7 @@ nfs3_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs3_createargs *args)
  * Encode MKDIR arguments
  */
 static int
-nfs3_xdr_mkdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_mkdirargs *args)
+nfs3_xdr_mkdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mkdirargs *args)
 {
        p = xdr_encode_fhandle(p, args->fh);
        p = xdr_encode_array(p, args->name, args->len);
@@ -393,7 +407,7 @@ nfs3_xdr_mkdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_mkdirargs *args)
  * Encode SYMLINK arguments
  */
 static int
-nfs3_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_symlinkargs *args)
+nfs3_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_symlinkargs *args)
 {
        p = xdr_encode_fhandle(p, args->fromfh);
        p = xdr_encode_array(p, args->fromname, args->fromlen);
@@ -410,7 +424,7 @@ nfs3_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_symlinkargs *args
  * Encode MKNOD arguments
  */
 static int
-nfs3_xdr_mknodargs(struct rpc_rqst *req, u32 *p, struct nfs3_mknodargs *args)
+nfs3_xdr_mknodargs(struct rpc_rqst *req, __be32 *p, struct nfs3_mknodargs *args)
 {
        p = xdr_encode_fhandle(p, args->fh);
        p = xdr_encode_array(p, args->name, args->len);
@@ -429,7 +443,7 @@ nfs3_xdr_mknodargs(struct rpc_rqst *req, u32 *p, struct nfs3_mknodargs *args)
  * Encode RENAME arguments
  */
 static int
-nfs3_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs3_renameargs *args)
+nfs3_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs3_renameargs *args)
 {
        p = xdr_encode_fhandle(p, args->fromfh);
        p = xdr_encode_array(p, args->fromname, args->fromlen);
@@ -443,7 +457,7 @@ nfs3_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs3_renameargs *args)
  * Encode LINK arguments
  */
 static int
-nfs3_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs3_linkargs *args)
+nfs3_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_linkargs *args)
 {
        p = xdr_encode_fhandle(p, args->fromfh);
        p = xdr_encode_fhandle(p, args->tofh);
@@ -456,9 +470,9 @@ nfs3_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs3_linkargs *args)
  * Encode arguments to readdir call
  */
 static int
-nfs3_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_readdirargs *args)
+nfs3_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirargs *args)
 {
-       struct rpc_auth *auth = req->rq_task->tk_auth;
+       struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
        unsigned int replen;
        u32 count = args->count;
 
@@ -485,21 +499,21 @@ nfs3_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_readdirargs *args
  * We just check for syntactical correctness.
  */
 static int
-nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res)
+nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res)
 {
        struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
        struct kvec *iov = rcvbuf->head;
        struct page **page;
-       int hdrlen, recvd;
-       int status, nr;
-       unsigned int len, pglen;
-       u32 *entry, *end, *kaddr;
+       size_t hdrlen;
+       u32 len, recvd, pglen;
+       int status, nr = 0;
+       __be32 *entry, *end, *kaddr;
 
        status = ntohl(*p++);
        /* Decode post_op_attrs */
        p = xdr_decode_post_op_attr(p, res->dir_attr);
        if (status)
-               return -nfs_stat_to_errno(status);
+               return nfs_stat_to_errno(status);
        /* Decode verifier cookie */
        if (res->verf) {
                res->verf[0] = *p++;
@@ -510,8 +524,8 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res)
 
        hdrlen = (u8 *) p - (u8 *) iov->iov_base;
        if (iov->iov_len < hdrlen) {
-               printk(KERN_WARNING "NFS: READDIR reply header overflowed:"
-                               "length %d > %Zu\n", hdrlen, iov->iov_len);
+               dprintk("NFS: READDIR reply header overflowed:"
+                               "length %Zu > %Zu\n", hdrlen, iov->iov_len);
                return -errno_NFSERR_IO;
        } else if (iov->iov_len != hdrlen) {
                dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
@@ -523,17 +537,22 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res)
        if (pglen > recvd)
                pglen = recvd;
        page = rcvbuf->pages;
-       kaddr = p = (u32 *)kmap_atomic(*page, KM_USER0);
-       end = (u32 *)((char *)p + pglen);
+       kaddr = p = kmap_atomic(*page, KM_USER0);
+       end = (__be32 *)((char *)p + pglen);
        entry = p;
-       for (nr = 0; *p++; nr++) {
+
+       /* Make sure the packet actually has a value_follows and EOF entry */
+       if ((entry + 1) > end)
+               goto short_pkt;
+
+       for (; *p++; nr++) {
                if (p + 3 > end)
                        goto short_pkt;
                p += 2;                         /* inode # */
                len = ntohl(*p++);              /* string length */
                p += XDR_QUADLEN(len) + 2;      /* name + cookie */
                if (len > NFS3_MAXNAMLEN) {
-                       printk(KERN_WARNING "NFS: giant filename in readdir (len %x)!\n",
+                       dprintk("NFS: giant filename in readdir (len 0x%x)!\n",
                                                len);
                        goto err_unmap;
                }
@@ -553,8 +572,8 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res)
                                        goto short_pkt;
                                len = ntohl(*p++);
                                if (len > NFS3_FHSIZE) {
-                                       printk(KERN_WARNING "NFS: giant filehandle in "
-                                               "readdir (len %x)!\n", len);
+                                       dprintk("NFS: giant filehandle in "
+                                               "readdir (len 0x%x)!\n", len);
                                        goto err_unmap;
                                }
                                p += XDR_QUADLEN(len);
@@ -565,26 +584,40 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res)
                        goto short_pkt;
                entry = p;
        }
-       if (!nr && (entry[0] != 0 || entry[1] == 0))
-               goto short_pkt;
+
+       /*
+        * Apparently some server sends responses that are a valid size, but
+        * contain no entries, and have value_follows==0 and EOF==0. For
+        * those, just set the EOF marker.
+        */
+       if (!nr && entry[1] == 0) {
+               dprintk("NFS: readdir reply truncated!\n");
+               entry[1] = 1;
+       }
  out:
        kunmap_atomic(kaddr, KM_USER0);
        return nr;
  short_pkt:
+       /*
+        * When we get a short packet there are 2 possibilities. We can
+        * return an error, or fix up the response to look like a valid
+        * response and return what we have so far. If there are no
+        * entries and the packet was short, then return -EIO. If there
+        * are valid entries in the response, return them and pretend that
+        * the call was successful, but incomplete. The caller can retry the
+        * readdir starting at the last cookie.
+        */
        entry[0] = entry[1] = 0;
-       /* truncate listing ? */
-       if (!nr) {
-               printk(KERN_NOTICE "NFS: readdir reply truncated!\n");
-               entry[1] = 1;
-       }
+       if (!nr)
+               nr = -errno_NFSERR_IO;
        goto out;
 err_unmap:
        nr = -errno_NFSERR_IO;
        goto out;
 }
 
-u32 *
-nfs3_decode_dirent(u32 *p, struct nfs_entry *entry, int plus)
+__be32 *
+nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
 {
        struct nfs_entry old = *entry;
 
@@ -626,7 +659,7 @@ nfs3_decode_dirent(u32 *p, struct nfs_entry *entry, int plus)
  * Encode COMMIT arguments
  */
 static int
-nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
+nfs3_xdr_commitargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args)
 {
        p = xdr_encode_fhandle(p, args->fh);
        p = xdr_encode_hyper(p, args->offset);
@@ -640,10 +673,10 @@ nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
  * Encode GETACL arguments
  */
 static int
-nfs3_xdr_getaclargs(struct rpc_rqst *req, u32 *p,
+nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p,
                    struct nfs3_getaclargs *args)
 {
-       struct rpc_auth *auth = req->rq_task->tk_auth;
+       struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
        unsigned int replen;
 
        p = xdr_encode_fhandle(p, args->fh);
@@ -664,32 +697,23 @@ nfs3_xdr_getaclargs(struct rpc_rqst *req, u32 *p,
  * Encode SETACL arguments
  */
 static int
-nfs3_xdr_setaclargs(struct rpc_rqst *req, u32 *p,
+nfs3_xdr_setaclargs(struct rpc_rqst *req, __be32 *p,
                    struct nfs3_setaclargs *args)
 {
        struct xdr_buf *buf = &req->rq_snd_buf;
-       unsigned int base, len_in_head, len = nfsacl_size(
-               (args->mask & NFS_ACL)   ? args->acl_access  : NULL,
-               (args->mask & NFS_DFACL) ? args->acl_default : NULL);
-       int count, err;
+       unsigned int base;
+       int err;
 
        p = xdr_encode_fhandle(p, NFS_FH(args->inode));
        *p++ = htonl(args->mask);
-       base = (char *)p - (char *)buf->head->iov_base;
-       /* put as much of the acls into head as possible. */
-       len_in_head = min_t(unsigned int, buf->head->iov_len - base, len);
-       len -= len_in_head;
-       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p + (len_in_head >> 2));
-
-       for (count = 0; (count << PAGE_SHIFT) < len; count++) {
-               args->pages[count] = alloc_page(GFP_KERNEL);
-               if (!args->pages[count]) {
-                       while (count)
-                               __free_page(args->pages[--count]);
-                       return -ENOMEM;
-               }
-       }
-       xdr_encode_pages(buf, args->pages, 0, len);
+       req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+       base = req->rq_slen;
+
+       if (args->npages != 0)
+               xdr_encode_pages(buf, args->pages, 0, args->len);
+       else
+               req->rq_slen = xdr_adjust_iovec(req->rq_svec,
+                               p + XDR_QUADLEN(args->len));
 
        err = nfsacl_encode(buf, base, args->inode,
                            (args->mask & NFS_ACL) ?
@@ -711,12 +735,12 @@ nfs3_xdr_setaclargs(struct rpc_rqst *req, u32 *p,
  * Decode attrstat reply.
  */
 static int
-nfs3_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+nfs3_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
 {
        int     status;
 
        if ((status = ntohl(*p++)))
-               return -nfs_stat_to_errno(status);
+               return nfs_stat_to_errno(status);
        xdr_decode_fattr(p, fattr);
        return 0;
 }
@@ -726,26 +750,32 @@ nfs3_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
  * SATTR, REMOVE, RMDIR
  */
 static int
-nfs3_xdr_wccstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+nfs3_xdr_wccstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
 {
        int     status;
 
        if ((status = ntohl(*p++)))
-               status = -nfs_stat_to_errno(status);
+               status = nfs_stat_to_errno(status);
        xdr_decode_wcc_data(p, fattr);
        return status;
 }
 
+static int
+nfs3_xdr_removeres(struct rpc_rqst *req, __be32 *p, struct nfs_removeres *res)
+{
+       return nfs3_xdr_wccstat(req, p, &res->dir_attr);
+}
+
 /*
  * Decode LOOKUP reply
  */
 static int
-nfs3_xdr_lookupres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
+nfs3_xdr_lookupres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
 {
        int     status;
 
        if ((status = ntohl(*p++))) {
-               status = -nfs_stat_to_errno(status);
+               status = nfs_stat_to_errno(status);
        } else {
                if (!(p = xdr_decode_fhandle(p, res->fh)))
                        return -errno_NFSERR_IO;
@@ -759,21 +789,21 @@ nfs3_xdr_lookupres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
  * Decode ACCESS reply
  */
 static int
-nfs3_xdr_accessres(struct rpc_rqst *req, u32 *p, struct nfs3_accessres *res)
+nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res)
 {
        int     status = ntohl(*p++);
 
        p = xdr_decode_post_op_attr(p, res->fattr);
        if (status)
-               return -nfs_stat_to_errno(status);
+               return nfs_stat_to_errno(status);
        res->access = ntohl(*p++);
        return 0;
 }
 
 static int
-nfs3_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkargs *args)
+nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args)
 {
-       struct rpc_auth *auth = req->rq_task->tk_auth;
+       struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
        unsigned int replen;
 
        p = xdr_encode_fhandle(p, args->fh);
@@ -789,11 +819,12 @@ nfs3_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkargs *ar
  * Decode READLINK reply
  */
 static int
-nfs3_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+nfs3_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
 {
        struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
        struct kvec *iov = rcvbuf->head;
-       int hdrlen, len, recvd;
+       size_t hdrlen;
+       u32 len, recvd;
        char    *kaddr;
        int     status;
 
@@ -801,27 +832,28 @@ nfs3_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
        p = xdr_decode_post_op_attr(p, fattr);
 
        if (status != 0)
-               return -nfs_stat_to_errno(status);
+               return nfs_stat_to_errno(status);
 
        /* Convert length of symlink */
        len = ntohl(*p++);
-       if (len >= rcvbuf->page_len || len <= 0) {
-               dprintk(KERN_WARNING "nfs: server returned giant symlink!\n");
+       if (len >= rcvbuf->page_len) {
+               dprintk("nfs: server returned giant symlink!\n");
                return -ENAMETOOLONG;
        }
 
        hdrlen = (u8 *) p - (u8 *) iov->iov_base;
        if (iov->iov_len < hdrlen) {
-               printk(KERN_WARNING "NFS: READLINK reply header overflowed:"
-                               "length %d > %Zu\n", hdrlen, iov->iov_len);
+               dprintk("NFS: READLINK reply header overflowed:"
+                               "length %Zu > %Zu\n", hdrlen, iov->iov_len);
                return -errno_NFSERR_IO;
        } else if (iov->iov_len != hdrlen) {
-               dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
+               dprintk("NFS: READLINK header is short. "
+                       "iovec will be shifted.\n");
                xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
        }
        recvd = req->rq_rcv_buf.len - hdrlen;
        if (recvd < len) {
-               printk(KERN_WARNING "NFS: server cheating in readlink reply: "
+               dprintk("NFS: server cheating in readlink reply: "
                                "count %u > recvd %u\n", len, recvd);
                return -EIO;
        }
@@ -837,18 +869,20 @@ nfs3_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
  * Decode READ reply
  */
 static int
-nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
+nfs3_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res)
 {
        struct kvec *iov = req->rq_rcv_buf.head;
-       int     status, count, ocount, recvd, hdrlen;
+       size_t hdrlen;
+       u32 count, ocount, recvd;
+       int status;
 
        status = ntohl(*p++);
        p = xdr_decode_post_op_attr(p, res->fattr);
 
        if (status != 0)
-               return -nfs_stat_to_errno(status);
+               return nfs_stat_to_errno(status);
 
-       /* Decode reply could and EOF flag. NFSv3 is somewhat redundant
+       /* Decode reply count and EOF flag. NFSv3 is somewhat redundant
         * in that it puts the count both in the res struct and in the
         * opaque data count. */
        count    = ntohl(*p++);
@@ -856,14 +890,14 @@ nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
        ocount   = ntohl(*p++);
 
        if (ocount != count) {
-               printk(KERN_WARNING "NFS: READ count doesn't match RPC opaque count.\n");
+               dprintk("NFS: READ count doesn't match RPC opaque count.\n");
                return -errno_NFSERR_IO;
        }
 
        hdrlen = (u8 *) p - (u8 *) iov->iov_base;
        if (iov->iov_len < hdrlen) {
-               printk(KERN_WARNING "NFS: READ reply header overflowed:"
-                               "length %d > %Zu\n", hdrlen, iov->iov_len);
+               dprintk("NFS: READ reply header overflowed:"
+                               "length %Zu > %Zu\n", hdrlen, iov->iov_len);
                        return -errno_NFSERR_IO;
        } else if (iov->iov_len != hdrlen) {
                dprintk("NFS: READ header is short. iovec will be shifted.\n");
@@ -872,8 +906,8 @@ nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
 
        recvd = req->rq_rcv_buf.len - hdrlen;
        if (count > recvd) {
-               printk(KERN_WARNING "NFS: server cheating in read reply: "
-                       "count %d > recvd %d\n", count, recvd);
+               dprintk("NFS: server cheating in read reply: "
+                       "count %u > recvd %u\n", count, recvd);
                count = recvd;
                res->eof = 0;
        }
@@ -888,7 +922,7 @@ nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
  * Decode WRITE response
  */
 static int
-nfs3_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
+nfs3_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
 {
        int     status;
 
@@ -896,7 +930,7 @@ nfs3_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
        p = xdr_decode_wcc_data(p, res->fattr);
 
        if (status != 0)
-               return -nfs_stat_to_errno(status);
+               return nfs_stat_to_errno(status);
 
        res->count = ntohl(*p++);
        res->verf->committed = (enum nfs3_stable_how)ntohl(*p++);
@@ -910,7 +944,7 @@ nfs3_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
  * Decode a CREATE response
  */
 static int
-nfs3_xdr_createres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
+nfs3_xdr_createres(struct rpc_rqst *req, __be32 *p, struct nfs3_diropres *res)
 {
        int     status;
 
@@ -927,7 +961,7 @@ nfs3_xdr_createres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
                        res->fattr->valid = 0;
                }
        } else {
-               status = -nfs_stat_to_errno(status);
+               status = nfs_stat_to_errno(status);
        }
        p = xdr_decode_wcc_data(p, res->dir_attr);
        return status;
@@ -937,12 +971,12 @@ nfs3_xdr_createres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res)
  * Decode RENAME reply
  */
 static int
-nfs3_xdr_renameres(struct rpc_rqst *req, u32 *p, struct nfs3_renameres *res)
+nfs3_xdr_renameres(struct rpc_rqst *req, __be32 *p, struct nfs3_renameres *res)
 {
        int     status;
 
        if ((status = ntohl(*p++)) != 0)
-               status = -nfs_stat_to_errno(status);
+               status = nfs_stat_to_errno(status);
        p = xdr_decode_wcc_data(p, res->fromattr);
        p = xdr_decode_wcc_data(p, res->toattr);
        return status;
@@ -952,12 +986,12 @@ nfs3_xdr_renameres(struct rpc_rqst *req, u32 *p, struct nfs3_renameres *res)
  * Decode LINK reply
  */
 static int
-nfs3_xdr_linkres(struct rpc_rqst *req, u32 *p, struct nfs3_linkres *res)
+nfs3_xdr_linkres(struct rpc_rqst *req, __be32 *p, struct nfs3_linkres *res)
 {
        int     status;
 
        if ((status = ntohl(*p++)) != 0)
-               status = -nfs_stat_to_errno(status);
+               status = nfs_stat_to_errno(status);
        p = xdr_decode_post_op_attr(p, res->fattr);
        p = xdr_decode_wcc_data(p, res->dir_attr);
        return status;
@@ -967,7 +1001,7 @@ nfs3_xdr_linkres(struct rpc_rqst *req, u32 *p, struct nfs3_linkres *res)
  * Decode FSSTAT reply
  */
 static int
-nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsstat *res)
+nfs3_xdr_fsstatres(struct rpc_rqst *req, __be32 *p, struct nfs_fsstat *res)
 {
        int             status;
 
@@ -975,7 +1009,7 @@ nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsstat *res)
 
        p = xdr_decode_post_op_attr(p, res->fattr);
        if (status != 0)
-               return -nfs_stat_to_errno(status);
+               return nfs_stat_to_errno(status);
 
        p = xdr_decode_hyper(p, &res->tbytes);
        p = xdr_decode_hyper(p, &res->fbytes);
@@ -992,7 +1026,7 @@ nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsstat *res)
  * Decode FSINFO reply
  */
 static int
-nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
+nfs3_xdr_fsinfores(struct rpc_rqst *req, __be32 *p, struct nfs_fsinfo *res)
 {
        int             status;
 
@@ -1000,7 +1034,7 @@ nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
 
        p = xdr_decode_post_op_attr(p, res->fattr);
        if (status != 0)
-               return -nfs_stat_to_errno(status);
+               return nfs_stat_to_errno(status);
 
        res->rtmax  = ntohl(*p++);
        res->rtpref = ntohl(*p++);
@@ -1020,7 +1054,7 @@ nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res)
  * Decode PATHCONF reply
  */
 static int
-nfs3_xdr_pathconfres(struct rpc_rqst *req, u32 *p, struct nfs_pathconf *res)
+nfs3_xdr_pathconfres(struct rpc_rqst *req, __be32 *p, struct nfs_pathconf *res)
 {
        int             status;
 
@@ -1028,7 +1062,7 @@ nfs3_xdr_pathconfres(struct rpc_rqst *req, u32 *p, struct nfs_pathconf *res)
 
        p = xdr_decode_post_op_attr(p, res->fattr);
        if (status != 0)
-               return -nfs_stat_to_errno(status);
+               return nfs_stat_to_errno(status);
        res->max_link = ntohl(*p++);
        res->max_namelen = ntohl(*p++);
 
@@ -1040,14 +1074,14 @@ nfs3_xdr_pathconfres(struct rpc_rqst *req, u32 *p, struct nfs_pathconf *res)
  * Decode COMMIT reply
  */
 static int
-nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
+nfs3_xdr_commitres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res)
 {
        int             status;
 
        status = ntohl(*p++);
        p = xdr_decode_wcc_data(p, res->fattr);
        if (status != 0)
-               return -nfs_stat_to_errno(status);
+               return nfs_stat_to_errno(status);
 
        res->verf->verifier[0] = *p++;
        res->verf->verifier[1] = *p++;
@@ -1059,7 +1093,7 @@ nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res)
  * Decode GETACL reply
  */
 static int
-nfs3_xdr_getaclres(struct rpc_rqst *req, u32 *p,
+nfs3_xdr_getaclres(struct rpc_rqst *req, __be32 *p,
                   struct nfs3_getaclres *res)
 {
        struct xdr_buf *buf = &req->rq_rcv_buf;
@@ -1069,7 +1103,7 @@ nfs3_xdr_getaclres(struct rpc_rqst *req, u32 *p,
        int err, base;
 
        if (status != 0)
-               return -nfs_stat_to_errno(status);
+               return nfs_stat_to_errno(status);
        p = xdr_decode_post_op_attr(p, res->fattr);
        res->mask = ntohl(*p++);
        if (res->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
@@ -1091,27 +1125,24 @@ nfs3_xdr_getaclres(struct rpc_rqst *req, u32 *p,
  * Decode setacl reply.
  */
 static int
-nfs3_xdr_setaclres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr)
+nfs3_xdr_setaclres(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr)
 {
        int status = ntohl(*p++);
 
        if (status)
-               return -nfs_stat_to_errno(status);
+               return nfs_stat_to_errno(status);
        xdr_decode_post_op_attr(p, fattr);
        return 0;
 }
 #endif  /* CONFIG_NFS_V3_ACL */
 
-#ifndef MAX
-# define MAX(a, b)     (((a) > (b))? (a) : (b))
-#endif
-
 #define PROC(proc, argtype, restype, timer)                            \
 [NFS3PROC_##proc] = {                                                  \
        .p_proc      = NFS3PROC_##proc,                                 \
        .p_encode    = (kxdrproc_t) nfs3_xdr_##argtype,                 \
        .p_decode    = (kxdrproc_t) nfs3_xdr_##restype,                 \
-       .p_bufsiz    = MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2,       \
+       .p_arglen    = NFS3_##argtype##_sz,                             \
+       .p_replen    = NFS3_##restype##_sz,                             \
        .p_timer     = timer,                                           \
        .p_statidx   = NFS3PROC_##proc,                                 \
        .p_name      = #proc,                                           \
@@ -1129,7 +1160,7 @@ struct rpc_procinfo       nfs3_procedures[] = {
   PROC(MKDIR,          mkdirargs,      createres, 0),
   PROC(SYMLINK,                symlinkargs,    createres, 0),
   PROC(MKNOD,          mknodargs,      createres, 0),
-  PROC(REMOVE,         diropargs,      wccstat, 0),
+  PROC(REMOVE,         removeargs,     removeres, 0),
   PROC(RMDIR,          diropargs,      wccstat, 0),
   PROC(RENAME,         renameargs,     renameres, 0),
   PROC(LINK,           linkargs,       linkres, 0),
@@ -1153,7 +1184,8 @@ static struct rpc_procinfo        nfs3_acl_procedures[] = {
                .p_proc = ACLPROC3_GETACL,
                .p_encode = (kxdrproc_t) nfs3_xdr_getaclargs,
                .p_decode = (kxdrproc_t) nfs3_xdr_getaclres,
-               .p_bufsiz = MAX(ACL3_getaclargs_sz, ACL3_getaclres_sz) << 2,
+               .p_arglen = ACL3_getaclargs_sz,
+               .p_replen = ACL3_getaclres_sz,
                .p_timer = 1,
                .p_name = "GETACL",
        },
@@ -1161,7 +1193,8 @@ static struct rpc_procinfo        nfs3_acl_procedures[] = {
                .p_proc = ACLPROC3_SETACL,
                .p_encode = (kxdrproc_t) nfs3_xdr_setaclargs,
                .p_decode = (kxdrproc_t) nfs3_xdr_setaclres,
-               .p_bufsiz = MAX(ACL3_setaclargs_sz, ACL3_setaclres_sz) << 2,
+               .p_arglen = ACL3_setaclargs_sz,
+               .p_replen = ACL3_setaclres_sz,
                .p_timer = 0,
                .p_name = "SETACL",
        },