Merge tag 'hwmon-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck...
[linux-drm-fsl-dcu.git] / fs / nfsd / nfs4xdr.c
index d9454fe5653f70af2175432d22e36cebcbaef5be..ee7237f99f54cd413dba6375dbc344084d0ece56 100644 (file)
@@ -141,8 +141,8 @@ xdr_error:                                  \
 
 static void next_decode_page(struct nfsd4_compoundargs *argp)
 {
-       argp->pagelist++;
        argp->p = page_address(argp->pagelist[0]);
+       argp->pagelist++;
        if (argp->pagelen < PAGE_SIZE) {
                argp->end = argp->p + (argp->pagelen>>2);
                argp->pagelen = 0;
@@ -411,6 +411,7 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
                label->data = kzalloc(dummy32 + 1, GFP_KERNEL);
                if (!label->data)
                        return nfserr_jukebox;
+               label->len = dummy32;
                defer_free(argp, kfree, label->data);
                memcpy(label->data, buf, dummy32);
        }
@@ -945,13 +946,16 @@ static __be32
 nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_confirm *open_conf)
 {
        DECODE_HEAD;
-                   
+
+       if (argp->minorversion >= 1)
+               return nfserr_notsupp;
+
        status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid);
        if (status)
                return status;
        READ_BUF(4);
        READ32(open_conf->oc_seqid);
-                                                       
+
        DECODE_TAIL;
 }
 
@@ -990,6 +994,14 @@ nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
        DECODE_TAIL;
 }
 
