diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-12-04 17:39:23 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-12-06 13:06:30 -0500 |
commit | c7848f69ec4a8c03732cde5c949bd2aa711a9f4b (patch) | |
tree | f4f6e24d4beea569ab9fd532ce61b0df3947c585 | |
parent | 374b105797c3d4f29c685f3be535c35f5689b30e (diff) |
NFSv4: OPEN must handle the NFS4ERR_IO return code correctly
decode_op_hdr() cannot distinguish between an XDR decoding error and
the perfectly valid errorcode NFS4ERR_IO. This is normally not a
problem, but for the particular case of OPEN, we need to be able
to increment the NFSv4 open sequence id when the server returns
a valid response.
Reported-by: J Bruce Fields <bfields@fieldses.org>
Link: http://lkml.kernel.org/r/20131204210356.GA19452@fieldses.org
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Cc: stable@vger.kernel.org
-rw-r--r-- | fs/nfs/nfs4xdr.c | 47 |
1 files changed, 31 insertions, 16 deletions
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 5be2868c02f1..8c21d69a9dc1 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c | |||
@@ -3097,7 +3097,8 @@ out_overflow: | |||
3097 | return -EIO; | 3097 | return -EIO; |
3098 | } | 3098 | } |
3099 | 3099 | ||
3100 | static int decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected) | 3100 | static bool __decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected, |
3101 | int *nfs_retval) | ||
3101 | { | 3102 | { |
3102 | __be32 *p; | 3103 | __be32 *p; |
3103 | uint32_t opnum; | 3104 | uint32_t opnum; |
@@ -3107,19 +3108,32 @@ static int decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected) | |||
3107 | if (unlikely(!p)) | 3108 | if (unlikely(!p)) |
3108 | goto out_overflow; | 3109 | goto out_overflow; |
3109 | opnum = be32_to_cpup(p++); | 3110 | opnum = be32_to_cpup(p++); |
3110 | if (opnum != expected) { | 3111 | if (unlikely(opnum != expected)) |
3111 | dprintk("nfs: Server returned operation" | 3112 | goto out_bad_operation; |
3112 | " %d but we issued a request for %d\n", | ||
3113 | opnum, expected); | ||
3114 | return -EIO; | ||
3115 | } | ||
3116 | nfserr = be32_to_cpup(p); | 3113 | nfserr = be32_to_cpup(p); |
3117 | if (nfserr != NFS_OK) | 3114 | if (nfserr == NFS_OK) |
3118 | return nfs4_stat_to_errno(nfserr); | 3115 | *nfs_retval = 0; |
3119 | return 0; | 3116 | else |
3117 | *nfs_retval = nfs4_stat_to_errno(nfserr); | ||
3118 | return true; | ||
3119 | out_bad_operation: | ||
3120 | dprintk("nfs: Server returned operation" | ||
3121 | " %d but we issued a request for %d\n", | ||
3122 | opnum, expected); | ||
3123 | *nfs_retval = -EREMOTEIO; | ||
3124 | return false; | ||
3120 | out_overflow: | 3125 | out_overflow: |
3121 | print_overflow_msg(__func__, xdr); | 3126 | print_overflow_msg(__func__, xdr); |
3122 | return -EIO; | 3127 | *nfs_retval = -EIO; |
3128 | return false; | ||
3129 | } | ||
3130 | |||
3131 | static int decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected) | ||
3132 | { | ||
3133 | int retval; | ||
3134 | |||
3135 | __decode_op_hdr(xdr, expected, &retval); | ||
3136 | return retval; | ||
3123 | } | 3137 | } |
3124 | 3138 | ||
3125 | /* Dummy routine */ | 3139 | /* Dummy routine */ |
@@ -5001,11 +5015,12 @@ static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res) | |||
5001 | uint32_t savewords, bmlen, i; | 5015 | uint32_t savewords, bmlen, i; |
5002 | int status; | 5016 | int status; |
5003 | 5017 | ||
5004 | status = decode_op_hdr(xdr, OP_OPEN); | 5018 | if (!__decode_op_hdr(xdr, OP_OPEN, &status)) |
5005 | if (status != -EIO) | 5019 | return status; |
5006 | nfs_increment_open_seqid(status, res->seqid); | 5020 | nfs_increment_open_seqid(status, res->seqid); |
5007 | if (!status) | 5021 | if (status) |
5008 | status = decode_stateid(xdr, &res->stateid); | 5022 | return status; |
5023 | status = decode_stateid(xdr, &res->stateid); | ||
5009 | if (unlikely(status)) | 5024 | if (unlikely(status)) |
5010 | return status; | 5025 | return status; |
5011 | 5026 | ||