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