diff options
| author | J. Bruce Fields <bfields@redhat.com> | 2014-01-20 17:08:27 -0500 |
|---|---|---|
| committer | J. Bruce Fields <bfields@redhat.com> | 2014-05-30 17:32:05 -0400 |
| commit | 476a7b1f4b2c9c38255653fa55157565be8b14be (patch) | |
| tree | d44f678620c4789cb9679ddd6cbf8e52d8e8d314 | |
| parent | 3b299709091befc0e02aa33d55ddd5baef006853 (diff) | |
nfsd4: don't treat readlink like a zero-copy operation
There's no advantage to this zero-copy-style readlink encoding, and it
unnecessarily limits the kinds of compounds we can handle. (In practice
I can't see why a client would want e.g. multiple readlink calls in a
comound, but it's probably a spec violation for us not to handle it.)
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
| -rw-r--r-- | fs/nfsd/nfs4xdr.c | 42 |
1 files changed, 12 insertions, 30 deletions
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 3f2a52ccb9d1..cda6226bda91 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c | |||
| @@ -3160,8 +3160,9 @@ static __be32 | |||
| 3160 | nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readlink *readlink) | 3160 | nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readlink *readlink) |
| 3161 | { | 3161 | { |
| 3162 | int maxcount; | 3162 | int maxcount; |
| 3163 | __be32 wire_count; | ||
| 3164 | int zero = 0; | ||
| 3163 | struct xdr_stream *xdr = &resp->xdr; | 3165 | struct xdr_stream *xdr = &resp->xdr; |
| 3164 | char *page; | ||
| 3165 | int length_offset = xdr->buf->len; | 3166 | int length_offset = xdr->buf->len; |
| 3166 | __be32 *p; | 3167 | __be32 *p; |
| 3167 | 3168 | ||
| @@ -3171,26 +3172,19 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd | |||
| 3171 | p = xdr_reserve_space(xdr, 4); | 3172 | p = xdr_reserve_space(xdr, 4); |
| 3172 | if (!p) | 3173 | if (!p) |
| 3173 | return nfserr_resource; | 3174 | return nfserr_resource; |
| 3174 | |||
| 3175 | if (resp->xdr.buf->page_len) | ||
| 3176 | return nfserr_resource; | ||
| 3177 | if (!*resp->rqstp->rq_next_page) | ||
| 3178 | return nfserr_resource; | ||
| 3179 | |||
| 3180 | page = page_address(*(resp->rqstp->rq_next_page++)); | ||
| 3181 | |||
| 3182 | maxcount = PAGE_SIZE; | 3175 | maxcount = PAGE_SIZE; |
| 3183 | 3176 | ||
| 3184 | if (xdr->end - xdr->p < 1) | 3177 | p = xdr_reserve_space(xdr, maxcount); |
| 3178 | if (!p) | ||
| 3185 | return nfserr_resource; | 3179 | return nfserr_resource; |
| 3186 | |||
| 3187 | /* | 3180 | /* |
| 3188 | * XXX: By default, the ->readlink() VFS op will truncate symlinks | 3181 | * XXX: By default, the ->readlink() VFS op will truncate symlinks |
| 3189 | * if they would overflow the buffer. Is this kosher in NFSv4? If | 3182 | * if they would overflow the buffer. Is this kosher in NFSv4? If |
| 3190 | * not, one easy fix is: if ->readlink() precisely fills the buffer, | 3183 | * not, one easy fix is: if ->readlink() precisely fills the buffer, |
| 3191 | * assume that truncation occurred, and return NFS4ERR_RESOURCE. | 3184 | * assume that truncation occurred, and return NFS4ERR_RESOURCE. |
| 3192 | */ | 3185 | */ |
| 3193 | nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, page, &maxcount); | 3186 | nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, |
| 3187 | (char *)p, &maxcount); | ||
| 3194 | if (nfserr == nfserr_isdir) | 3188 | if (nfserr == nfserr_isdir) |
| 3195 | nfserr = nfserr_inval; | 3189 | nfserr = nfserr_inval; |
| 3196 | if (nfserr) { | 3190 | if (nfserr) { |
| @@ -3198,24 +3192,12 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd | |||
| 3198 | return nfserr; | 3192 | return nfserr; |
| 3199 | } | 3193 | } |
| 3200 | 3194 | ||
| 3201 | WRITE32(maxcount); | 3195 | wire_count = htonl(maxcount); |
| 3202 | resp->xdr.buf->head[0].iov_len = (char *)p | 3196 | write_bytes_to_xdr_buf(xdr->buf, length_offset, &wire_count, 4); |
| 3203 | - (char *)resp->xdr.buf->head[0].iov_base; | 3197 | xdr_truncate_encode(xdr, length_offset + 4 + maxcount); |
| 3204 | resp->xdr.buf->page_len = maxcount; | 3198 | if (maxcount & 3) |
| 3205 | xdr->buf->len += maxcount; | 3199 | write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount, |
| 3206 | xdr->page_ptr += 1; | 3200 | &zero, 4 - (maxcount&3)); |
| 3207 | xdr->buf->buflen -= PAGE_SIZE; | ||
| 3208 | xdr->iov = xdr->buf->tail; | ||
| 3209 | |||
| 3210 | /* Use rest of head for padding and remaining ops: */ | ||
| 3211 | resp->xdr.buf->tail[0].iov_base = p; | ||
| 3212 | resp->xdr.buf->tail[0].iov_len = 0; | ||
| 3213 | if (maxcount&3) { | ||
| 3214 | p = xdr_reserve_space(xdr, 4); | ||
| 3215 | WRITE32(0); | ||
| 3216 | resp->xdr.buf->tail[0].iov_base += maxcount&3; | ||
| 3217 | resp->xdr.buf->tail[0].iov_len = 4 - (maxcount&3); | ||
| 3218 | } | ||
| 3219 | return 0; | 3201 | return 0; |
| 3220 | } | 3202 | } |
| 3221 | 3203 | ||
