Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 476a7b1f authored by J. Bruce Fields's avatar J. Bruce Fields
Browse files

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: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 3b299709
Loading
Loading
Loading
Loading
+12 −30
Original line number Diff line number Diff line
@@ -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;
}