|
@@ -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);
|
|
@@ -1220,6 +1241,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 +1543,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 +1560,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 +1583,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 +1653,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 +1663,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 +1679,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 +2391,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)
|