nfsd4: don't treat readlink like a zero-copy operation
authorJ. Bruce Fields <bfields@redhat.com>
Mon, 20 Jan 2014 22:08:27 +0000 (17:08 -0500)
committerJ. Bruce Fields <bfields@redhat.com>
Fri, 30 May 2014 21:32:05 +0000 (17:32 -0400)
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>
fs/nfsd/nfs4xdr.c

index 3f2a52c..cda6226 100644 (file)
@@ -3160,8 +3160,9 @@ static __be32
 nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_readlink *readlink)
 {
        int maxcount;
+       __be32 wire_count;
+       int zero = 0;
        struct xdr_stream *xdr = &resp->xdr;
-       char *page;
        int length_offset = xdr->buf->len;
        __be32 *p;
 
@@ -3171,26 +3172,19 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd
        p = xdr_reserve_space(xdr, 4);
        if (!p)
                return nfserr_resource;
-
-       if (resp->xdr.buf->page_len)
-               return nfserr_resource;
-       if (!*resp->rqstp->rq_next_page)
-               return nfserr_resource;
-
-       page = page_address(*(resp->rqstp->rq_next_page++));
-
        maxcount = PAGE_SIZE;
 
-       if (xdr->end - xdr->p < 1)
+       p = xdr_reserve_space(xdr, maxcount);
+       if (!p)
                return nfserr_resource;
-
        /*
         * XXX: By default, the ->readlink() VFS op will truncate symlinks
         * if they would overflow the buffer.  Is this kosher in NFSv4?  If
         * not, one easy fix is: if ->readlink() precisely fills the buffer,
         * assume that truncation occurred, and return NFS4ERR_RESOURCE.
         */
-       nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp, page, &maxcount);
+       nfserr = nfsd_readlink(readlink->rl_rqstp, readlink->rl_fhp,
+                                               (char *)p, &maxcount);
        if (nfserr == nfserr_isdir)
                nfserr = nfserr_inval;
        if (nfserr) {
@@ -3198,24 +3192,12 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd
                return nfserr;
        }
 
-       WRITE32(maxcount);
-       resp->xdr.buf->head[0].iov_len = (char *)p
-                               - (char *)resp->xdr.buf->head[0].iov_base;
-       resp->xdr.buf->page_len = maxcount;
-       xdr->buf->len += maxcount;
-       xdr->page_ptr += 1;
-       xdr->buf->buflen -= PAGE_SIZE;
-       xdr->iov = xdr->buf->tail;
-
-       /* Use rest of head for padding and remaining ops: */
-       resp->xdr.buf->tail[0].iov_base = p;
-       resp->xdr.buf->tail[0].iov_len = 0;
-       if (maxcount&3) {
-               p = xdr_reserve_space(xdr, 4);
-               WRITE32(0);
-               resp->xdr.buf->tail[0].iov_base += maxcount&3;
-               resp->xdr.buf->tail[0].iov_len = 4 - (maxcount&3);
-       }
+       wire_count = htonl(maxcount);
+       write_bytes_to_xdr_buf(xdr->buf, length_offset, &wire_count, 4);
+       xdr_truncate_encode(xdr, length_offset + 4 + maxcount);
+       if (maxcount & 3)
+               write_bytes_to_xdr_buf(xdr->buf, length_offset + 4 + maxcount,
+                                               &zero, 4 - (maxcount&3));
        return 0;
 }