[mod_webdav] use copy_file_range() if available

This commit is contained in:
Glenn Strauss 2019-12-29 15:10:59 -05:00
parent 3f7779d247
commit 0d62b8657b
5 changed files with 28 additions and 2 deletions

View File

@ -386,6 +386,7 @@ if 1:
'arc4random_buf',
'chroot',
'clock_gettime',
'copy_file_range',
'dup2',
'epoll_ctl',
'explicit_bzero',

View File

@ -1216,6 +1216,7 @@ AC_CHECK_FUNCS([\
arc4random_buf \
chroot \
clock_gettime \
copy_file_range \
epoll_ctl \
explicit_bzero \
explicit_memset \

View File

@ -150,6 +150,7 @@ check_type_size(off_t SIZEOF_OFF_T)
check_function_exists(arc4random_buf HAVE_ARC4RANDOM_BUF)
check_function_exists(chroot HAVE_CHROOT)
check_function_exists(copy_file_range HAVE_COPY_FILE_RANGE)
check_function_exists(epoll_ctl HAVE_EPOLL_CTL)
check_function_exists(fork HAVE_FORK)
check_function_exists(getloadavg HAVE_GETLOADAVG)

View File

@ -113,6 +113,7 @@ conf_data.set('SIZEOF_OFF_T', compiler.sizeof('off_t', args: defs))
conf_data.set('HAVE_ARC4RANDOM_BUF', compiler.has_function('arc4random_buf', args: defs))
conf_data.set('HAVE_CHROOT', compiler.has_function('chroot', args: defs))
conf_data.set('HAVE_COPY_FILE_RANGE', compiler.has_function('copy_file_range', args: defs))
conf_data.set('HAVE_EPOLL_CTL', compiler.has_function('epoll_ctl', args: defs))
conf_data.set('HAVE_FORK', compiler.has_function('fork', args: defs))
conf_data.set('HAVE_GETLOADAVG', compiler.has_function('getloadavg', args: defs))

View File

@ -4369,11 +4369,11 @@ mod_webdav_put_deprecated_unsafe_partial_put_compat (connection * const con,
const char *num = h->ptr;
off_t offset;
char *err;
if (0 != strncmp(num, "bytes", sizeof("bytes")-1)) {
if (0 != strncmp(num, "bytes ", sizeof("bytes ")-1)) {
http_status_set_error(con, 501); /* Not Implemented */
return HANDLER_FINISHED;
}
num += 5; /* +5 for "bytes" */
num += sizeof("bytes ")-1; /* +6 for "bytes " */
offset = strtoll(num, &err, 10); /*(strtoll() ignores leading whitespace)*/
if (num == err || *err != '-' || offset < 0) {
http_status_set_error(con, 501); /* Not Implemented */
@ -4387,6 +4387,27 @@ mod_webdav_put_deprecated_unsafe_partial_put_compat (connection * const con,
return HANDLER_FINISHED;
}
#ifdef HAVE_COPY_FILE_RANGE
/* use Linux copy_file_range() if available
* (Linux 4.5, but glibc 2.27 provides a user-space emulation)
* fd_in and fd_out must be on same mount (handled in mod_webdav_put_prep())
* check that reqbody is contained in single tempfile and open fd (expected)
* (Note: copying might take some time, temporarily pausing server)
*/
chunkqueue * const cq = con->request_content_queue;
chunk *c = cq->first;
off_t cqlen = chunkqueue_length(cq);
if (c->type == FILE_CHUNK && NULL == c->next && c->file.fd >= 0) {
loff_t zoff = 0;
loff_t ooff = offset;
ssize_t wr;
do {
wr = copy_file_range(c->file.fd,&zoff,fd,&ooff,(size_t)cqlen, 0);
} while (wr >= 0 && (cqlen -= wr));
}
if (0 != cqlen) /* fallback, retry if copy_file_range() did not finish */
#endif
{
if (-1 == lseek(fd, offset, SEEK_SET)) {
close(fd);
http_status_set_error(con, 500); /* Internal Server Error */
@ -4398,6 +4419,7 @@ mod_webdav_put_deprecated_unsafe_partial_put_compat (connection * const con,
* (Note: copying might take some time, temporarily pausing server)
* (error status is set if error occurs) */
mod_webdav_write_cq(con, con->request_content_queue, fd);
}
struct stat st;
if (0 != con->conf.etag_flags && !http_status_is_set(con)) {