io_uring: inline io_req_clean_work()
[linux-2.6-microblaze.git] / fs / nfsd / nfs3xdr.c
index 34b8802..9d9a01c 100644 (file)
@@ -29,8 +29,9 @@ static u32    nfs3_ftypes[] = {
 
 
 /*
- * XDR functions for basic NFS types
+ * Basic NFSv3 data types (RFC 1813 Sections 2.5 and 2.6)
  */
+
 static __be32 *
 encode_time3(__be32 *p, struct timespec64 *time)
 {
@@ -38,32 +39,47 @@ encode_time3(__be32 *p, struct timespec64 *time)
        return p;
 }
 
-static __be32 *
-decode_time3(__be32 *p, struct timespec64 *time)
+static bool
+svcxdr_decode_nfstime3(struct xdr_stream *xdr, struct timespec64 *timep)
 {
-       time->tv_sec = ntohl(*p++);
-       time->tv_nsec = ntohl(*p++);
-       return p;
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, XDR_UNIT * 2);
+       if (!p)
+               return false;
+       timep->tv_sec = be32_to_cpup(p++);
+       timep->tv_nsec = be32_to_cpup(p);
+
+       return true;
 }
 
-static __be32 *
-decode_fh(__be32 *p, struct svc_fh *fhp)
+/**
+ * svcxdr_decode_nfs_fh3 - Decode an NFSv3 file handle
+ * @xdr: XDR stream positioned at an undecoded NFSv3 FH
+ * @fhp: OUT: filled-in server file handle
+ *
+ * Return values:
+ *  %false: The encoded file handle was not valid
+ *  %true: @fhp has been initialized
+ */
+bool
+svcxdr_decode_nfs_fh3(struct xdr_stream *xdr, struct svc_fh *fhp)
 {
-       unsigned int size;
+       __be32 *p;
+       u32 size;
+
+       if (xdr_stream_decode_u32(xdr, &size) < 0)
+               return false;
+       if (size == 0 || size > NFS3_FHSIZE)
+               return false;
+       p = xdr_inline_decode(xdr, size);
+       if (!p)
+               return false;
        fh_init(fhp, NFS3_FHSIZE);
-       size = ntohl(*p++);
-       if (size > NFS3_FHSIZE)
-               return NULL;
-
-       memcpy(&fhp->fh_handle.fh_base, p, size);
        fhp->fh_handle.fh_size = size;
-       return p + XDR_QUADLEN(size);
-}
+       memcpy(&fhp->fh_handle.fh_base, p, size);
 
-/* Helper function for NFSv3 ACL code */
-__be32 *nfs3svc_decode_fh(__be32 *p, struct svc_fh *fhp)
-{
-       return decode_fh(p, fhp);
+       return true;
 }
 
 static __be32 *
@@ -76,69 +92,165 @@ encode_fh(__be32 *p, struct svc_fh *fhp)
        return p + XDR_QUADLEN(size);
 }
 
-/*
- * Decode a file name and make sure that the path contains
- * no slashes or null bytes.
- */
-static __be32 *
-decode_filename(__be32 *p, char **namp, unsigned int *lenp)
+static bool
+svcxdr_decode_filename3(struct xdr_stream *xdr, char **name, unsigned int *len)
 {
-       char            *name;
-       unsigned int    i;
+       u32 size, i;
+       __be32 *p;
+       char *c;
+
+       if (xdr_stream_decode_u32(xdr, &size) < 0)
+               return false;
+       if (size == 0 || size > NFS3_MAXNAMLEN)
+               return false;
+       p = xdr_inline_decode(xdr, size);
+       if (!p)
+               return false;
 
-       if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS3_MAXNAMLEN)) != NULL) {
-               for (i = 0, name = *namp; i < *lenp; i++, name++) {
-                       if (*name == '\0' || *name == '/')
-                               return NULL;
-               }
+       *len = size;
+       *name = (char *)p;
+       for (i = 0, c = *name; i < size; i++, c++) {
+               if (*c == '\0' || *c == '/')
+                       return false;
        }
 
-       return p;
+       return true;
 }
 
