summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS4
-rw-r--r--main/streams/php_stream_mmap.h2
-rw-r--r--main/streams/plain_wrapper.c5
-rw-r--r--main/streams/streams.c53
4 files changed, 47 insertions, 17 deletions
diff --git a/NEWS b/NEWS
index 7ad5262506..4259d4d0e8 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,10 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? 2020, PHP 7.4.12
+- Core:
+ . Fixed bug #80061 (Copying large files may have suboptimal performance).
+ (cmb)
+
- MySQLnd:
. Fixed bug #80115 (mysqlnd.debug doesn't recognize absolute paths with
slashes). (cmb)
diff --git a/main/streams/php_stream_mmap.h b/main/streams/php_stream_mmap.h
index 132f3214c1..40288cc27f 100644
--- a/main/streams/php_stream_mmap.h
+++ b/main/streams/php_stream_mmap.h
@@ -58,6 +58,8 @@ typedef struct {
#define PHP_STREAM_MMAP_ALL 0
+#define PHP_STREAM_MMAP_MAX (512 * 1024 * 1024)
+
#define php_stream_mmap_supported(stream) (_php_stream_set_option((stream), PHP_STREAM_OPTION_MMAP_API, PHP_STREAM_MMAP_SUPPORTED, NULL) == 0 ? 1 : 0)
/* Returns 1 if the stream in its current state can be memory mapped,
diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c
index 50ca925dea..298950cf9e 100644
--- a/main/streams/plain_wrapper.c
+++ b/main/streams/plain_wrapper.c
@@ -826,6 +826,11 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void
delta = (DWORD)range->offset - loffs;
}
+ /* MapViewOfFile()ing zero bytes would map to the end of the file; match *nix behavior instead */
+ if (range->length + delta == 0) {
+ return PHP_STREAM_OPTION_RETURN_ERR;
+ }
+
data->last_mapped_addr = MapViewOfFile(data->file_mapping, acc, 0, loffs, range->length + delta);
if (data->last_mapped_addr) {
diff --git a/main/streams/streams.c b/main/streams/streams.c
index f1f8bf7eab..cf411a1dd3 100644
--- a/main/streams/streams.c
+++ b/main/streams/streams.c
@@ -1573,29 +1573,48 @@ PHPAPI int _php_stream_copy_to_stream_ex(php_stream *src, php_stream *dest, size
if (php_stream_mmap_possible(src)) {
char *p;
- size_t mapped;
- p = php_stream_mmap_range(src, php_stream_tell(src), maxlen, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
+ do {
+ size_t chunk_size = (maxlen == 0 || maxlen > PHP_STREAM_MMAP_MAX) ? PHP_STREAM_MMAP_MAX : maxlen;
+ size_t mapped;
- if (p) {
- ssize_t didwrite = php_stream_write(dest, p, mapped);
- if (didwrite < 0) {
- *len = 0;
- return FAILURE;
- }
+ p = php_stream_mmap_range(src, php_stream_tell(src), chunk_size, PHP_STREAM_MAP_MODE_SHARED_READONLY, &mapped);
+
+ if (p) {
+ ssize_t didwrite;
+
+ if (php_stream_seek(src, mapped, SEEK_CUR) != 0) {
+ php_stream_mmap_unmap(src);
+ break;
+ }
+
+ didwrite = php_stream_write(dest, p, mapped);
+ if (didwrite < 0) {
+ *len = haveread;
+ return FAILURE;
+ }
- php_stream_mmap_unmap_ex(src, mapped);
+ php_stream_mmap_unmap(src);
- *len = didwrite;
+ *len = haveread += didwrite;
- /* we've got at least 1 byte to read
- * less than 1 is an error
- * AND read bytes match written */
- if (mapped > 0 && mapped == didwrite) {
- return SUCCESS;
+ /* we've got at least 1 byte to read
+ * less than 1 is an error
+ * AND read bytes match written */
+ if (mapped == 0 || mapped != didwrite) {
+ return FAILURE;
+ }
+ if (mapped < chunk_size) {
+ return SUCCESS;
+ }
+ if (maxlen != 0) {
+ maxlen -= mapped;
+ if (maxlen == 0) {
+ return SUCCESS;
+ }
+ }
}
- return FAILURE;
- }
+ } while (p);
}
while(1) {