@@ -1555,6 +1555,90 @@ PHPAPI zend_result _php_stream_copy_to_stream_ex(php_stream *src, php_stream *de
1555
1555
return SUCCESS ;
1556
1556
}
1557
1557
1558
+ #ifdef HAVE_COPY_FILE_RANGE
1559
+
1560
+ /* TODO: on FreeBSD, copy_file_range() works only with the
1561
+ undocumented flag 0x01000000; until the problem is fixed
1562
+ properly, copy_file_range() is not used on FreeBSD */
1563
+ #ifndef __FreeBSD__
1564
+ if (php_stream_is (src , PHP_STREAM_IS_STDIO ) &&
1565
+ php_stream_is (dest , PHP_STREAM_IS_STDIO ) &&
1566
+ src -> writepos == src -> readpos &&
1567
+ php_stream_can_cast (src , PHP_STREAM_AS_FD ) == SUCCESS &&
1568
+ php_stream_can_cast (dest , PHP_STREAM_AS_FD ) == SUCCESS ) {
1569
+ /* both php_stream instances are backed by a file
1570
+ descriptor, are not filtered and the read buffer is
1571
+ empty: we can use copy_file_range() */
1572
+
1573
+ int src_fd , dest_fd ;
1574
+
1575
+ php_stream_cast (src , PHP_STREAM_AS_FD , (void * )& src_fd , 0 );
1576
+ php_stream_cast (dest , PHP_STREAM_AS_FD , (void * )& dest_fd , 0 );
1577
+
1578
+ /* clamp to INT_MAX to avoid EOVERFLOW */
1579
+ const size_t cfr_max = MIN (maxlen , (size_t )SSIZE_MAX );
1580
+
1581
+ /* copy_file_range() is a Linux-specific system call
1582
+ which allows efficient copying between two file
1583
+ descriptors, eliminating the need to transfer data
1584
+ from the kernel to userspace and back. For
1585
+ networking file systems like NFS and Ceph, it even
1586
+ eliminates copying data to the client, and local
1587
+ filesystems like Btrfs and XFS can create shared
1588
+ extents. */
1589
+
1590
+ ssize_t result = copy_file_range (src_fd , NULL ,
1591
+ dest_fd , NULL ,
1592
+ cfr_max , 0 );
1593
+ if (result > 0 ) {
1594
+ size_t nbytes = (size_t )result ;
1595
+ haveread += nbytes ;
1596
+
1597
+ src -> position += nbytes ;
1598
+ dest -> position += nbytes ;
1599
+
1600
+ if ((maxlen != PHP_STREAM_COPY_ALL && nbytes == maxlen ) ||
1601
+ php_stream_eof (src )) {
1602
+ /* the whole request was satisfied or
1603
+ end-of-file reached - done */
1604
+ * len = haveread ;
1605
+ return SUCCESS ;
1606
+ }
1607
+
1608
+ /* there may be more data; continue copying
1609
+ using the fallback code below */
1610
+ } else if (result == 0 ) {
1611
+ /* end of file */
1612
+ * len = haveread ;
1613
+ return SUCCESS ;
1614
+ } else if (result < 0 ) {
1615
+ switch (errno ) {
1616
+ case EINVAL :
1617
+ /* some formal error, e.g. overlapping
1618
+ file ranges */
1619
+ break ;
1620
+
1621
+ case EXDEV :
1622
+ /* pre Linux 5.3 error */
1623
+ break ;
1624
+
1625
+ case ENOSYS :
1626
+ /* not implemented by this Linux kernel */
1627
+ break ;
1628
+
1629
+ default :
1630
+ /* unexpected I/O error - give up, no
1631
+ fallback */
1632
+ * len = haveread ;
1633
+ return FAILURE ;
1634
+ }
1635
+
1636
+ /* fall back to classic copying */
1637
+ }
1638
+ }
1639
+ #endif // __FreeBSD__
1640
+ #endif // HAVE_COPY_FILE_RANGE
1641
+
1558
1642
if (maxlen == PHP_STREAM_COPY_ALL ) {
1559
1643
maxlen = 0 ;
1560
1644
}
0 commit comments