-static __be32 *
-decode_sattr3(__be32 *p, struct iattr *iap, struct user_namespace *userns)
+static bool
+svcxdr_decode_diropargs3(struct xdr_stream *xdr, struct svc_fh *fhp,
+                        char **name, unsigned int *len)
+{
+       return svcxdr_decode_nfs_fh3(xdr, fhp) &&
+               svcxdr_decode_filename3(xdr, name, len);
+}
+
+static bool
+svcxdr_decode_sattr3(struct svc_rqst *rqstp, struct xdr_stream *xdr,
+                    struct iattr *iap)
 {
-       u32     tmp;
+       u32 set_it;
 
        iap->ia_valid = 0;
 
-       if (*p++) {
+       if (xdr_stream_decode_bool(xdr, &set_it) < 0)
+               return false;
+       if (set_it) {
+               u32 mode;
+
+               if (xdr_stream_decode_u32(xdr, &mode) < 0)
+                       return false;
                iap->ia_valid |= ATTR_MODE;
-               iap->ia_mode = ntohl(*p++);
+               iap->ia_mode = mode;
        }
-       if (*p++) {
-               iap->ia_uid = make_kuid(userns, ntohl(*p++));
+       if (xdr_stream_decode_bool(xdr, &set_it) < 0)
+               return false;
+       if (set_it) {
+               u32 uid;
+
+               if (xdr_stream_decode_u32(xdr, &uid) < 0)
+                       return false;
+               iap->ia_uid = make_kuid(nfsd_user_namespace(rqstp), uid);
                if (uid_valid(iap->ia_uid))
                        iap->ia_valid |= ATTR_UID;
        }
-       if (*p++) {
-               iap->ia_gid = make_kgid(userns, ntohl(*p++));
+       if (xdr_stream_decode_bool(xdr, &set_it) < 0)
+               return false;
+       if (set_it) {
+               u32 gid;
+
+               if (xdr_stream_decode_u32(xdr, &gid) < 0)
+                       return false;
+               iap->ia_gid = make_kgid(nfsd_user_namespace(rqstp), gid);
                if (gid_valid(iap->ia_gid))
                        iap->ia_valid |= ATTR_GID;
        }
-       if (*p++) {
-               u64     newsize;
+       if (xdr_stream_decode_bool(xdr, &set_it) < 0)
+               return false;
+       if (set_it) {
+               u64 newsize;
 
+               if (xdr_stream_decode_u64(xdr, &newsize) < 0)
+                       return false;
                iap->ia_valid |= ATTR_SIZE;
-               p = xdr_decode_hyper(p, &newsize);
                iap->ia_size = min_t(u64, newsize, NFS_OFFSET_MAX);
        }
-       if ((tmp = ntohl(*p++)) == 1) { /* set to server time */
+       if (xdr_stream_decode_u32(xdr, &set_it) < 0)
+               return false;
+       switch (set_it) {
+       case DONT_CHANGE:
+               break;
+       case SET_TO_SERVER_TIME:
                iap->ia_valid |= ATTR_ATIME;
-       } else if (tmp == 2) {          /* set to client time */
+               break;
+       case SET_TO_CLIENT_TIME:
+               if (!svcxdr_decode_nfstime3(xdr, &iap->ia_atime))
+                       return false;
                iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
-               iap->ia_atime.tv_sec = ntohl(*p++);
-               iap->ia_atime.tv_nsec = ntohl(*p++);
+               break;
+       default:
+               return false;
        }
-       if ((tmp = ntohl(*p++)) == 1) { /* set to server time */
+       if (xdr_stream_decode_u32(xdr, &set_it) < 0)
+               return false;
+       switch (set_it) {
+       case DONT_CHANGE:
+               break;
+       case SET_TO_SERVER_TIME:
                iap->ia_valid |= ATTR_MTIME;
-       } else if (tmp == 2) {          /* set to client time */
+               break;
+       case SET_TO_CLIENT_TIME:
+               if (!svcxdr_decode_nfstime3(xdr, &iap->ia_mtime))
+                       return false;
                iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
-               iap->ia_mtime.tv_sec = ntohl(*p++);
-               iap->ia_mtime.tv_nsec = ntohl(*p++);
+               break;
+       default:
+               return false;
        }
-       return p;
+
+       return true;
+}
+
+static bool
+svcxdr_decode_sattrguard3(struct xdr_stream *xdr, struct nfsd3_sattrargs *args)
+{
+       __be32 *p;
+       u32 check;
+
+       if (xdr_stream_decode_bool(xdr, &check) < 0)
+               return false;
+       if (check) {
+               p = xdr_inline_decode(xdr, XDR_UNIT * 2);
+               if (!p)
+                       return false;
+               args->check_guard = 1;
+               args->guardtime = be32_to_cpup(p);
+       } else
+               args->check_guard = 0;
+
+       return true;
+}
+
+static bool
+svcxdr_decode_specdata3(struct xdr_stream *xdr, struct nfsd3_mknodargs *args)
+{
+       __be32 *p;
+
+       p = xdr_inline_decode(xdr, XDR_UNIT * 2);
+       if (!p)
+               return false;
+       args->major = be32_to_cpup(p++);
+       args->minor = be32_to_cpup(p);
+
+       return true;
+}
+
+static bool
+svcxdr_decode_devicedata3(struct svc_rqst *rqstp, struct xdr_stream *xdr,
+                         struct nfsd3_mknodargs *args)
+{
+       return svcxdr_decode_sattr3(rqstp, xdr, &args->attrs) &&
+               svcxdr_decode_specdata3(xdr, args);
 }
 
 static __be32 *encode_fsid(__be32 *p, struct svc_fh *fhp)
@@ -252,6 +364,11 @@ encode_wcc_data(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp)
        return encode_post_op_attr(rqstp, p, fhp);
 }
 
+static bool fs_supports_change_attribute(struct super_block *sb)
+{
+       return sb->s_flags & SB_I_VERSION || sb->s_export_op->fetch_iversion;
+}
+
 /*
  * Fill in the pre_op attr for the wcc data
  */
@@ -260,24 +377,26 @@ void fill_pre_wcc(struct svc_fh *fhp)
        struct inode    *inode;
        struct kstat    stat;
        bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
-       __be32 err;
 
        if (fhp->fh_no_wcc || fhp->fh_pre_saved)
                return;
        inode = d_inode(fhp->fh_dentry);
-       err = fh_getattr(fhp, &stat);
-       if (err) {
-               /* Grab the times from inode anyway */
-               stat.mtime = inode->i_mtime;
-               stat.ctime = inode->i_ctime;
-               stat.size  = inode->i_size;
+       if (fs_supports_change_attribute(inode->i_sb) || !v4) {
+               __be32 err = fh_getattr(fhp, &stat);
+
+               if (err) {
+                       /* Grab the times from inode anyway */
+                       stat.mtime = inode->i_mtime;
+                       stat.ctime = inode->i_ctime;
+                       stat.size  = inode->i_size;
+               }
+               fhp->fh_pre_mtime = stat.mtime;
+               fhp->fh_pre_ctime = stat.ctime;
+               fhp->fh_pre_size  = stat.size;
        }
        if (v4)
                fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode);
 
-       fhp->fh_pre_mtime = stat.mtime;
-       fhp->fh_pre_ctime = stat.ctime;
-       fhp->fh_pre_size  = stat.size;
        fhp->fh_pre_saved = true;
 }
 
@@ -288,7 +407,6 @@ void fill_post_wcc(struct svc_fh *fhp)
 {
        bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
        struct inode *inode = d_inode(fhp->fh_dentry);
-       __be32 err;
 
        if (fhp->fh_no_wcc)
                return;
@@ -296,12 +414,16 @@ void fill_post_wcc(struct svc_fh *fhp)
        if (fhp->fh_post_saved)
                printk("nfsd: inode locked twice during operation.\n");
 
-       err = fh_getattr(fhp, &fhp->fh_post_attr);
-       if (err) {
-               fhp->fh_post_saved = false;
-               fhp->fh_post_attr.ctime = inode->i_ctime;
-       } else
-               fhp->fh_post_saved = true;
+       fhp->fh_post_saved = true;
+
+       if (fs_supports_change_attribute(inode->i_sb) || !v4) {
+               __be32 err = fh_getattr(fhp, &fhp->fh_post_attr);
+
+               if (err) {
+                       fhp->fh_post_saved = false;
+                       fhp->fh_post_attr.ctime = inode->i_ctime;
+               }
+       }
        if (v4)
                fhp->fh_post_change =
                        nfsd4_change_attribute(&fhp->fh_post_attr, inode);
@@ -312,331 +434,277 @@ void fill_post_wcc(struct svc_fh *fhp)
  */
 
 int
-nfs3svc_decode_fhandle(struct svc_rqst *rqstp, __be32 *p)
+nfs3svc_decode_fhandleargs(struct svc_rqst *rqstp, __be32 *p)
 {
+       struct xdr_stream *xdr = &rqstp->rq_arg_stream;
        struct nfsd_fhandle *args = rqstp->rq_argp;
 
-       p = decode_fh(p, &args->fh);
-       if (!p)
-               return 0;
-       return xdr_argsize_check(rqstp, p);
+       return svcxdr_decode_nfs_fh3(xdr, &args->fh);
 }
 
 int
 nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p)
 {
+       struct xdr_stream *xdr = &rqstp->rq_arg_stream;
        struct nfsd3_sattrargs *args = rqstp->rq_argp;
 
-       p = decode_fh(p, &args->fh);
-       if (!p)
-               return 0;
-       p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
-
-       if ((args->check_guard = ntohl(*p++)) != 0) { 
-               struct timespec64 time;
-               p = decode_time3(p, &time);
-               args->guardtime = time.tv_sec;
-       }
-
-       return xdr_argsize_check(rqstp, p);
+       return svcxdr_decode_nfs_fh3(xdr, &args->fh) &&
+               svcxdr_decode_sattr3(rqstp, xdr, &args->attrs) &&
+               svcxdr_decode_sattrguard3(xdr, args);
 }
 
 int
 nfs3svc_decode_diropargs(struct svc_rqst *rqstp, __be32 *p)
 {
+       struct xdr_stream *xdr = &rqstp->rq_arg_stream;
        struct nfsd3_diropargs *args = rqstp->rq_argp;
 
-       if (!(p = decode_fh(p, &args->fh))
-        || !(p = decode_filename(p, &args->name, &args->len)))
-               return 0;
-
-       return xdr_argsize_check(rqstp, p);
+       return svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len);
 }
 
 int
 nfs3svc_decode_accessargs(struct svc_rqst *rqstp, __be32 *p)
 {
+       struct xdr_stream *xdr = &rqstp->rq_arg_stream;
        struct nfsd3_accessargs *args = rqstp->rq_argp;
 
-       p = decode_fh(p, &args->fh);
-       if (!p)
+       if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
+               return 0;
+       if (xdr_stream_decode_u32(xdr, &args->access) < 0)
                return 0;
-       args->access = ntohl(*p++);
 
-       return xdr_argsize_check(rqstp, p);
+       return 1;
 }
 
 int
 nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p)
 {
+       struct xdr_stream *xdr = &rqstp->rq_arg_stream;
        struct nfsd3_readargs *args = rqstp->rq_argp;
-       unsigned int len;
-       int v;
-       u32 max_blocksize = svc_max_payload(rqstp);
 
-       p = decode_fh(p, &args->fh);
-       if (!p)
+       if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
+               return 0;
+       if (xdr_stream_decode_u64(xdr, &args->offset) < 0)
+               return 0;
+       if (xdr_stream_decode_u32(xdr, &args->count) < 0)
                return 0;
-       p = xdr_decode_hyper(p, &args->offset);
-
-       args->count = ntohl(*p++);
-       len = min(args->count, max_blocksize);
-
-       /* set up the kvec */
-       v=0;
-       while (len > 0) {
-               struct page *p = *(rqstp->rq_next_page++);
 
-               rqstp->rq_vec[v].iov_base = page_address(p);
-               rqstp->rq_vec[v].iov_len = min_t(unsigned int, len, PAGE_SIZE);
-               len -= rqstp->rq_vec[v].iov_len;
-               v++;
-       }
-       args->vlen = v;
-       return xdr_argsize_check(rqstp, p);
+       return 1;
 }
 
 int
 nfs3svc_decode_writeargs(struct svc_rqst *rqstp, __be32 *p)
 {
+       struct xdr_stream *xdr = &rqstp->rq_arg_stream;
        struct nfsd3_writeargs *args = rqstp->rq_argp;
-       unsigned int len, hdr, dlen;
        u32 max_blocksize = svc_max_payload(rqstp);
        struct kvec *head = rqstp->rq_arg.head;
        struct kvec *tail = rqstp->rq_arg.tail;
+       size_t remaining;
 
-       p = decode_fh(p, &args->fh);
-       if (!p)
+       if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
                return 0;
-       p = xdr_decode_hyper(p, &args->offset);
-
-       args->count = ntohl(*p++);
-       args->stable = ntohl(*p++);
-       len = args->len = ntohl(*p++);
-       if ((void *)p > head->iov_base + head->iov_len)
+       if (xdr_stream_decode_u64(xdr, &args->offset) < 0)
                return 0;
-       /*
-        * The count must equal the amount of data passed.
-        */
-       if (args->count != args->len)
+       if (xdr_stream_decode_u32(xdr, &args->count) < 0)
+               return 0;
+       if (xdr_stream_decode_u32(xdr, &args->stable) < 0)
                return 0;
 
-       /*
-        * Check to make sure that we got the right number of
-        * bytes.
-        */
-       hdr = (void*)p - head->iov_base;
-       dlen = head->iov_len + rqstp->rq_arg.page_len + tail->iov_len - hdr;
-       /*
-        * Round the length of the data which was specified up to
-        * the next multiple of XDR units and then compare that
-        * against the length which was actually received.
-        * Note that when RPCSEC/GSS (for example) is used, the
-        * data buffer can be padded so dlen might be larger
-        * than required.  It must never be smaller.
-        */
-       if (dlen < XDR_QUADLEN(len)*4)
+       /* opaque data */
+       if (xdr_stream_decode_u32(xdr, &args->len) < 0)
                return 0;
 
+       /* request sanity */
+       if (args->count != args->len)
+               return 0;
+       remaining = head->iov_len + rqstp->rq_arg.page_len + tail->iov_len;
+       remaining -= xdr_stream_pos(xdr);
+       if (remaining < xdr_align_size(args->len))
+               return 0;
        if (args->count > max_blocksize) {
                args->count = max_blocksize;
-               len = args->len = max_blocksize;
+               args->len = max_blocksize;
        }
 
-       args->first.iov_base = (void *)p;
-       args->first.iov_len = head->iov_len - hdr;
+       args->first.iov_base = xdr->p;
+       args->first.iov_len = head->iov_len - xdr_stream_pos(xdr);
+
        return 1;
 }
 
 int
 nfs3svc_decode_createargs(struct svc_rqst *rqstp, __be32 *p)
 {
+       struct xdr_stream *xdr = &rqstp->rq_arg_stream;
        struct nfsd3_createargs *args = rqstp->rq_argp;
 
-       if (!(p = decode_fh(p, &args->fh))
-        || !(p = decode_filename(p, &args->name, &args->len)))
+       if (!svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len))
                return 0;
-
-       switch (args->createmode = ntohl(*p++)) {
+       if (xdr_stream_decode_u32(xdr, &args->createmode) < 0)
+               return 0;
+       switch (args->createmode) {
        case NFS3_CREATE_UNCHECKED:
        case NFS3_CREATE_GUARDED:
-               p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
-               break;
+               return svcxdr_decode_sattr3(rqstp, xdr, &args->attrs);
        case NFS3_CREATE_EXCLUSIVE:
-               args->verf = p;
-               p += 2;
+               args->verf = xdr_inline_decode(xdr, NFS3_CREATEVERFSIZE);
+               if (!args->verf)
+                       return 0;
                break;
        default:
                return 0;
        }
-
-       return xdr_argsize_check(rqstp, p);
+       return 1;
 }
 
 int
 nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, __be32 *p)
 {
+       struct xdr_stream *xdr = &rqstp->rq_arg_stream;
        struct nfsd3_createargs *args = rqstp->rq_argp;
 
-       if (!(p = decode_fh(p, &args->fh)) ||
-           !(p = decode_filename(p, &args->name, &args->len)))
-               return 0;
-       p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
-
-       return xdr_argsize_check(rqstp, p);
+       return svcxdr_decode_diropargs3(xdr, &args->fh,
+                                       &args->name, &args->len) &&
+               svcxdr_decode_sattr3(rqstp, xdr, &args->attrs);
 }
 
 int
 nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
 {
+       struct xdr_stream *xdr = &rqstp->rq_arg_stream;
        struct nfsd3_symlinkargs *args = rqstp->rq_argp;
-       char *base = (char *)p;
-       size_t dlen;
+       struct kvec *head = rqstp->rq_arg.head;
+       struct kvec *tail = rqstp->rq_arg.tail;
+       size_t remaining;
 
-       if (!(p = decode_fh(p, &args->ffh)) ||
-           !(p = decode_filename(p, &args->fname, &args->flen)))
+       if (!svcxdr_decode_diropargs3(xdr, &args->ffh, &args->fname, &args->flen))
+               return 0;
+       if (!svcxdr_decode_sattr3(rqstp, xdr, &args->attrs))
+               return 0;
+       if (xdr_stream_decode_u32(xdr, &args->tlen) < 0)
                return 0;
-       p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
 
-       args->tlen = ntohl(*p++);
+       /* request sanity */
+       remaining = head->iov_len + rqstp->rq_arg.page_len + tail->iov_len;
+       remaining -= xdr_stream_pos(xdr);
+       if (remaining < xdr_align_size(args->tlen))
+               return 0;
 
-       args->first.iov_base = p;
-       args->first.iov_len = rqstp->rq_arg.head[0].iov_len;
-       args->first.iov_len -= (char *)p - base;
+       args->first.iov_base = xdr->p;
+       args->first.iov_len = head->iov_len - xdr_stream_pos(xdr);
 
-       dlen = args->first.iov_len + rqstp->rq_arg.page_len +
-              rqstp->rq_arg.tail[0].iov_len;
-       if (dlen < XDR_QUADLEN(args->tlen) << 2)
-               return 0;
        return 1;
 }
 
 int
 nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, __be32 *p)
 {
+       struct xdr_stream *xdr = &rqstp->rq_arg_stream;
        struct nfsd3_mknodargs *args = rqstp->rq_argp;
 
-       if (!(p = decode_fh(p, &args->fh))
-        || !(p = decode_filename(p, &args->name, &args->len)))
+       if (!svcxdr_decode_diropargs3(xdr, &args->fh, &args->name, &args->len))
+               return 0;
+       if (xdr_stream_decode_u32(xdr, &args->ftype) < 0)
+               return 0;
+       switch (args->ftype) {
+       case NF3CHR:
+       case NF3BLK:
+               return svcxdr_decode_devicedata3(rqstp, xdr, args);
+       case NF3SOCK:
+       case NF3FIFO:
+               return svcxdr_decode_sattr3(rqstp, xdr, &args->attrs);
+       case NF3REG:
+       case NF3DIR:
+       case NF3LNK:
+               /* Valid XDR but illegal file types */
+               break;
+       default:
                return 0;
-
-       args->ftype = ntohl(*p++);
-
-       if (args->ftype == NF3BLK  || args->ftype == NF3CHR
-        || args->ftype == NF3SOCK || args->ftype == NF3FIFO)
-               p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
-
-       if (args->ftype == NF3BLK || args->ftype == NF3CHR) {
-               args->major = ntohl(*p++);
-               args->minor = ntohl(*p++);
        }
 
-       return xdr_argsize_check(rqstp, p);
+       return 1;
 }
 
 int
 nfs3svc_decode_renameargs(struct svc_rqst *rqstp, __be32 *p)
 {
+       struct xdr_stream *xdr = &rqstp->rq_arg_stream;
        struct nfsd3_renameargs *args = rqstp->rq_argp;
 
-       if (!(p = decode_fh(p, &args->ffh))
-        || !(p = decode_filename(p, &args->fname, &args->flen))
-        || !(p = decode_fh(p, &args->tfh))
-        || !(p = decode_filename(p, &args->tname, &args->tlen)))
-               return 0;
-
-       return xdr_argsize_check(rqstp, p);
-}
-
-int
-nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p)
-{
-       struct nfsd3_readlinkargs *args = rqstp->rq_argp;
-
-       p = decode_fh(p, &args->fh);
-       if (!p)
-               return 0;
-       args->buffer = page_address(*(rqstp->rq_next_page++));
-
-       return xdr_argsize_check(rqstp, p);
+       return svcxdr_decode_diropargs3(xdr, &args->ffh,
+                                       &args->fname, &args->flen) &&
+               svcxdr_decode_diropargs3(xdr, &args->tfh,
+                                        &args->tname, &args->tlen);
 }
 
 int
 nfs3svc_decode_linkargs(struct svc_rqst *rqstp, __be32 *p)
 {
+       struct xdr_stream *xdr = &rqstp->rq_arg_stream;
        struct nfsd3_linkargs *args = rqstp->rq_argp;
 
-       if (!(p = decode_fh(p, &args->ffh))
-        || !(p = decode_fh(p, &args->tfh))
-        || !(p = decode_filename(p, &args->tname, &args->tlen)))
-               return 0;
-
-       return xdr_argsize_check(rqstp, p);
+       return svcxdr_decode_nfs_fh3(xdr, &args->ffh) &&
+               svcxdr_decode_diropargs3(xdr, &args->tfh,
+                                        &args->tname, &args->tlen);
 }
 
 int
 nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p)
 {
+       struct xdr_stream *xdr = &rqstp->rq_arg_stream;
        struct nfsd3_readdirargs *args = rqstp->rq_argp;
-       int len;
-       u32 max_blocksize = svc_max_payload(rqstp);
 
-       p = decode_fh(p, &args->fh);
-       if (!p)
+       if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
+               return 0;
+       if (xdr_stream_decode_u64(xdr, &args->cookie) < 0)
+               return 0;
+       args->verf = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE);
+       if (!args->verf)
+               return 0;
+       if (xdr_stream_decode_u32(xdr, &args->count) < 0)
                return 0;
