]> git.ipfire.org Git - thirdparty/git.git/commitdiff
archive: flush deflate stream until Z_STREAM_END
authorJustin Tobler <jltobler@gmail.com>
Sat, 2 Aug 2025 22:08:03 +0000 (17:08 -0500)
committerJunio C Hamano <gitster@pobox.com>
Mon, 4 Aug 2025 20:36:35 +0000 (13:36 -0700)
In `archive-zip.c:write_zip_entry()` when using a stream as input for
deflating a file, the call to `git_deflate()` with Z_FINISH always
expects Z_STREAM_END to be returned. Per zlib documentation[1]:

        If the parameter flush is set to Z_FINISH, pending input is
        processed, pending output is flushed and deflate returns with
        Z_STREAM_END if there was enough output space. If deflate
        returns with Z_OK or Z_BUF_ERROR, this function must be called
        again with Z_FINISH and more output space (updated avail_out)
        but no more input data, until it returns with Z_STREAM_END or an
        error. After deflate has returned Z_STREAM_END, the only
        possible operations on the stream are deflateReset or
        deflateEnd.

In scenarios where the output buffer is not large enough to write all
the compressed data, it is perfectly valid for the underlying
`deflate()` to return Z_OK. Thus, expecting a single pass of `deflate()`
here to always return Z_STREAM_END is a bug. Update the code to flush
the deflate stream until Z_STREAM_END is returned.

[1]: https://zlib.net/manual.html

Helped-by: Toon Claes <toon@iotcl.com>
Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
archive-zip.c

index dbd90d9c3d4b32b3fb0628789e448931c70ac0f7..bea5bdd43dc43e3c4bbae4efc1a09e110b4898c5 100644 (file)
@@ -492,14 +492,22 @@ static int write_zip_entry(struct archiver_args *args,
 
                zstream.next_in = buf;
                zstream.avail_in = 0;
-               result = git_deflate(&zstream, Z_FINISH);
-               if (result != Z_STREAM_END)
-                       die("deflate error (%d)", result);
+
+               do {
+                       result = git_deflate(&zstream, Z_FINISH);
+                       if (result != Z_OK && result != Z_STREAM_END)
+                               die("deflate error (%d)", result);
+
+                       out_len = zstream.next_out - compressed;
+                       if (out_len > 0) {
+                               write_or_die(1, compressed, out_len);
+                               compressed_size += out_len;
+                               zstream.next_out = compressed;
+                               zstream.avail_out = sizeof(compressed);
+                       }
+               } while (result != Z_STREAM_END);
 
                git_deflate_end(&zstream);
-               out_len = zstream.next_out - compressed;
-               write_or_die(1, compressed, out_len);
-               compressed_size += out_len;
                zip_offset += compressed_size;
 
                write_zip_data_desc(size, compressed_size, crc);