Skip to content

Commit 8275a45

Browse files
committed
Fix GH-10548: copy() fails on cifs mounts because of incorrect length (cfr_max) specified in streams.c:1584 copy_file_range()
On some filesystems, the copy operation fails if we specify a size larger than the file size. We use a stat call to clamp the size to copy to the actual filesize. This stat call shouldn't impact performance notably because stat calls can be cached. In some cases (like for /proc files), the returned size is 0, so we should avoid problems by not using copy_file_range in those cases (copy_file_range wouldn't work anyway on this particular example because the syscall is not supported for /proc).
1 parent a347a66 commit 8275a45

File tree

1 file changed

+11
-4
lines changed

1 file changed

+11
-4
lines changed

main/streams/streams.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,14 +1567,21 @@ PHPAPI zend_result _php_stream_copy_to_stream_ex(php_stream *src, php_stream *de
15671567
/* get dest open flags to check if the stream is open in append mode */
15681568
php_stream_parse_fopen_modes(dest->mode, &dest_open_flags);
15691569

1570+
/* Some filesystems will cause failures if the max length is greater than the file length.
1571+
* We therefore use a stat call to get the actual file size. Since stat calls are cached
1572+
* this shouldn't have much impact. */
1573+
php_stream_statbuf statbuf;
1574+
15701575
/* copy_file_range does not work with O_APPEND */
15711576
if (php_stream_cast(src, PHP_STREAM_AS_FD, (void*)&src_fd, 0) == SUCCESS &&
15721577
php_stream_cast(dest, PHP_STREAM_AS_FD, (void*)&dest_fd, 0) == SUCCESS &&
15731578
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);
1579+
!(dest_open_flags & O_APPEND) &&
1580+
php_stream_stat(src, &statbuf) == SUCCESS &&
1581+
/* Some files (such as /proc files) report a size of zero, avoid problems */
1582+
statbuf.sb.st_size > 0) {
1583+
/* clamp to INT_MAX to avoid EOVERFLOW, and to the filesize to avoid failure on some filesystems */
1584+
const size_t cfr_max = MIN(MIN(maxlen, (size_t)SSIZE_MAX), statbuf.sb.st_size);
15781585

15791586
/* copy_file_range() is a Linux-specific system call which allows efficient copying
15801587
* between two file descriptors, eliminating the need to transfer data from the kernel

0 commit comments

Comments
 (0)