-       p = xdr_decode_hyper(p, &args->cookie);
-       args->verf   = p; p += 2;
-       args->dircount = ~0;
-       args->count  = ntohl(*p++);
-       len = args->count  = min_t(u32, args->count, max_blocksize);
-
-       while (len > 0) {
-               struct page *p = *(rqstp->rq_next_page++);
-               if (!args->buffer)
-                       args->buffer = page_address(p);
-               len -= PAGE_SIZE;
-       }
 
-       return xdr_argsize_check(rqstp, p);
+       return 1;
 }
 
 int
 nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p)
 {
+       struct xdr_stream *xdr = &rqstp->rq_arg_stream;
        struct nfsd3_readdirargs *args = rqstp->rq_argp;
-       int len;
-       u32 max_blocksize = svc_max_payload(rqstp);
+       u32 dircount;
 
-       p = decode_fh(p, &args->fh);
-       if (!p)
+       if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
+               return 0;
+       if (xdr_stream_decode_u64(xdr, &args->cookie) < 0)
+               return 0;
+       args->verf = xdr_inline_decode(xdr, NFS3_COOKIEVERFSIZE);
+       if (!args->verf)
+               return 0;
+       /* dircount is ignored */
+       if (xdr_stream_decode_u32(xdr, &dircount) < 0)
+               return 0;
+       if (xdr_stream_decode_u32(xdr, &args->count) < 0)
                return 0;
-       p = xdr_decode_hyper(p, &args->cookie);
-       args->verf     = p; p += 2;
-       args->dircount = ntohl(*p++);
-       args->count    = ntohl(*p++);
-
-       len = args->count = min(args->count, max_blocksize);
-       while (len > 0) {
-               struct page *p = *(rqstp->rq_next_page++);
-               if (!args->buffer)
-                       args->buffer = page_address(p);
-               len -= PAGE_SIZE;
-       }
 
-       return xdr_argsize_check(rqstp, p);
+       return 1;
 }
 
 int
 nfs3svc_decode_commitargs(struct svc_rqst *rqstp, __be32 *p)
 {
+       struct xdr_stream *xdr = &rqstp->rq_arg_stream;
        struct nfsd3_commitargs *args = rqstp->rq_argp;
-       p = decode_fh(p, &args->fh);
-       if (!p)
+
+       if (!svcxdr_decode_nfs_fh3(xdr, &args->fh))
+               return 0;
+       if (xdr_stream_decode_u64(xdr, &args->offset) < 0)
+               return 0;
+       if (xdr_stream_decode_u32(xdr, &args->count) < 0)
                return 0;
-       p = xdr_decode_hyper(p, &args->offset);
-       args->count = ntohl(*p++);
 
-       return xdr_argsize_check(rqstp, p);
+       return 1;
 }
 
 /*