diff --git a/configure.ac b/configure.ac index 63513b7d800ea..c4fcf5affa34d 100644 --- a/configure.ac +++ b/configure.ac @@ -365,6 +365,7 @@ PHP_CHECK_FUNC(htonl, socket, network) PHP_CHECK_FUNC(gethostname, nsl, network) PHP_CHECK_FUNC(gethostbyaddr, nsl, network) PHP_CHECK_FUNC(copy_file_range) +PHP_CHECK_FUNC(sendfilev, sendfile) PHP_CHECK_FUNC(dlopen, dl, root) PHP_CHECK_FUNC(dlsym, dl, root) if test "$ac_cv_func_dlopen" = "yes"; then @@ -410,6 +411,7 @@ sys/mount.h \ sys/poll.h \ sys/resource.h \ sys/select.h \ +sys/sendfile.h \ sys/socket.h \ sys/stat.h \ sys/statfs.h \ diff --git a/main/streams/streams.c b/main/streams/streams.c index c10af433904bc..72d82358dc1f2 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -29,6 +29,9 @@ #include "ext/standard/basic_functions.h" /* for BG(CurrentStatFile) */ #include "ext/standard/php_string.h" /* for php_memnstr, used by php_stream_get_record() */ #include +#if defined(HAVE_SYS_SENDFILE_H) +#include +#endif #include #include "php_streams_int.h" @@ -1575,7 +1578,7 @@ PHPAPI zend_result _php_stream_copy_to_stream_ex(php_stream *src, php_stream *de php_stream_cast(src, PHP_STREAM_AS_FD, (void*)&src_fd, 0); php_stream_cast(dest, PHP_STREAM_AS_FD, (void*)&dest_fd, 0); - /* clamp to INT_MAX to avoid EOVERFLOW */ + /* clamp to SIZE_MAX to avoid EOVERFLOW */ const size_t cfr_max = MIN(maxlen, (size_t)SSIZE_MAX); /* copy_file_range() is a Linux-specific system call @@ -1638,6 +1641,53 @@ PHPAPI zend_result _php_stream_copy_to_stream_ex(php_stream *src, php_stream *de } #endif // __FreeBSD__ #endif // HAVE_COPY_FILE_RANGE +#ifdef HAVE_SENDFILEV + if (php_stream_is(src, PHP_STREAM_IS_STDIO) && + php_stream_is(dest, PHP_STREAM_IS_STDIO) && + src->writepos == src->readpos && + dest->mode[0] == 'w' && + php_stream_can_cast(src, PHP_STREAM_AS_FD) == SUCCESS && + php_stream_can_cast(dest, PHP_STREAM_AS_FD) == SUCCESS) { + int src_fd, dest_fd; + + php_stream_cast(src, PHP_STREAM_AS_FD, (void*)&src_fd, 0); + php_stream_cast(dest, PHP_STREAM_AS_FD, (void*)&dest_fd, 0); + + /* clamp to INT_MAX to avoid EOVERFLOW */ + const size_t cfr_max = MIN(maxlen, (size_t)INT_MAX); + struct sendfilevec vec = { + .sfv_fd = src_fd, + .sfv_flag = 0, + .sfv_off = 0, + .sfv_len = cfr_max, + }; + ssize_t result = -1; + size_t nbytes = 0; + /* we use the syscall only when output is in write mode */ + do { + result = sendfilev(dest_fd, &vec, 1, &nbytes); + } while (result == -1 && errno == EAGAIN); + + if (result > 0) { + haveread += nbytes; + + src->position += nbytes; + dest->position += nbytes; + + if ((maxlen != PHP_STREAM_COPY_ALL && nbytes == maxlen) || + php_stream_eof(src)) { + *len = haveread; + return SUCCESS; + } + } else if (result == 0) { + *len = haveread; + return SUCCESS; + } else if (result < 0) { + *len = haveread; + return FAILURE; + } + } +#endif // HAVE_SENDFILEV if (maxlen == PHP_STREAM_COPY_ALL) { maxlen = 0;