diff options
author | J. Bruce Fields <bfields@redhat.com> | 2014-03-18 17:44:10 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2014-05-30 17:32:12 -0400 |
commit | b042098063849794d69b5322fcc6cf9fb5f2586e (patch) | |
tree | 954338c0f2de77763df3e8728265b91842984466 /fs/nfsd/nfs4xdr.c | |
parent | fec25fa4ad728dd9b063313f2a61ff65eae0d571 (diff) |
nfsd4: allow exotic read compounds
I'm not sure why a client would want to stuff multiple reads in a
single compound rpc, but it's legal for them to do it, and we should
really support it.
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd/nfs4xdr.c')
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 61 |
1 files changed, 25 insertions, 36 deletions
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 92f071b44f75..480f12c4e590 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c | |||
@@ -3139,28 +3139,34 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp, | |||
3139 | struct xdr_stream *xdr = &resp->xdr; | 3139 | struct xdr_stream *xdr = &resp->xdr; |
3140 | u32 eof; | 3140 | u32 eof; |
3141 | int v; | 3141 | int v; |
3142 | struct page *page; | ||
3143 | int starting_len = xdr->buf->len - 8; | 3142 | int starting_len = xdr->buf->len - 8; |
3144 | int space_left; | ||
3145 | long len; | 3143 | long len; |
3144 | int thislen; | ||
3146 | __be32 nfserr; | 3145 | __be32 nfserr; |
3147 | __be32 tmp; | 3146 | __be32 tmp; |
3148 | __be32 *p; | 3147 | __be32 *p; |
3148 | u32 zzz = 0; | ||
3149 | int pad; | ||
3149 | 3150 | ||
3150 | len = maxcount; | 3151 | len = maxcount; |
3151 | v = 0; | 3152 | v = 0; |
3152 | while (len) { | ||
3153 | int thislen; | ||
3154 | 3153 | ||
3155 | page = *(resp->rqstp->rq_next_page); | 3154 | thislen = (void *)xdr->end - (void *)xdr->p; |
3156 | if (!page) { /* ran out of pages */ | 3155 | if (len < thislen) |
3157 | maxcount -= len; | 3156 | thislen = len; |
3158 | break; | 3157 | p = xdr_reserve_space(xdr, (thislen+3)&~3); |
3159 | } | 3158 | WARN_ON_ONCE(!p); |
3159 | resp->rqstp->rq_vec[v].iov_base = p; | ||
3160 | resp->rqstp->rq_vec[v].iov_len = thislen; | ||
3161 | v++; | ||
3162 | len -= thislen; | ||
3163 | |||
3164 | while (len) { | ||
3160 | thislen = min_t(long, len, PAGE_SIZE); | 3165 | thislen = min_t(long, len, PAGE_SIZE); |
3161 | resp->rqstp->rq_vec[v].iov_base = page_address(page); | 3166 | p = xdr_reserve_space(xdr, (thislen+3)&~3); |
3167 | WARN_ON_ONCE(!p); | ||
3168 | resp->rqstp->rq_vec[v].iov_base = p; | ||
3162 | resp->rqstp->rq_vec[v].iov_len = thislen; | 3169 | resp->rqstp->rq_vec[v].iov_len = thislen; |
3163 | resp->rqstp->rq_next_page++; | ||
3164 | v++; | 3170 | v++; |
3165 | len -= thislen; | 3171 | len -= thislen; |
3166 | } | 3172 | } |
@@ -3170,6 +3176,7 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp, | |||
3170 | read->rd_vlen, &maxcount); | 3176 | read->rd_vlen, &maxcount); |
3171 | if (nfserr) | 3177 | if (nfserr) |
3172 | return nfserr; | 3178 | return nfserr; |
3179 | xdr_truncate_encode(xdr, starting_len + 8 + ((maxcount+3)&~3)); | ||
3173 | 3180 | ||
3174 | eof = (read->rd_offset + maxcount >= | 3181 | eof = (read->rd_offset + maxcount >= |
3175 | read->rd_fhp->fh_dentry->d_inode->i_size); | 3182 | read->rd_fhp->fh_dentry->d_inode->i_size); |
@@ -3179,27 +3186,9 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp, | |||
3179 | tmp = htonl(maxcount); | 3186 | tmp = htonl(maxcount); |
3180 | write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp, 4); | 3187 | write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp, 4); |
3181 | 3188 | ||
3182 | resp->xdr.buf->page_len = maxcount; | 3189 | pad = (maxcount&3) ? 4 - (maxcount&3) : 0; |
3183 | xdr->buf->len += maxcount; | 3190 | write_bytes_to_xdr_buf(xdr->buf, starting_len + 8 + maxcount, |
3184 | xdr->page_ptr += v; | 3191 | &zzz, pad); |
3185 | xdr->iov = xdr->buf->tail; | ||
3186 | |||
3187 | /* Use rest of head for padding and remaining ops: */ | ||
3188 | resp->xdr.buf->tail[0].iov_base = xdr->p; | ||
3189 | resp->xdr.buf->tail[0].iov_len = 0; | ||
3190 | if (maxcount&3) { | ||
3191 | p = xdr_reserve_space(xdr, 4); | ||
3192 | WRITE32(0); | ||
3193 | resp->xdr.buf->tail[0].iov_base += maxcount&3; | ||
3194 | resp->xdr.buf->tail[0].iov_len = 4 - (maxcount&3); | ||
3195 | xdr->buf->len -= (maxcount&3); | ||
3196 | } | ||
3197 | |||
3198 | space_left = min_t(int, (void *)xdr->end - (void *)xdr->p, | ||
3199 | xdr->buf->buflen - xdr->buf->len); | ||
3200 | xdr->buf->buflen = xdr->buf->len + space_left; | ||
3201 | xdr->end = (__be32 *)((void *)xdr->end + space_left); | ||
3202 | |||
3203 | return 0; | 3192 | return 0; |
3204 | 3193 | ||
3205 | } | 3194 | } |
@@ -3224,15 +3213,15 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, | |||
3224 | WARN_ON_ONCE(resp->rqstp->rq_splice_ok); | 3213 | WARN_ON_ONCE(resp->rqstp->rq_splice_ok); |
3225 | return nfserr_resource; | 3214 | return nfserr_resource; |
3226 | } | 3215 | } |
3227 | 3216 | if (resp->xdr.buf->page_len && resp->rqstp->rq_splice_ok) { | |
3228 | if (resp->xdr.buf->page_len) { | 3217 | WARN_ON_ONCE(1); |
3229 | WARN_ON_ONCE(resp->rqstp->rq_splice_ok); | ||
3230 | return nfserr_resource; | 3218 | return nfserr_resource; |
3231 | } | 3219 | } |
3232 | |||
3233 | xdr_commit_encode(xdr); | 3220 | xdr_commit_encode(xdr); |
3234 | 3221 | ||
3235 | maxcount = svc_max_payload(resp->rqstp); | 3222 | maxcount = svc_max_payload(resp->rqstp); |
3223 | if (maxcount > xdr->buf->buflen - xdr->buf->len) | ||
3224 | maxcount = xdr->buf->buflen - xdr->buf->len; | ||
3236 | if (maxcount > read->rd_length) | 3225 | if (maxcount > read->rd_length) |
3237 | maxcount = read->rd_length; | 3226 | maxcount = read->rd_length; |
3238 | 3227 | ||