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

Commit 38a70315 authored by Chuck Lever's avatar Chuck Lever Committed by J. Bruce Fields
Browse files

NFSD: Clean up legacy NFS SYMLINK argument XDR decoders



Move common code in NFSD's legacy SYMLINK decoders into a helper.
The immediate benefits include:

 - one fewer data copies on transports that support DDP
 - consistent error checking across all versions
 - reduction of code duplication
 - support for both legal forms of SYMLINK requests on RDMA
   transports for all versions of NFS (in particular, NFSv2, for
   completeness)

In the long term, this helper is an appropriate spot to perform a
per-transport call-out to fill the pathname argument using, say,
RDMA Reads.

Filling the pathname in the proc function also means that eventually
the incoming filehandle can be interpreted so that filesystem-
specific memory can be allocated as a sink for the pathname
argument, rather than using anonymous pages.

Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 8154ef27
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -283,6 +283,16 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp)
	struct nfsd3_diropres *resp = rqstp->rq_resp;
	__be32	nfserr;

	if (argp->tlen == 0)
		RETURN_STATUS(nfserr_inval);
	if (argp->tlen > NFS3_MAXPATHLEN)
		RETURN_STATUS(nfserr_nametoolong);

	argp->tname = svc_fill_symlink_pathname(rqstp, &argp->first,
						argp->tlen);
	if (IS_ERR(argp->tname))
		RETURN_STATUS(nfserrno(PTR_ERR(argp->tname)));

	dprintk("nfsd: SYMLINK(3)  %s %.*s -> %.*s\n",
				SVCFH_fmt(&argp->ffh),
				argp->flen, argp->fname,
+12 −39
Original line number Diff line number Diff line
@@ -481,51 +481,24 @@ int
nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
{
	struct nfsd3_symlinkargs *args = rqstp->rq_argp;
	unsigned int len, avail;
	char *old, *new;
	struct kvec *vec;
	char *base = (char *)p;
	size_t dlen;

	if (!(p = decode_fh(p, &args->ffh)) ||
	    !(p = decode_filename(p, &args->fname, &args->flen))
		)
	    !(p = decode_filename(p, &args->fname, &args->flen)))
		return 0;
	p = decode_sattr3(p, &args->attrs);

	/* now decode the pathname, which might be larger than the first page.
	 * As we have to check for nul's anyway, we copy it into a new page
	 * This page appears in the rq_res.pages list, but as pages_len is always
	 * 0, it won't get in the way
	 */
	len = ntohl(*p++);
	if (len == 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE)
		return 0;
	args->tname = new = page_address(*(rqstp->rq_next_page++));
	args->tlen = len;
	/* first copy and check from the first page */
	old = (char*)p;
	vec = &rqstp->rq_arg.head[0];
	if ((void *)old > vec->iov_base + vec->iov_len)
		return 0;
	avail = vec->iov_len - (old - (char*)vec->iov_base);
	while (len && avail && *old) {
		*new++ = *old++;
		len--;
		avail--;
	}
	/* now copy next page if there is one */
	if (len && !avail && rqstp->rq_arg.page_len) {
		avail = min_t(unsigned int, rqstp->rq_arg.page_len, PAGE_SIZE);
		old = page_address(rqstp->rq_arg.pages[0]);
	}
	while (len && avail && *old) {
		*new++ = *old++;
		len--;
		avail--;
	}
	*new = '\0';
	if (len)
		return 0;
	args->tlen = ntohl(*p++);

	args->first.iov_base = p;
	args->first.iov_len = rqstp->rq_arg.head[0].iov_len;
	args->first.iov_len -= (char *)p - base;

	dlen = args->first.iov_len + rqstp->rq_arg.page_len +
	       rqstp->rq_arg.tail[0].iov_len;
	if (dlen < XDR_QUADLEN(args->tlen) << 2)
		return 0;
	return 1;
}

+8 −6
Original line number Diff line number Diff line
@@ -449,17 +449,19 @@ nfsd_proc_symlink(struct svc_rqst *rqstp)
	struct svc_fh	newfh;
	__be32		nfserr;

	if (argp->tlen > NFS_MAXPATHLEN)
		return nfserr_nametoolong;

	argp->tname = svc_fill_symlink_pathname(rqstp, &argp->first,
						argp->tlen);
	if (IS_ERR(argp->tname))
		return nfserrno(PTR_ERR(argp->tname));

	dprintk("nfsd: SYMLINK  %s %.*s -> %.*s\n",
		SVCFH_fmt(&argp->ffh), argp->flen, argp->fname,
		argp->tlen, argp->tname);

	fh_init(&newfh, NFS_FHSIZE);
	/*
	 * Crazy hack: the request fits in a page, and already-decoded
	 * attributes follow argp->tname, so it's safe to just write a
	 * null to ensure it's null-terminated:
	 */
	argp->tname[argp->tlen] = '\0';
	nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
						 argp->tname, &newfh);

+29 −20
Original line number Diff line number Diff line
@@ -70,22 +70,6 @@ decode_filename(__be32 *p, char **namp, unsigned int *lenp)
	return p;
}

static __be32 *
decode_pathname(__be32 *p, char **namp, unsigned int *lenp)
{
	char		*name;
	unsigned int	i;

	if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS_MAXPATHLEN)) != NULL) {
		for (i = 0, name = *namp; i < *lenp; i++, name++) {
			if (*name == '\0')
				return NULL;
		}
	}

	return p;
}

static __be32 *
decode_sattr(__be32 *p, struct iattr *iap)
{
@@ -384,14 +368,39 @@ int
nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
{
	struct nfsd_symlinkargs *args = rqstp->rq_argp;
	char *base = (char *)p;
	size_t xdrlen;

	if (   !(p = decode_fh(p, &args->ffh))
	    || !(p = decode_filename(p, &args->fname, &args->flen))
	    || !(p = decode_pathname(p, &args->tname, &args->tlen)))
	    || !(p = decode_filename(p, &args->fname, &args->flen)))
		return 0;
	p = decode_sattr(p, &args->attrs);

	return xdr_argsize_check(rqstp, p);
	args->tlen = ntohl(*p++);
	if (args->tlen == 0)
		return 0;

	args->first.iov_base = p;
	args->first.iov_len = rqstp->rq_arg.head[0].iov_len;
	args->first.iov_len -= (char *)p - base;

	/* This request is never larger than a page. Therefore,
	 * transport will deliver either:
	 * 1. pathname in the pagelist -> sattr is in the tail.
	 * 2. everything in the head buffer -> sattr is in the head.
	 */
	if (rqstp->rq_arg.page_len) {
		if (args->tlen != rqstp->rq_arg.page_len)
			return 0;
		p = rqstp->rq_arg.tail[0].iov_base;
	} else {
		xdrlen = XDR_QUADLEN(args->tlen);
		if (xdrlen > args->first.iov_len - (8 * sizeof(__be32)))
			return 0;
		p += xdrlen;
	}
	decode_sattr(p, &args->attrs);

	return 1;
}

int
+1 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ struct nfsd_symlinkargs {
	char *			tname;
	unsigned int		tlen;
	struct iattr		attrs;
	struct kvec		first;
};

struct nfsd_readdirargs {
Loading