zlib 1.2.3.5 (and 1.2.4, although not yet packaged) breaks the openttd
package due to gzeof misbehaving.

The manual states: "If gzeof() returns true, then the read functions
will return no more data, unless the end-of-file indicator is reset by
gzclearerr() and the input file has grown since the previous end of file
was detected."

However, tests have shown that even when gzeof returns true, gzread has
not returned all data yet. Actually checking the gzread function's
return value to be <= 0 and then using gzeof to figure out whether it
was an error or end-of-file works around the problem. As such I deduced
that gzread returns data after gzeof returns true for the first time.

In our case the output file is short "correct output file size" modulo
65536 bytes. As such gzeof of zlib 1.2.3.5 and 1.2.4 is unusable.

I think wireshark's behaviour can be explained in a similar way: the
seeking in the file causes zlib to read a number of bytes from the file
and then it notices the end-of-file and sets some flag so gzeof returns
0. Note that this is pure speculation based on the description of the
error and my experience with the behaviour in OpenTTD.

Note that in 1.2.3.4 it did behave like the manual states.


Attached is a simple program that shows the difference in behaviour
between 1.2.3.4 and 1.2.3.5.

g++ -lz -o gzeof_test gzeof_test.cpp      # compile the test program

dd if=/dev/zero of=test bs=100000 count=1 # create 100000 bytes file
wc test                                   # yes, it's 100000 bytes
gzip test                                 # compress that file
./gzeof_test test.gz test                 # uncompress with test program
wc test                                   # 100000 bytes with 1.2.3.4
                                          # but 16384 bytes with 1.2.3.5
gunzip test.gz                            # yes, it's 100000 bytes, i.e.
                                          # test.gz is correct


Regards,
Remko Bijker
#include <stdlib.h>
#include <stdio.h>

#include <zlib.h>

static bool GunzipFile(const char *filename_in, const char *filename_out)
{
	bool ret = true;
	FILE *ftmp = fopen(filename_in, "rb");
	gzFile fin = gzdopen(fileno(ftmp), "rb");
	FILE *fout = fopen(filename_out, "wb");

	if (fin == NULL || fout == NULL) {
		if (fin == NULL) printf("Could not open %s\n", filename_in);
		if (fout == NULL) printf("Could not open %s\n", filename_out);
		ret = false;
		goto exit;
	}

	char buff[8192];
	while (!gzeof(fin)) {
		int read = gzread(fin, buff, sizeof(buff));
		if (read < 0 || (size_t)read != fwrite(buff, 1, read, fout)) {
			printf("Reading or writing failed\n");
			ret = false;
			break;
		}
	}

exit:
	if (fin != NULL) {
		/* Closes ftmp too! */
		gzclose(fin);
	} else if (ftmp != NULL) {
		/* In case the gz stream was opened correctly this will
		 * be closed by gzclose. */
		fclose(ftmp);
	}
	if (fout != NULL) fclose(fout);

	return ret;
}

int main(int argc, char *argv[])
{
	if (argc != 3) {
		printf("usage: %s <filename_in> <filename_out>\n", argv[0]);
		return -1;
	}

	printf("Using zlib %s (compiled with %s)\n", zlibVersion(), ZLIB_VERSION);

	bool ret = GunzipFile(argv[1], argv[2]);
	printf("Extracting %s\n", ret ? "succeeded" : "failed");
	return ret;
}

Reply via email to