@@ -1557,87 +1557,73 @@ PHPAPI zend_result _php_stream_copy_to_stream_ex(php_stream *src, php_stream *de
15571557 }
15581558
15591559#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__
15651560 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 */
16051601 * len = haveread ;
16061602 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+ }
16291622
1630- default :
1631- /* unexpected I/O error - give up, no
1632- fallback */
1633- * len = haveread ;
1634- return FAILURE ;
1623+ /* fall back to classic copying */
16351624 }
1636-
1637- /* fall back to classic copying */
16381625 }
16391626 }
1640- #endif // __FreeBSD__
16411627#endif // HAVE_COPY_FILE_RANGE
16421628
16431629 if (maxlen == PHP_STREAM_COPY_ALL ) {
0 commit comments