diff options
author | Christoph M. Becker <cmbecker69@gmx.de> | 2020-09-21 13:57:05 +0200 |
---|---|---|
committer | Christoph M. Becker <cmbecker69@gmx.de> | 2020-09-21 16:07:57 +0200 |
commit | 19c844594e40d79cea016b54f9ab3a367440b4c9 (patch) | |
tree | dee764e2934dae24b4f0389b64585aaa49442901 /main/streams/streams.c | |
parent | d1feeed7f33bda43f92dd2b5fba9547b4cc3fe33 (diff) | |
download | php-git-19c844594e40d79cea016b54f9ab3a367440b4c9.tar.gz |
Fix mmap copying
Instead of attempting to map large files into memory at once, we map
chunks of at most `PHP_STREAM_MMAP_MAX` bytes, and repeat that until we
hit the point where `php_stream_seek()` fails (see bug 54902), and copy
the rest of the file by reading and writing small chunks.
We also fix the mapping behavior for zero bytes on Windows, which did
not error (as with `mmap()`), but would have mapped the remaining file.
Diffstat (limited to 'main/streams/streams.c')
-rw-r--r-- | main/streams/streams.c | 53 |
1 files changed, 36 insertions, 17 deletions
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) { |