+static __be32
+nfsd4_decode_putpubfh(struct nfsd4_compoundargs *argp, void *p)
+{
+       if (argp->minorversion == 0)
+               return nfs_ok;
+       return nfserr_notsupp;
+}
+
 static __be32
 nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read)
 {
@@ -1061,6 +1073,9 @@ nfsd4_decode_renew(struct nfsd4_compoundargs *argp, clientid_t *clientid)
 {
        DECODE_HEAD;
 
+       if (argp->minorversion >= 1)
+               return nfserr_notsupp;
+
        READ_BUF(sizeof(clientid_t));
        COPYMEM(clientid, sizeof(clientid_t));
 
@@ -1111,6 +1126,9 @@ nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclient
 {
        DECODE_HEAD;
 
+       if (argp->minorversion >= 1)
+               return nfserr_notsupp;
+
        READ_BUF(NFS4_VERIFIER_SIZE);
        COPYMEM(setclientid->se_verf.data, NFS4_VERIFIER_SIZE);
 
@@ -1137,6 +1155,9 @@ nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_s
 {
        DECODE_HEAD;
 
+       if (argp->minorversion >= 1)
+               return nfserr_notsupp;
+
        READ_BUF(8 + NFS4_VERIFIER_SIZE);
        COPYMEM(&scd_c->sc_clientid, 8);
        COPYMEM(&scd_c->sc_confirm, NFS4_VERIFIER_SIZE);
@@ -1208,6 +1229,7 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write)
                len -= pages * PAGE_SIZE;
 
                argp->p = (__be32 *)page_address(argp->pagelist[0]);
+               argp->pagelist++;
                argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE);
        }
        argp->p += XDR_QUADLEN(len);
@@ -1220,6 +1242,9 @@ nfsd4_decode_release_lockowner(struct nfsd4_compoundargs *argp, struct nfsd4_rel
 {
        DECODE_HEAD;
 
+       if (argp->minorversion >= 1)
+               return nfserr_notsupp;
+
        READ_BUF(12);
        COPYMEM(&rlockowner->rl_clientid, sizeof(clientid_t));
        READ32(rlockowner->rl_owner.len);
@@ -1519,7 +1544,7 @@ static nfsd4_dec nfsd4_dec_ops[] = {
        [OP_OPEN_CONFIRM]       = (nfsd4_dec)nfsd4_decode_open_confirm,
        [OP_OPEN_DOWNGRADE]     = (nfsd4_dec)nfsd4_decode_open_downgrade,
        [OP_PUTFH]              = (nfsd4_dec)nfsd4_decode_putfh,
-       [OP_PUTPUBFH]           = (nfsd4_dec)nfsd4_decode_noop,
+       [OP_PUTPUBFH]           = (nfsd4_dec)nfsd4_decode_putpubfh,
        [OP_PUTROOTFH]          = (nfsd4_dec)nfsd4_decode_noop,
        [OP_READ]               = (nfsd4_dec)nfsd4_decode_read,
        [OP_READDIR]            = (nfsd4_dec)nfsd4_decode_readdir,
@@ -1536,46 +1561,6 @@ static nfsd4_dec nfsd4_dec_ops[] = {
        [OP_VERIFY]             = (nfsd4_dec)nfsd4_decode_verify,
        [OP_WRITE]              = (nfsd4_dec)nfsd4_decode_write,
        [OP_RELEASE_LOCKOWNER]  = (nfsd4_dec)nfsd4_decode_release_lockowner,
-};
-
-static nfsd4_dec nfsd41_dec_ops[] = {
-       [OP_ACCESS]             = (nfsd4_dec)nfsd4_decode_access,
-       [OP_CLOSE]              = (nfsd4_dec)nfsd4_decode_close,
-       [OP_COMMIT]             = (nfsd4_dec)nfsd4_decode_commit,
-       [OP_CREATE]             = (nfsd4_dec)nfsd4_decode_create,
-       [OP_DELEGPURGE]         = (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_DELEGRETURN]        = (nfsd4_dec)nfsd4_decode_delegreturn,
-       [OP_GETATTR]            = (nfsd4_dec)nfsd4_decode_getattr,
-       [OP_GETFH]              = (nfsd4_dec)nfsd4_decode_noop,
-       [OP_LINK]               = (nfsd4_dec)nfsd4_decode_link,
-       [OP_LOCK]               = (nfsd4_dec)nfsd4_decode_lock,
-       [OP_LOCKT]              = (nfsd4_dec)nfsd4_decode_lockt,
-       [OP_LOCKU]              = (nfsd4_dec)nfsd4_decode_locku,
-       [OP_LOOKUP]             = (nfsd4_dec)nfsd4_decode_lookup,
-       [OP_LOOKUPP]            = (nfsd4_dec)nfsd4_decode_noop,
-       [OP_NVERIFY]            = (nfsd4_dec)nfsd4_decode_verify,
-       [OP_OPEN]               = (nfsd4_dec)nfsd4_decode_open,
-       [OP_OPENATTR]           = (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_OPEN_CONFIRM]       = (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_OPEN_DOWNGRADE]     = (nfsd4_dec)nfsd4_decode_open_downgrade,
-       [OP_PUTFH]              = (nfsd4_dec)nfsd4_decode_putfh,
-       [OP_PUTPUBFH]           = (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_PUTROOTFH]          = (nfsd4_dec)nfsd4_decode_noop,
-       [OP_READ]               = (nfsd4_dec)nfsd4_decode_read,
-       [OP_READDIR]            = (nfsd4_dec)nfsd4_decode_readdir,
-       [OP_READLINK]           = (nfsd4_dec)nfsd4_decode_noop,
-       [OP_REMOVE]             = (nfsd4_dec)nfsd4_decode_remove,
-       [OP_RENAME]             = (nfsd4_dec)nfsd4_decode_rename,
-       [OP_RENEW]              = (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_RESTOREFH]          = (nfsd4_dec)nfsd4_decode_noop,
-       [OP_SAVEFH]             = (nfsd4_dec)nfsd4_decode_noop,
-       [OP_SECINFO]            = (nfsd4_dec)nfsd4_decode_secinfo,
-       [OP_SETATTR]            = (nfsd4_dec)nfsd4_decode_setattr,
-       [OP_SETCLIENTID]        = (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_SETCLIENTID_CONFIRM]= (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_VERIFY]             = (nfsd4_dec)nfsd4_decode_verify,
-       [OP_WRITE]              = (nfsd4_dec)nfsd4_decode_write,
-       [OP_RELEASE_LOCKOWNER]  = (nfsd4_dec)nfsd4_decode_notsupp,
 
        /* new operations for NFSv4.1 */
        [OP_BACKCHANNEL_CTL]    = (nfsd4_dec)nfsd4_decode_backchannel_ctl,
@@ -1599,24 +1584,53 @@ static nfsd4_dec nfsd41_dec_ops[] = {
        [OP_RECLAIM_COMPLETE]   = (nfsd4_dec)nfsd4_decode_reclaim_complete,
 };
 
-struct nfsd4_minorversion_ops {
-       nfsd4_dec *decoders;
-       int nops;
-};
+static inline bool
+nfsd4_opnum_in_range(struct nfsd4_compoundargs *argp, struct nfsd4_op *op)
+{
+       if (op->opnum < FIRST_NFS4_OP)
+               return false;
+       else if (argp->minorversion == 0 && op->opnum > LAST_NFS40_OP)
+               return false;
+       else if (argp->minorversion == 1 && op->opnum > LAST_NFS41_OP)
+               return false;
+       else if (argp->minorversion == 2 && op->opnum > LAST_NFS42_OP)
+               return false;
+       return true;
+}
 
-static struct nfsd4_minorversion_ops nfsd4_minorversion[] = {
-       [0] = { nfsd4_dec_ops, ARRAY_SIZE(nfsd4_dec_ops) },
-       [1] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) },
-       [2] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) },
-};
+/*
+ * Return a rough estimate of the maximum possible reply size.  Note the
+ * estimate includes rpc headers so is meant to be passed to
+ * svc_reserve, not svc_reserve_auth.
+ *
+ * Also note the current compound encoding permits only one operation to
+ * use pages beyond the first one, so the maximum possible length is the
+ * maximum over these values, not the sum.
+ */
+static int nfsd4_max_reply(u32 opnum)
+{
+       switch (opnum) {
+       case OP_READLINK:
+       case OP_READDIR:
+               /*
+                * Both of these ops take a single page for data and put
+                * the head and tail in another page:
+                */
+               return 2 * PAGE_SIZE;
+       case OP_READ:
+               return INT_MAX;
+       default:
+               return PAGE_SIZE;
+       }
+}
 
 static __be32
 nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
 {
        DECODE_HEAD;
        struct nfsd4_op *op;
-       struct nfsd4_minorversion_ops *ops;
        bool cachethis = false;
+       int max_reply = PAGE_SIZE;
        int i;
 
        READ_BUF(4);
@@ -1640,10 +1654,9 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                }
        }
 
