summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2018-07-27 11:19:10 -0400
committerJ. Bruce Fields <bfields@redhat.com>2018-08-09 16:11:21 -0400
commit11b4d66ea3313d9b03a83b80458ddee64990e3c3 (patch)
treed2558f204fe319fc365c07756bfd48a055a0580d
parent3fd9557aec919e2db99365ad5a2c00d04ae8893c (diff)
NFSD: Handle full-length symlinks
I've given up on the idea of zero-copy handling of SYMLINK on the server side. This is because the Linux VFS symlink API requires the symlink pathname to be in a NUL-terminated kmalloc'd buffer. The NUL-termination is going to be problematic (watching out for landing on a page boundary and dealing with a 4096-byte pathname). I don't believe that SYMLINK creation is on a performance path or is requested frequently enough that it will cause noticeable CPU cache pollution due to data copies. There will be two places where a transport callout will be necessary to fill in the rqstp: one will be in the svc_fill_symlink_pathname() helper that is used by NFSv2 and NFSv3, and the other will be in nfsd4_decode_create(). Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--fs/nfsd/nfs3proc.c2
-rw-r--r--fs/nfsd/nfsproc.c2
-rw-r--r--include/linux/sunrpc/svc.h3
-rw-r--r--net/sunrpc/svc.c67
4 files changed, 31 insertions, 43 deletions
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c
index 8d1c2d1a159b..9eb8086ea841 100644
--- a/fs/nfsd/nfs3proc.c
+++ b/fs/nfsd/nfs3proc.c
@@ -290,6 +290,7 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp)
290 RETURN_STATUS(nfserr_nametoolong); 290 RETURN_STATUS(nfserr_nametoolong);
291 291
292 argp->tname = svc_fill_symlink_pathname(rqstp, &argp->first, 292 argp->tname = svc_fill_symlink_pathname(rqstp, &argp->first,
293 page_address(rqstp->rq_arg.pages[0]),
293 argp->tlen); 294 argp->tlen);
294 if (IS_ERR(argp->tname)) 295 if (IS_ERR(argp->tname))
295 RETURN_STATUS(nfserrno(PTR_ERR(argp->tname))); 296 RETURN_STATUS(nfserrno(PTR_ERR(argp->tname)));
@@ -303,6 +304,7 @@ nfsd3_proc_symlink(struct svc_rqst *rqstp)
303 fh_init(&resp->fh, NFS3_FHSIZE); 304 fh_init(&resp->fh, NFS3_FHSIZE);
304 nfserr = nfsd_symlink(rqstp, &resp->dirfh, argp->fname, argp->flen, 305 nfserr = nfsd_symlink(rqstp, &resp->dirfh, argp->fname, argp->flen,
305 argp->tname, &resp->fh); 306 argp->tname, &resp->fh);
307 kfree(argp->tname);
306 RETURN_STATUS(nfserr); 308 RETURN_STATUS(nfserr);
307} 309}
308 310
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index a6faee562b31..0d20fd161225 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -454,6 +454,7 @@ nfsd_proc_symlink(struct svc_rqst *rqstp)
454 return nfserr_nametoolong; 454 return nfserr_nametoolong;
455 455
456 argp->tname = svc_fill_symlink_pathname(rqstp, &argp->first, 456 argp->tname = svc_fill_symlink_pathname(rqstp, &argp->first,
457 page_address(rqstp->rq_arg.pages[0]),
457 argp->tlen); 458 argp->tlen);
458 if (IS_ERR(argp->tname)) 459 if (IS_ERR(argp->tname))
459 return nfserrno(PTR_ERR(argp->tname)); 460 return nfserrno(PTR_ERR(argp->tname));
@@ -466,6 +467,7 @@ nfsd_proc_symlink(struct svc_rqst *rqstp)
466 nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen, 467 nfserr = nfsd_symlink(rqstp, &argp->ffh, argp->fname, argp->flen,
467 argp->tname, &newfh); 468 argp->tname, &newfh);
468 469
470 kfree(argp->tname);
469 fh_put(&argp->ffh); 471 fh_put(&argp->ffh);
470 fh_put(&newfh); 472 fh_put(&newfh);
471 return nfserr; 473 return nfserr;
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index 43f88bd7b601..73e130a840ce 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -499,7 +499,8 @@ unsigned int svc_fill_write_vector(struct svc_rqst *rqstp,
499 struct page **pages, 499 struct page **pages,
500 struct kvec *first, size_t total); 500 struct kvec *first, size_t total);
501char *svc_fill_symlink_pathname(struct svc_rqst *rqstp, 501char *svc_fill_symlink_pathname(struct svc_rqst *rqstp,
502 struct kvec *first, size_t total); 502 struct kvec *first, void *p,
503 size_t total);
503 504
504#define RPC_MAX_ADDRBUFLEN (63U) 505#define RPC_MAX_ADDRBUFLEN (63U)
505 506
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 2194ed507991..d13e05f1a990 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1577,65 +1577,48 @@ EXPORT_SYMBOL_GPL(svc_fill_write_vector);
1577 * svc_fill_symlink_pathname - Construct pathname argument for VFS symlink call 1577 * svc_fill_symlink_pathname - Construct pathname argument for VFS symlink call
1578 * @rqstp: svc_rqst to operate on 1578 * @rqstp: svc_rqst to operate on
1579 * @first: buffer containing first section of pathname 1579 * @first: buffer containing first section of pathname
1580 * @p: buffer containing remaining section of pathname
1580 * @total: total length of the pathname argument 1581 * @total: total length of the pathname argument
1581 * 1582 *
1582 * Returns pointer to a NUL-terminated string, or an ERR_PTR. The buffer is 1583 * The VFS symlink API demands a NUL-terminated pathname in mapped memory.
1583 * released automatically when @rqstp is recycled. 1584 * Returns pointer to a NUL-terminated string, or an ERR_PTR. Caller must free
1585 * the returned string.
1584 */ 1586 */
1585char *svc_fill_symlink_pathname(struct svc_rqst *rqstp, struct kvec *first, 1587char *svc_fill_symlink_pathname(struct svc_rqst *rqstp, struct kvec *first,
1586 size_t total) 1588 void *p, size_t total)
1587{ 1589{
1588 struct xdr_buf *arg = &rqstp->rq_arg; 1590 size_t len, remaining;
1589 struct page **pages; 1591 char *result, *dst;
1590 char *result;
1591
1592 /* VFS API demands a NUL-terminated pathname. This function
1593 * uses a page from @rqstp as the pathname buffer, to enable
1594 * direct placement. Thus the total buffer size is PAGE_SIZE.
1595 * Space in this buffer for NUL-termination requires that we
1596 * cap the size of the returned symlink pathname just a
1597 * little early.
1598 */
1599 if (total > PAGE_SIZE - 1)
1600 return ERR_PTR(-ENAMETOOLONG);
1601 1592
1602 /* Some types of transport can present the pathname entirely 1593 result = kmalloc(total + 1, GFP_KERNEL);
1603 * in rq_arg.pages. If not, then copy the pathname into one 1594 if (!result)
1604 * page. 1595 return ERR_PTR(-ESERVERFAULT);
1605 */
1606 pages = arg->pages;
1607 WARN_ON_ONCE(arg->page_base != 0);
1608 if (first->iov_base == 0) {
1609 result = page_address(*pages);
1610 result[total] = '\0';
1611 } else {
1612 size_t len, remaining;
1613 char *dst;
1614 1596
1615 result = page_address(*(rqstp->rq_next_page++)); 1597 dst = result;
1616 dst = result; 1598 remaining = total;
1617 remaining = total;
1618 1599
1619 len = min_t(size_t, total, first->iov_len); 1600 len = min_t(size_t, total, first->iov_len);
1601 if (len) {
1620 memcpy(dst, first->iov_base, len); 1602 memcpy(dst, first->iov_base, len);
1621 dst += len; 1603 dst += len;
1622 remaining -= len; 1604 remaining -= len;
1605 }
1623 1606
1624 /* No more than one page left */ 1607 if (remaining) {
1625 if (remaining) { 1608 len = min_t(size_t, remaining, PAGE_SIZE);
1626 len = min_t(size_t, remaining, PAGE_SIZE); 1609 memcpy(dst, p, len);
1627 memcpy(dst, page_address(*pages), len); 1610 dst += len;
1628 dst += len;
1629 }
1630
1631 *dst = '\0';
1632 } 1611 }
1633 1612
1634 /* Sanity check: we don't allow the pathname argument to 1613 *dst = '\0';
1614
1615 /* Sanity check: Linux doesn't allow the pathname argument to
1635 * contain a NUL byte. 1616 * contain a NUL byte.
1636 */ 1617 */
1637 if (strlen(result) != total) 1618 if (strlen(result) != total) {
1619 kfree(result);
1638 return ERR_PTR(-EINVAL); 1620 return ERR_PTR(-EINVAL);
1621 }
1639 return result; 1622 return result;
1640} 1623}
1641EXPORT_SYMBOL_GPL(svc_fill_symlink_pathname); 1624EXPORT_SYMBOL_GPL(svc_fill_symlink_pathname);