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