1616
1717#include "php_io_internal.h"
1818#include <sys/types.h>
19+ #include <sys/stat.h>
1920#include <sys/sendfile.h>
2021#include <unistd.h>
2122#include <errno.h>
@@ -31,22 +32,33 @@ static zend_result php_io_solaris_sendfile(int src_fd, int dest_fd, size_t maxle
3132 return php_io_generic_copy_fallback (src_fd , dest_fd , maxlen , copied );
3233 }
3334
35+ /* Unlike Linux, Solaris sendfile() returns -1/EINVAL (rather than 0) when
36+ * called with an offset at or past EOF, so a trailing call issued after the
37+ * source has been fully consumed would spuriously fail an otherwise complete
38+ * copy. Bound the loop to the bytes actually available in the source file so
39+ * we stop exactly at EOF and never call sendfile() past it. */
40+ struct stat st ;
41+ if (fstat (src_fd , & st ) != 0 || !S_ISREG (st .st_mode )) {
42+ return php_io_generic_copy_fallback (src_fd , dest_fd , maxlen , copied );
43+ }
44+
45+ size_t available = (st .st_size > start_offset ) ? (size_t ) (st .st_size - start_offset ) : 0 ;
46+ size_t target = (maxlen == PHP_IO_COPY_ALL || maxlen > available ) ? available : maxlen ;
47+
3448 off_t offset = start_offset ;
3549 size_t total_copied = 0 ;
36- size_t remaining = (maxlen == PHP_IO_COPY_ALL ) ? SIZE_MAX : maxlen ;
3750 zend_result result = SUCCESS ;
3851
39- while (remaining > 0 ) {
52+ while (total_copied < target ) {
53+ size_t remaining = target - total_copied ;
4054 size_t to_send = (remaining < SSIZE_MAX ) ? remaining : SSIZE_MAX ;
4155 /* offset is updated in place by sendfile() */
4256 ssize_t sent = sendfile (dest_fd , src_fd , & offset , to_send );
4357
4458 if (sent > 0 ) {
4559 total_copied += (size_t ) sent ;
46- if (maxlen != PHP_IO_COPY_ALL ) {
47- remaining -= (size_t ) sent ;
48- }
4960 } else if (sent == 0 ) {
61+ /* Source shrank under us; stop with what we have. */
5062 break ;
5163 } else {
5264 if (errno == EINTR ) {
0 commit comments