changeset 17328:625249382745

lib-compression: gz compression didn't handle multiple flushes correctly.
author Timo Sirainen <tss@iki.fi>
date Wed, 07 May 2014 12:27:36 +0300
parents 2fe36249c62b
children 6b40179a6868
files src/lib-compression/ostream-zlib.c
diffstat 1 files changed, 23 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-compression/ostream-zlib.c	Wed May 07 12:26:58 2014 +0300
+++ b/src/lib-compression/ostream-zlib.c	Wed May 07 12:27:36 2014 +0300
@@ -141,12 +141,18 @@
 			}
 		}
 
-		switch (deflate(zs, flush)) {
+		ret = deflate(zs, flush);
+		switch (ret) {
 		case Z_OK:
 		case Z_BUF_ERROR:
 			break;
+		case Z_STREAM_ERROR:
+			i_assert(zstream->gz);
+			i_panic("zlib.write(%s) failed: Can't write more data to .gz after flushing",
+				o_stream_get_name(&zstream->ostream.ostream));
 		default:
-			i_unreached();
+			i_panic("zlib.write(%s) failed with unexpected code %d",
+				o_stream_get_name(&zstream->ostream.ostream), ret);
 		}
 	}
 	size -= zs->avail_in;
@@ -158,12 +164,13 @@
 	return size;
 }
 
-static int o_stream_zlib_send_flush(struct zlib_ostream *zstream)
+static int
+o_stream_zlib_send_flush(struct zlib_ostream *zstream, bool final)
 {
 	z_stream *zs = &zstream->zs;
 	unsigned int len;
 	bool done = FALSE;
-	int ret;
+	int ret, flush;
 
 	if (zs->avail_in != 0) {
 		i_assert(zstream->ostream.ostream.last_failed_errno != 0);
@@ -182,6 +189,9 @@
 	if ((ret = o_stream_zlib_send_outbuf(zstream)) <= 0)
 		return ret;
 
+	flush = !zstream->gz ? Z_SYNC_FLUSH :
+		(final ? Z_FINISH : Z_NO_FLUSH);
+
 	i_assert(zstream->outbuf_used == 0);
 	do {
 		len = sizeof(zstream->outbuf) - zs->avail_out;
@@ -196,7 +206,7 @@
 				break;
 		}
 
-		switch (deflate(zs, zstream->gz ? Z_FINISH : Z_SYNC_FLUSH)) {
+		switch (deflate(zs, flush)) {
 		case Z_OK:
 		case Z_BUF_ERROR:
 			break;
@@ -208,9 +218,12 @@
 		}
 	} while (zs->avail_out != sizeof(zstream->outbuf));
 
-	if (o_stream_zlib_send_gz_trailer(zstream) < 0)
-		return -1;
-	zstream->flushed = TRUE;
+	if (final) {
+		if (o_stream_zlib_send_gz_trailer(zstream) < 0)
+			return -1;
+	}
+	if (final || flush != Z_NO_FLUSH)
+		zstream->flushed = TRUE;
 	return 0;
 }
 
@@ -219,7 +232,7 @@
 	struct zlib_ostream *zstream = (struct zlib_ostream *)stream;
 	int ret;
 
-	if (o_stream_zlib_send_flush(zstream) < 0)
+	if (o_stream_zlib_send_flush(zstream, TRUE) < 0)
 		return -1;
 
 	ret = o_stream_flush(stream->parent);
@@ -254,7 +267,7 @@
 	stream->ostream.offset += bytes;
 
 	if (!zstream->ostream.corked && i == iov_count) {
-		if (o_stream_zlib_send_flush(zstream) < 0)
+		if (o_stream_zlib_send_flush(zstream, FALSE) < 0)
 			return -1;
 	}
 	/* avail_in!=0 check is used to detect errors. if it's non-zero here