@@ -1558,87 +1558,73 @@ PHPAPI zend_result _php_stream_copy_to_stream_ex(php_stream *src, php_stream *de
15581558 }
15591559
15601560#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__
15661561 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 */
16061602 * len = haveread ;
16071603 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+ }
16301623
1631- default :
1632- /* unexpected I/O error - give up, no
1633- fallback */
1634- * len = haveread ;
1635- return FAILURE ;
1624+ /* fall back to classic copying */
16361625 }
1637-
1638- /* fall back to classic copying */
16391626 }
16401627 }
1641- #endif // __FreeBSD__
16421628#endif // HAVE_COPY_FILE_RANGE
16431629
16441630 if (maxlen == PHP_STREAM_COPY_ALL ) {
0 commit comments