diff options
author | Chuck Lever <chuck.lever@oracle.com> | 2018-07-27 11:19:10 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2018-08-09 16:11:21 -0400 |
commit | 11b4d66ea3313d9b03a83b80458ddee64990e3c3 (patch) | |
tree | d2558f204fe319fc365c07756bfd48a055a0580d /net/sunrpc | |
parent | 3fd9557aec919e2db99365ad5a2c00d04ae8893c (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>
Diffstat (limited to 'net/sunrpc')
-rw-r--r-- | net/sunrpc/svc.c | 67 |
1 files changed, 25 insertions, 42 deletions
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 | */ |
1585 | char *svc_fill_symlink_pathname(struct svc_rqst *rqstp, struct kvec *first, | 1587 | char *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 | } |
1641 | EXPORT_SYMBOL_GPL(svc_fill_symlink_pathname); | 1624 | EXPORT_SYMBOL_GPL(svc_fill_symlink_pathname); |