-       if (argp->minorversion >= ARRAY_SIZE(nfsd4_minorversion))
+       if (argp->minorversion > NFSD_SUPPORTED_MINOR_VERSION)
                argp->opcnt = 0;
 
-       ops = &nfsd4_minorversion[argp->minorversion];
        for (i = 0; i < argp->opcnt; i++) {
                op = &argp->ops[i];
                op->replay = NULL;
@@ -1651,8 +1664,8 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                READ_BUF(4);
                READ32(op->opnum);
 
-               if (op->opnum >= FIRST_NFS4_OP && op->opnum <= LAST_NFS4_OP)
-                       op->status = ops->decoders[op->opnum](argp, &op->u);
+               if (nfsd4_opnum_in_range(argp, op))
+                       op->status = nfsd4_dec_ops[op->opnum](argp, &op->u);
                else {
                        op->opnum = OP_ILLEGAL;
                        op->status = nfserr_op_illegal;
@@ -1667,10 +1680,14 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                 * op in the compound wants to be cached:
                 */
                cachethis |= nfsd4_cache_this_op(op);
+
+               max_reply = max(max_reply, nfsd4_max_reply(op->opnum));
        }
        /* Sessions make the DRC unnecessary: */
        if (argp->minorversion)
                cachethis = false;
+       if (max_reply != INT_MAX)
+               svc_reserve(argp->rqstp, max_reply);
        argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE;
 
        DECODE_TAIL;
@@ -2375,7 +2392,7 @@ out_acl:
        if (bmval0 & FATTR4_WORD0_MAXFILESIZE) {
                if ((buflen -= 8) < 0)
                        goto out_resource;
-               WRITE64(~(u64)0);
+               WRITE64(exp->ex_path.mnt->mnt_sb->s_maxbytes);
        }
        if (bmval0 & FATTR4_WORD0_MAXLINK) {
                if ((buflen -= 4) < 0)