mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	NFSD: Update the NFSv2 READDIR entry encoder to use struct xdr_stream
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
		
							parent
							
								
									94c8f8c682
								
							
						
					
					
						commit
						f5dcccd647
					
				
					 3 changed files with 103 additions and 11 deletions
				
			
		| 
						 | 
				
			
			@ -559,14 +559,27 @@ static void nfsd_init_dirlist_pages(struct svc_rqst *rqstp,
 | 
			
		|||
				    struct nfsd_readdirres *resp,
 | 
			
		||||
				    int count)
 | 
			
		||||
{
 | 
			
		||||
	struct xdr_buf *buf = &resp->dirlist;
 | 
			
		||||
	struct xdr_stream *xdr = &resp->xdr;
 | 
			
		||||
 | 
			
		||||
	count = min_t(u32, count, PAGE_SIZE);
 | 
			
		||||
 | 
			
		||||
	/* Convert byte count to number of words (i.e. >> 2),
 | 
			
		||||
	 * and reserve room for the NULL ptr & eof flag (-2 words) */
 | 
			
		||||
	resp->buflen = (count >> 2) - 2;
 | 
			
		||||
	memset(buf, 0, sizeof(*buf));
 | 
			
		||||
 | 
			
		||||
	resp->buffer = page_address(*rqstp->rq_next_page);
 | 
			
		||||
	/* Reserve room for the NULL ptr & eof flag (-2 words) */
 | 
			
		||||
	buf->buflen = count - sizeof(__be32) * 2;
 | 
			
		||||
	buf->pages = rqstp->rq_next_page;
 | 
			
		||||
	rqstp->rq_next_page++;
 | 
			
		||||
 | 
			
		||||
	/* This is xdr_init_encode(), but it assumes that
 | 
			
		||||
	 * the head kvec has already been consumed. */
 | 
			
		||||
	xdr_set_scratch_buffer(xdr, NULL, 0);
 | 
			
		||||
	xdr->buf = buf;
 | 
			
		||||
	xdr->page_ptr = buf->pages;
 | 
			
		||||
	xdr->iov = NULL;
 | 
			
		||||
	xdr->p = page_address(*buf->pages);
 | 
			
		||||
	xdr->end = xdr->p + (PAGE_SIZE >> 2);
 | 
			
		||||
	xdr->rqst = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -585,12 +598,11 @@ nfsd_proc_readdir(struct svc_rqst *rqstp)
 | 
			
		|||
 | 
			
		||||
	nfsd_init_dirlist_pages(rqstp, resp, argp->count);
 | 
			
		||||
 | 
			
		||||
	resp->offset = NULL;
 | 
			
		||||
	resp->common.err = nfs_ok;
 | 
			
		||||
	/* Read directory and encode entries on the fly */
 | 
			
		||||
	resp->cookie_offset = 0;
 | 
			
		||||
	offset = argp->cookie;
 | 
			
		||||
	resp->status = nfsd_readdir(rqstp, &argp->fh, &offset,
 | 
			
		||||
				    &resp->common, nfssvc_encode_entry);
 | 
			
		||||
				    &resp->common, nfs2svc_encode_entry);
 | 
			
		||||
	nfssvc_encode_nfscookie(resp, offset);
 | 
			
		||||
 | 
			
		||||
	fh_put(&argp->fh);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -576,12 +576,13 @@ nfssvc_encode_readdirres(struct svc_rqst *rqstp, __be32 *p)
 | 
			
		|||
{
 | 
			
		||||
	struct xdr_stream *xdr = &rqstp->rq_res_stream;
 | 
			
		||||
	struct nfsd_readdirres *resp = rqstp->rq_resp;
 | 
			
		||||
	struct xdr_buf *dirlist = &resp->dirlist;
 | 
			
		||||
 | 
			
		||||
	if (!svcxdr_encode_stat(xdr, resp->status))
 | 
			
		||||
		return 0;
 | 
			
		||||
	switch (resp->status) {
 | 
			
		||||
	case nfs_ok:
 | 
			
		||||
		xdr_write_pages(xdr, &resp->page, 0, resp->count << 2);
 | 
			
		||||
		xdr_write_pages(xdr, dirlist->pages, 0, dirlist->len);
 | 
			
		||||
		/* no more entries */
 | 
			
		||||
		if (xdr_stream_encode_item_absent(xdr) < 0)
 | 
			
		||||
			return 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -623,14 +624,86 @@ nfssvc_encode_statfsres(struct svc_rqst *rqstp, __be32 *p)
 | 
			
		|||
 * @resp: readdir result context
 | 
			
		||||
 * @offset: offset cookie to encode
 | 
			
		||||
 *
 | 
			
		||||
 * The buffer space for the offset cookie has already been reserved
 | 
			
		||||
 * by svcxdr_encode_entry_common().
 | 
			
		||||
 */
 | 
			
		||||
void nfssvc_encode_nfscookie(struct nfsd_readdirres *resp, u32 offset)
 | 
			
		||||
{
 | 
			
		||||
	if (!resp->offset)
 | 
			
		||||
	__be32 cookie = cpu_to_be32(offset);
 | 
			
		||||
 | 
			
		||||
	if (!resp->cookie_offset)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	*resp->offset = cpu_to_be32(offset);
 | 
			
		||||
	resp->offset = NULL;
 | 
			
		||||
	write_bytes_to_xdr_buf(&resp->dirlist, resp->cookie_offset, &cookie,
 | 
			
		||||
			       sizeof(cookie));
 | 
			
		||||
	resp->cookie_offset = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool
 | 
			
		||||
svcxdr_encode_entry_common(struct nfsd_readdirres *resp, const char *name,
 | 
			
		||||
			   int namlen, loff_t offset, u64 ino)
 | 
			
		||||
{
 | 
			
		||||
	struct xdr_buf *dirlist = &resp->dirlist;
 | 
			
		||||
	struct xdr_stream *xdr = &resp->xdr;
 | 
			
		||||
 | 
			
		||||
	if (xdr_stream_encode_item_present(xdr) < 0)
 | 
			
		||||
		return false;
 | 
			
		||||
	/* fileid */
 | 
			
		||||
	if (xdr_stream_encode_u32(xdr, (u32)ino) < 0)
 | 
			
		||||
		return false;
 | 
			
		||||
	/* name */
 | 
			
		||||
	if (xdr_stream_encode_opaque(xdr, name, min(namlen, NFS2_MAXNAMLEN)) < 0)
 | 
			
		||||
		return false;
 | 
			
		||||
	/* cookie */
 | 
			
		||||
	resp->cookie_offset = dirlist->len;
 | 
			
		||||
	if (xdr_stream_encode_u32(xdr, ~0U) < 0)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * nfs2svc_encode_entry - encode one NFSv2 READDIR entry
 | 
			
		||||
 * @data: directory context
 | 
			
		||||
 * @name: name of the object to be encoded
 | 
			
		||||
 * @namlen: length of that name, in bytes
 | 
			
		||||
 * @offset: the offset of the previous entry
 | 
			
		||||
 * @ino: the fileid of this entry
 | 
			
		||||
 * @d_type: unused
 | 
			
		||||
 *
 | 
			
		||||
 * Return values:
 | 
			
		||||
 *   %0: Entry was successfully encoded.
 | 
			
		||||
 *   %-EINVAL: An encoding problem occured, secondary status code in resp->common.err
 | 
			
		||||
 *
 | 
			
		||||
 * On exit, the following fields are updated:
 | 
			
		||||
 *   - resp->xdr
 | 
			
		||||
 *   - resp->common.err
 | 
			
		||||
 *   - resp->cookie_offset
 | 
			
		||||
 */
 | 
			
		||||
int nfs2svc_encode_entry(void *data, const char *name, int namlen,
 | 
			
		||||
			 loff_t offset, u64 ino, unsigned int d_type)
 | 
			
		||||
{
 | 
			
		||||
	struct readdir_cd *ccd = data;
 | 
			
		||||
	struct nfsd_readdirres *resp = container_of(ccd,
 | 
			
		||||
						    struct nfsd_readdirres,
 | 
			
		||||
						    common);
 | 
			
		||||
	unsigned int starting_length = resp->dirlist.len;
 | 
			
		||||
 | 
			
		||||
	/* The offset cookie for the previous entry */
 | 
			
		||||
	nfssvc_encode_nfscookie(resp, offset);
 | 
			
		||||
 | 
			
		||||
	if (!svcxdr_encode_entry_common(resp, name, namlen, offset, ino))
 | 
			
		||||
		goto out_toosmall;
 | 
			
		||||
 | 
			
		||||
	xdr_commit_encode(&resp->xdr);
 | 
			
		||||
	resp->common.err = nfs_ok;
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
out_toosmall:
 | 
			
		||||
	resp->cookie_offset = 0;
 | 
			
		||||
	resp->common.err = nfserr_toosmall;
 | 
			
		||||
	resp->dirlist.len = starting_length;
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -106,15 +106,20 @@ struct nfsd_readres {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
struct nfsd_readdirres {
 | 
			
		||||
	/* Components of the reply */
 | 
			
		||||
	__be32			status;
 | 
			
		||||
 | 
			
		||||
	int			count;
 | 
			
		||||
 | 
			
		||||
	/* Used to encode the reply's entry list */
 | 
			
		||||
	struct xdr_stream	xdr;
 | 
			
		||||
	struct xdr_buf		dirlist;
 | 
			
		||||
	struct readdir_cd	common;
 | 
			
		||||
	__be32 *		buffer;
 | 
			
		||||
	int			buflen;
 | 
			
		||||
	__be32 *		offset;
 | 
			
		||||
	struct page		*page;
 | 
			
		||||
	unsigned int		cookie_offset;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct nfsd_statfsres {
 | 
			
		||||
| 
						 | 
				
			
			@ -159,6 +164,8 @@ int nfssvc_encode_statfsres(struct svc_rqst *, __be32 *);
 | 
			
		|||
int nfssvc_encode_readdirres(struct svc_rqst *, __be32 *);
 | 
			
		||||
 | 
			
		||||
void nfssvc_encode_nfscookie(struct nfsd_readdirres *resp, u32 offset);
 | 
			
		||||
int nfs2svc_encode_entry(void *data, const char *name, int namlen,
 | 
			
		||||
			 loff_t offset, u64 ino, unsigned int d_type);
 | 
			
		||||
int nfssvc_encode_entry(void *, const char *name,
 | 
			
		||||
			int namlen, loff_t offset, u64 ino, unsigned int);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue