diff options
author | J. Bruce Fields <bfields@redhat.com> | 2014-03-06 20:39:29 -0500 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2014-05-27 11:09:08 -0400 |
commit | 07d1f8020738ba3180ea9992c4fa7dbc0685396a (patch) | |
tree | 3c400bbbe4c453f6b1d5afddf3d7bfae77e81daa /fs | |
parent | 1802a67894fab3ff90a3ef4f484e97a5b4515426 (diff) |
nfsd4: fix encoding of out-of-space replies
If nfsd4_check_resp_size() returns an error then we should really be
truncating the reply here, otherwise we may leave extra garbage at the
end of the rpc reply.
Also add a warning to catch any cases where our reply-size estimates may
be wrong in the case of a non-idempotent operation.
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfsd/nfs4proc.c | 13 | ||||
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 17 | ||||
-rw-r--r-- | fs/nfsd/xdr4.h | 2 |
3 files changed, 27 insertions, 5 deletions
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 747d6a87a47d..bf8cddfdd3be 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c | |||
@@ -1171,9 +1171,7 @@ struct nfsd4_operation { | |||
1171 | 1171 | ||
1172 | static struct nfsd4_operation nfsd4_ops[]; | 1172 | static struct nfsd4_operation nfsd4_ops[]; |
1173 | 1173 | ||
1174 | #ifdef NFSD_DEBUG | ||
1175 | static const char *nfsd4_op_name(unsigned opnum); | 1174 | static const char *nfsd4_op_name(unsigned opnum); |
1176 | #endif | ||
1177 | 1175 | ||
1178 | /* | 1176 | /* |
1179 | * Enforce NFSv4.1 COMPOUND ordering rules: | 1177 | * Enforce NFSv4.1 COMPOUND ordering rules: |
@@ -1859,14 +1857,21 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1859 | }, | 1857 | }, |
1860 | }; | 1858 | }; |
1861 | 1859 | ||
1862 | #ifdef NFSD_DEBUG | 1860 | void warn_on_nonidempotent_op(struct nfsd4_op *op) |
1861 | { | ||
1862 | if (OPDESC(op)->op_flags & OP_MODIFIES_SOMETHING) { | ||
1863 | pr_err("unable to encode reply to nonidempotent op %d (%s)\n", | ||
1864 | op->opnum, nfsd4_op_name(op->opnum)); | ||
1865 | WARN_ON_ONCE(1); | ||
1866 | } | ||
1867 | } | ||
1868 | |||
1863 | static const char *nfsd4_op_name(unsigned opnum) | 1869 | static const char *nfsd4_op_name(unsigned opnum) |
1864 | { | 1870 | { |
1865 | if (opnum < ARRAY_SIZE(nfsd4_ops)) | 1871 | if (opnum < ARRAY_SIZE(nfsd4_ops)) |
1866 | return nfsd4_ops[opnum].op_name; | 1872 | return nfsd4_ops[opnum].op_name; |
1867 | return "unknown_operation"; | 1873 | return "unknown_operation"; |
1868 | } | 1874 | } |
1869 | #endif | ||
1870 | 1875 | ||
1871 | #define nfsd4_voidres nfsd4_voidargs | 1876 | #define nfsd4_voidres nfsd4_voidargs |
1872 | struct nfsd4_voidargs { int dummy; }; | 1877 | struct nfsd4_voidargs { int dummy; }; |
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 187925065d16..24ba652008db 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c | |||
@@ -3637,6 +3637,7 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) | |||
3637 | { | 3637 | { |
3638 | struct nfs4_stateowner *so = resp->cstate.replay_owner; | 3638 | struct nfs4_stateowner *so = resp->cstate.replay_owner; |
3639 | __be32 *statp; | 3639 | __be32 *statp; |
3640 | nfsd4_enc encoder; | ||
3640 | __be32 *p; | 3641 | __be32 *p; |
3641 | 3642 | ||
3642 | RESERVE_SPACE(8); | 3643 | RESERVE_SPACE(8); |
@@ -3648,10 +3649,24 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op) | |||
3648 | goto status; | 3649 | goto status; |
3649 | BUG_ON(op->opnum < 0 || op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) || | 3650 | BUG_ON(op->opnum < 0 || op->opnum >= ARRAY_SIZE(nfsd4_enc_ops) || |
3650 | !nfsd4_enc_ops[op->opnum]); | 3651 | !nfsd4_enc_ops[op->opnum]); |
3651 | op->status = nfsd4_enc_ops[op->opnum](resp, op->status, &op->u); | 3652 | encoder = nfsd4_enc_ops[op->opnum]; |
3653 | op->status = encoder(resp, op->status, &op->u); | ||
3652 | /* nfsd4_check_resp_size guarantees enough room for error status */ | 3654 | /* nfsd4_check_resp_size guarantees enough room for error status */ |
3653 | if (!op->status) | 3655 | if (!op->status) |
3654 | op->status = nfsd4_check_resp_size(resp, 0); | 3656 | op->status = nfsd4_check_resp_size(resp, 0); |
3657 | if (op->status == nfserr_resource || | ||
3658 | op->status == nfserr_rep_too_big || | ||
3659 | op->status == nfserr_rep_too_big_to_cache) { | ||
3660 | /* | ||
3661 | * The operation may have already been encoded or | ||
3662 | * partially encoded. No op returns anything additional | ||
3663 | * in the case of one of these three errors, so we can | ||
3664 | * just truncate back to after the status. But it's a | ||
3665 | * bug if we had to do this on a non-idempotent op: | ||
3666 | */ | ||
3667 | warn_on_nonidempotent_op(op); | ||
3668 | resp->xdr.p = statp + 1; | ||
3669 | } | ||
3655 | if (so) { | 3670 | if (so) { |
3656 | so->so_replay.rp_status = op->status; | 3671 | so->so_replay.rp_status = op->status; |
3657 | so->so_replay.rp_buflen = (char *)resp->xdr.p | 3672 | so->so_replay.rp_buflen = (char *)resp->xdr.p |
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index f62a055bf63c..15ca47797a82 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h | |||
@@ -536,6 +536,8 @@ static inline bool nfsd4_last_compound_op(struct svc_rqst *rqstp) | |||
536 | return argp->opcnt == resp->opcnt; | 536 | return argp->opcnt == resp->opcnt; |
537 | } | 537 | } |
538 | 538 | ||
539 | void warn_on_nonidempotent_op(struct nfsd4_op *op); | ||
540 | |||
539 | #define NFS4_SVC_XDRSIZE sizeof(struct nfsd4_compoundargs) | 541 | #define NFS4_SVC_XDRSIZE sizeof(struct nfsd4_compoundargs) |
540 | 542 | ||
541 | static inline void | 543 | static inline void |