diff options
author | Christoph M. Becker <cmbecker69@gmx.de> | 2020-08-19 18:19:08 +0200 |
---|---|---|
committer | Christoph M. Becker <cmbecker69@gmx.de> | 2020-12-08 12:23:24 +0100 |
commit | 20e75329f2adb11dd231852c061926d0e4080929 (patch) | |
tree | e4a41a2393d16ba7f74ecd5c8810a09a59580f65 /ext | |
parent | 65f5573bc82108bbaf2727ffa11575f3292d736f (diff) | |
download | php-git-20e75329f2adb11dd231852c061926d0e4080929.tar.gz |
Fix #48725: Support for flushing in zlib stream
When `php_zlib_deflate_filter()` is called with `PSFS_FLAG_FLUSH_INC`
but without new buckets being available (e.g. because a user calls
`rewind()` after writing to the stream), we have to make sure that any
pending data are flushed. This could basically be done like in the
attached patch[1], but that could cause unnessary flushes, which can be
harmful for compression, and adds unnecessary flush markers to the
stream. Thus, we use the `php_zlib_filter_data.finished` field, which
has not been used for `zlib.deflate` filters, and properly keep track
of the need to flush.
[1] <https://bugs.php.net/patch-display.php?bug_id=48725&patch=zlib-filter-flush-fix.patch&revision=latest>
Closes GH-6019.
Diffstat (limited to 'ext')
-rw-r--r-- | ext/zlib/tests/bug48725.phpt | 24 | ||||
-rw-r--r-- | ext/zlib/zlib_filter.c | 14 |
2 files changed, 34 insertions, 4 deletions
diff --git a/ext/zlib/tests/bug48725.phpt b/ext/zlib/tests/bug48725.phpt new file mode 100644 index 0000000000..0d04887a2d --- /dev/null +++ b/ext/zlib/tests/bug48725.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #48725 (Support for flushing in zlib stream) +--SKIPIF-- +<?php +if (!extension_loaded('zlib')) die('skip zip extension not available'); +?> +--FILE-- +<?php +$text = str_repeat('0123456789abcdef', 1000); + +$temp = fopen('php://temp', 'r+'); +stream_filter_append($temp, 'zlib.deflate', STREAM_FILTER_WRITE); +fwrite($temp, $text); + +rewind($temp); + +var_dump(bin2hex(stream_get_contents($temp))); +var_dump(ftell($temp)); + +fclose($temp); +?> +--EXPECT-- +string(138) "ecc7c901c0100000b09594bac641d97f840e22f9253c31bdb9d4d6c75cdf3ec1ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddaffc0f0000ffff" +int(69) diff --git a/ext/zlib/zlib_filter.c b/ext/zlib/zlib_filter.c index 3c19d38016..988ed603ca 100644 --- a/ext/zlib/zlib_filter.c +++ b/ext/zlib/zlib_filter.c @@ -29,7 +29,7 @@ typedef struct _php_zlib_filter_data { unsigned char *outbuf; size_t outbuf_len; int persistent; - zend_bool finished; + zend_bool finished; /* for zlib.deflate: signals that no flush is pending */ } php_zlib_filter_data; /* }}} */ @@ -196,6 +196,8 @@ static php_stream_filter_status_t php_zlib_deflate_filter( bucket = php_stream_bucket_make_writeable(bucket); while (bin < (unsigned int) bucket->buflen) { + int flush_mode; + desired = bucket->buflen - bin; if (desired > data->inbuf_len) { desired = data->inbuf_len; @@ -203,7 +205,9 @@ static php_stream_filter_status_t php_zlib_deflate_filter( memcpy(data->strm.next_in, bucket->buf + bin, desired); data->strm.avail_in = desired; - status = deflate(&(data->strm), flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FULL_FLUSH : (flags & PSFS_FLAG_FLUSH_INC ? Z_SYNC_FLUSH : Z_NO_FLUSH)); + flush_mode = flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FULL_FLUSH : (flags & PSFS_FLAG_FLUSH_INC ? Z_SYNC_FLUSH : Z_NO_FLUSH); + data->finished = flush_mode != Z_NO_FLUSH; + status = deflate(&(data->strm), flush_mode); if (status != Z_OK) { /* Something bad happened */ php_stream_bucket_delref(bucket); @@ -230,11 +234,12 @@ static php_stream_filter_status_t php_zlib_deflate_filter( php_stream_bucket_delref(bucket); } - if (flags & PSFS_FLAG_FLUSH_CLOSE) { + if (flags & PSFS_FLAG_FLUSH_CLOSE || ((flags & PSFS_FLAG_FLUSH_INC) && !data->finished)) { /* Spit it out! */ status = Z_OK; while (status == Z_OK) { - status = deflate(&(data->strm), Z_FINISH); + status = deflate(&(data->strm), (flags & PSFS_FLAG_FLUSH_CLOSE ? Z_FINISH : Z_SYNC_FLUSH)); + data->finished = 1; if (data->strm.avail_out < data->outbuf_len) { size_t bucketlen = data->outbuf_len - data->strm.avail_out; @@ -395,6 +400,7 @@ factory_setlevel: } } status = deflateInit2(&(data->strm), level, Z_DEFLATED, windowBits, memLevel, 0); + data->finished = 1; fops = &php_zlib_deflate_ops; } else { status = Z_DATA_ERROR; |