1 /* minigzip.c -- simulate gzip using the zlib compression library
2 * Copyright (C) 1995-2006, 2010, 2011, 2016 Jean-loup Gailly
3 * For conditions of distribution and use, see copyright notice in zlib.h
4 */
5
6 /*
7 * minigzip is a minimal implementation of the gzip utility. This is
8 * only an example of using zlib and isn't meant to replace the
9 * full-featured gzip. No attempt is made to deal with file systems
10 * limiting names to 14 or 8+3 characters, etc... Error checking is
11 * very limited. So use minigzip only for testing; use gzip for the
12 * real thing.
13 */
14
15 /* @(#) $Id$ */
16
17 #include "zlib.h"
18 #include <stdio.h>
19 #include <assert.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <inttypes.h>
23
24 #ifdef USE_MMAP
25 # include <sys/types.h>
26 # include <sys/mman.h>
27 # include <sys/stat.h>
28 #endif
29
30 #ifndef UNALIGNED_OK
31 # include <malloc.h>
32 #endif
33
34 #if defined(WIN32) || defined(__CYGWIN__)
35 # include <fcntl.h>
36 # include <io.h>
37 # define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
38 #else
39 # define SET_BINARY_MODE(file)
40 #endif
41
42 #if defined(_MSC_VER) && _MSC_VER < 1900
43 # define snprintf _snprintf
44 #endif
45
46 #if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE)
47 #ifndef WIN32 /* unlink already in stdio.h for WIN32 */
48 extern int unlink (const char *);
49 #endif
50 #endif
51
52 #ifndef GZ_SUFFIX
53 # define GZ_SUFFIX ".gz"
54 #endif
55 #define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1)
56
57 #define BUFLEN 16384 /* read buffer size */
58 #define BUFLENW (BUFLEN * 3) /* write buffer size */
59 #define MAX_NAME_LEN 1024
60
61 #ifdef Z_SOLO
62 /* for Z_SOLO, create simplified gz* functions using deflate and inflate */
63
64 #if defined(Z_HAVE_UNISTD_H) || defined(Z_LARGE)
65 # include <unistd.h> /* for unlink() */
66 #endif
67
68 void *myalloc (void *, unsigned, unsigned);
69 void myfree (void *, void *);
70
myalloc(void * q,unsigned n,unsigned m)71 void *myalloc(void *q, unsigned n, unsigned m)
72 {
73 (void)q;
74 #ifndef UNALIGNED_OK
75 return memalign(16, n * m);
76 #else
77 return calloc(n, m);
78 #endif
79 }
80
myfree(void * q,void * p)81 void myfree(void *q, void *p)
82 {
83 (void)q;
84 free(p);
85 }
86
87 typedef struct gzFile_s {
88 FILE *file;
89 int write;
90 int err;
91 const char *msg;
92 z_stream strm;
93 unsigned char *buf;
94 } *gzFile;
95
96 gzFile gzopen(const char *, const char *);
97 gzFile gzdopen(int, const char *);
98 gzFile gz_open (const char *, int, const char *);
99
gzopen(const char * path,const char * mode)100 gzFile gzopen(const char *path, const char *mode)
101 {
102 return gz_open(path, -1, mode);
103 }
104
gzdopen(int fd,const char * mode)105 gzFile gzdopen(int fd, const char *mode)
106 {
107 return gz_open(NULL, fd, mode);
108 }
109
gz_open(const char * path,int fd,const char * mode)110 gzFile gz_open(const char *path, int fd, const char *mode)
111 {
112 gzFile gz;
113 int ret;
114 int level = Z_DEFAULT_COMPRESSION;
115 const char *plevel = mode;
116
117 gz = malloc(sizeof(struct gzFile_s));
118 if (gz == NULL)
119 return NULL;
120 gz->write = strchr(mode, 'w') != NULL;
121 gz->strm.zalloc = myalloc;
122 gz->strm.zfree = myfree;
123 gz->strm.opaque = NULL;
124 gz->buf = malloc(gz->write ? BUFLENW : BUFLEN);
125
126 if (gz->buf == NULL) {
127 free(gz);
128 return NULL;
129 }
130
131 while (*plevel) {
132 if (*plevel >= '0' && *plevel <= '9') {
133 level = *plevel - '0';
134 break;
135 }
136 plevel++;
137 }
138 if (gz->write)
139 ret = deflateInit2(&(gz->strm), level, 8, 15 + 16, 8, 0);
140 else {
141 gz->strm.next_in = NULL;
142 gz->strm.avail_in = 0;
143 ret = inflateInit2(&(gz->strm), 15 + 16);
144 }
145 if (ret != Z_OK) {
146 free(gz);
147 return NULL;
148 }
149 gz->file = path == NULL ? fdopen(fd, gz->write ? "wb" : "rb") :
150 fopen(path, gz->write ? "wb" : "rb");
151 if (gz->file == NULL) {
152 gz->write ? deflateEnd(&(gz->strm)) : inflateEnd(&(gz->strm));
153 free(gz);
154 return NULL;
155 }
156 gz->err = 0;
157 gz->msg = "";
158 return gz;
159 }
160
161 int gzwrite(gzFile, const void *, unsigned);
162
gzwrite(gzFile gz,const void * buf,unsigned len)163 int gzwrite(gzFile gz, const void *buf, unsigned len)
164 {
165 z_stream *strm;
166
167 if (gz == NULL || !gz->write)
168 return 0;
169 strm = &(gz->strm);
170 strm->next_in = (void *)buf;
171 strm->avail_in = len;
172 do {
173 strm->next_out = gz->buf;
174 strm->avail_out = BUFLENW;
175 (void)deflate(strm, Z_NO_FLUSH);
176 fwrite(gz->buf, 1, BUFLENW - strm->avail_out, gz->file);
177 } while (strm->avail_out == 0);
178 return len;
179 }
180
181 int gzread(gzFile, void *, unsigned);
182
gzread(gzFile gz,void * buf,unsigned len)183 int gzread(gzFile gz, void *buf, unsigned len)
184 {
185 z_stream *strm;
186
187 if (gz == NULL || gz->write || gz->err)
188 return 0;
189 strm = &(gz->strm);
190 strm->next_out = buf;
191 strm->avail_out = len;
192 do {
193 if (strm->avail_in == 0)
194 {
195 strm->next_in = gz->buf;
196 strm->avail_in = (uint32_t)fread(gz->buf, 1, BUFLEN, gz->file);
197 }
198 if (strm->avail_in > 0)
199 {
200 int ret = inflate(strm, Z_NO_FLUSH);
201 if (ret == Z_DATA_ERROR) {
202 gz->err = ret;
203 gz->msg = strm->msg;
204 return 0;
205 }
206 else if (ret == Z_STREAM_END)
207 inflateReset(strm);
208 }
209 else
210 break;
211 } while (strm->avail_out);
212 return len - strm->avail_out;
213 }
214
215 int gzclose(gzFile);
216
gzclose(gzFile gz)217 int gzclose(gzFile gz)
218 {
219 z_stream *strm;
220
221 if (gz == NULL)
222 return Z_STREAM_ERROR;
223 strm = &(gz->strm);
224 if (gz->write) {
225 strm->next_in = NULL;
226 strm->avail_in = 0;
227 do {
228 strm->next_out = gz->buf;
229 strm->avail_out = BUFLENW;
230 (void)deflate(strm, Z_FINISH);
231 fwrite(gz->buf, 1, BUFLENW - strm->avail_out, gz->file);
232 } while (strm->avail_out == 0);
233 deflateEnd(strm);
234 }
235 else
236 inflateEnd(strm);
237 free(gz->buf);
238 fclose(gz->file);
239 free(gz);
240 return Z_OK;
241 }
242
243 const char *gzerror(gzFile, int *);
244
gzerror(gzFile gz,int * err)245 const char *gzerror(gzFile gz, int *err)
246 {
247 *err = gz->err;
248 return gz->msg;
249 }
250
251 #endif
252
253 static char *prog;
254
255 void error (const char *msg);
256 void gz_compress (FILE *in, gzFile out);
257 #ifdef USE_MMAP
258 int gz_compress_mmap (FILE *in, gzFile out);
259 #endif
260 void gz_uncompress (gzFile in, FILE *out);
261 void file_compress (char *file, char *mode);
262 void file_uncompress (char *file);
263 int main (int argc, char *argv[]);
264
265 /* ===========================================================================
266 * Display error message and exit
267 */
error(const char * msg)268 void error(const char *msg)
269 {
270 fprintf(stderr, "%s: %s\n", prog, msg);
271 exit(1);
272 }
273
274 /* ===========================================================================
275 * Compress input to output then close both files.
276 */
277
gz_compress(FILE * in,gzFile out)278 void gz_compress(FILE *in, gzFile out)
279 {
280 char buf[BUFLEN];
281 int len;
282 int err;
283
284 #ifdef USE_MMAP
285 /* Try first compressing with mmap. If mmap fails (minigzip used in a
286 * pipe), use the normal fread loop.
287 */
288 if (gz_compress_mmap(in, out) == Z_OK) return;
289 #endif
290 /* Clear out the contents of buf before reading from the file to avoid
291 MemorySanitizer: use-of-uninitialized-value warnings. */
292 memset(buf, 0, sizeof(buf));
293 for (;;) {
294 len = (int)fread(buf, 1, sizeof(buf), in);
295 if (ferror(in)) {
296 perror("fread");
297 exit(1);
298 }
299 if (len == 0) break;
300
301 if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err));
302 }
303 fclose(in);
304 if (gzclose(out) != Z_OK) error("failed gzclose");
305 }
306
307 #ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */
308
309 /* Try compressing the input file at once using mmap. Return Z_OK if
310 * if success, Z_ERRNO otherwise.
311 */
gz_compress_mmap(FILE * in,gzFile out)312 int gz_compress_mmap(FILE *in, gzFile out)
313 {
314 int len;
315 int err;
316 int ifd = fileno(in);
317 caddr_t buf; /* mmap'ed buffer for the entire input file */
318 off_t buf_len; /* length of the input file */
319 struct stat sb;
320
321 /* Determine the size of the file, needed for mmap: */
322 if (fstat(ifd, &sb) < 0) return Z_ERRNO;
323 buf_len = sb.st_size;
324 if (buf_len <= 0) return Z_ERRNO;
325
326 /* Now do the actual mmap: */
327 buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0);
328 if (buf == (caddr_t)(-1)) return Z_ERRNO;
329
330 /* Compress the whole file at once: */
331 len = gzwrite(out, (char *)buf, (unsigned)buf_len);
332
333 if (len != (int)buf_len) error(gzerror(out, &err));
334
335 munmap(buf, buf_len);
336 fclose(in);
337 if (gzclose(out) != Z_OK) error("failed gzclose");
338 return Z_OK;
339 }
340 #endif /* USE_MMAP */
341
342 /* ===========================================================================
343 * Uncompress input to output then close both files.
344 */
gz_uncompress(gzFile in,FILE * out)345 void gz_uncompress(gzFile in, FILE *out)
346 {
347 char buf[BUFLENW];
348 int len;
349 int err;
350
351 for (;;) {
352 len = gzread(in, buf, sizeof(buf));
353 if (len < 0) error (gzerror(in, &err));
354 if (len == 0) break;
355
356 if ((int)fwrite(buf, 1, (unsigned)len, out) != len) {
357 error("failed fwrite");
358 }
359 }
360 if (fclose(out)) error("failed fclose");
361
362 if (gzclose(in) != Z_OK) error("failed gzclose");
363 }
364
365
366 /* ===========================================================================
367 * Compress the given file: create a corresponding .gz file and remove the
368 * original.
369 */
file_compress(char * file,char * mode)370 void file_compress(char *file, char *mode)
371 {
372 char outfile[MAX_NAME_LEN];
373 FILE *in;
374 gzFile out;
375
376 if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) {
377 fprintf(stderr, "%s: filename too long\n", prog);
378 exit(1);
379 }
380
381 snprintf(outfile, sizeof(outfile), "%s%s", file, GZ_SUFFIX);
382
383 in = fopen(file, "rb");
384 if (in == NULL) {
385 perror(file);
386 exit(1);
387 }
388 out = gzopen(outfile, mode);
389 if (out == NULL) {
390 fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile);
391 exit(1);
392 }
393 gz_compress(in, out);
394
395 unlink(file);
396 }
397
398
399 /* ===========================================================================
400 * Uncompress the given file and remove the original.
401 */
file_uncompress(char * file)402 void file_uncompress(char *file)
403 {
404 char buf[MAX_NAME_LEN];
405 char *infile, *outfile;
406 FILE *out;
407 gzFile in;
408 size_t len = strlen(file);
409
410 if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) {
411 fprintf(stderr, "%s: filename too long\n", prog);
412 exit(1);
413 }
414
415 snprintf(buf, sizeof(buf), "%s", file);
416
417 if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) {
418 infile = file;
419 outfile = buf;
420 outfile[len-3] = '\0';
421 } else {
422 outfile = file;
423 infile = buf;
424 snprintf(buf + len, sizeof(buf) - len, "%s", GZ_SUFFIX);
425 }
426 in = gzopen(infile, "rb");
427 if (in == NULL) {
428 fprintf(stderr, "%s: can't gzopen %s\n", prog, infile);
429 exit(1);
430 }
431 out = fopen(outfile, "wb");
432 if (out == NULL) {
433 perror(file);
434 exit(1);
435 }
436
437 gz_uncompress(in, out);
438
439 unlink(infile);
440 }
441
LLVMFuzzerTestOneInput(const uint8_t * data,size_t dataLen)442 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataLen) {
443 char *inFileName = "/tmp/minigzip_fuzzer.out";
444 char *outFileName = "/tmp/minigzip_fuzzer.out.gz";
445 char outmode[20];
446 FILE *in;
447 char buf[BUFLEN];
448 uint32_t offset = 0;
449
450 /* Discard inputs larger than 1Mb. */
451 static size_t kMaxSize = 1024 * 1024;
452 if (dataLen < 1 || dataLen > kMaxSize)
453 return 0;
454
455 in = fopen(inFileName, "w");
456 if (fwrite(data, 1, (unsigned)dataLen, in) != dataLen)
457 error("failed fwrite");
458 if (fclose(in))
459 error("failed fclose");
460
461 memset(outmode, 0, sizeof(outmode));
462 snprintf(outmode, sizeof(outmode), "%s", "wb");
463
464 /* Compression level: [0..9]. */
465 outmode[2] = data[0] % 10;
466
467 switch (data[0] % 4) {
468 default:
469 case 0:
470 outmode[3] = 0;
471 break;
472 case 1:
473 /* compress with Z_FILTERED */
474 outmode[3] = 'f';
475 break;
476 case 2:
477 /* compress with Z_HUFFMAN_ONLY */
478 outmode[3] = 'h';
479 break;
480 case 3:
481 /* compress with Z_RLE */
482 outmode[3] = 'R';
483 break;
484 }
485
486 file_compress(inFileName, outmode);
487 file_uncompress(outFileName);
488
489 /* Check that the uncompressed file matches the input data. */
490 in = fopen(inFileName, "rb");
491 if (in == NULL) {
492 perror(inFileName);
493 exit(1);
494 }
495
496 memset(buf, 0, sizeof(buf));
497 for (;;) {
498 int len = (int)fread(buf, 1, sizeof(buf), in);
499 if (ferror(in)) {
500 perror("fread");
501 exit(1);
502 }
503 if (len == 0)
504 break;
505 assert(0 == memcmp(data + offset, buf, len));
506 offset += len;
507 }
508
509 if (fclose(in))
510 error("failed fclose");
511
512 /* This function must return 0. */
513 return 0;
514 }
515