Coverage Report

Created: 2026-10-17 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/zlib-ng/gzlib.c
Line
Count
Source
1
/* gzlib.c -- zlib functions common to reading and writing gzip files
2
 * Copyright (C) 2004-2026 Mark Adler
3
 * For conditions of distribution and use, see copyright notice in zlib.h
4
 */
5
6
#include "zbuild.h"
7
#include "zutil.h"
8
#include "zutil_p.h"
9
#include "gzguts.h"
10
11
#if defined(_WIN32)
12
#  define LSEEK _lseeki64
13
#else
14
#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0
15
11.7k
#  define LSEEK lseek64
16
#else
17
#  define LSEEK lseek
18
#endif
19
#endif
20
21
/* Local functions */
22
static gzFile gz_state_init(void);
23
static void gz_reset(gz_state *);
24
static gzFile gz_open(const void *, int, const char *);
25
26
/* Initialize struct for gzFile state */
27
23.4k
static gzFile gz_state_init(void) {
28
    /* allocate gzFile structure to return */
29
23.4k
    gz_state *state = (gz_state *)zng_alloc(sizeof(gz_state));
30
23.4k
    if (state == NULL)
31
0
        return NULL;
32
33
23.4k
    state->strm.zalloc = NULL;
34
23.4k
    state->strm.zfree = NULL;
35
23.4k
    state->strm.opaque = NULL;
36
23.4k
    state->strm.next_in = NULL;
37
38
23.4k
    state->size = 0;
39
23.4k
    state->want = GZBUFSIZE;
40
23.4k
    state->in = NULL;
41
23.4k
    state->out = NULL;
42
23.4k
    state->direct = 0;
43
23.4k
    state->mode = GZ_NONE;
44
23.4k
    state->level = Z_DEFAULT_COMPRESSION;
45
23.4k
    state->strategy = Z_DEFAULT_STRATEGY;
46
23.4k
    state->msg = NULL;
47
48
23.4k
    state->past = 0;
49
23.4k
    state->skip = 0;
50
51
23.4k
    return (gzFile)state;
52
23.4k
}
53
54
11.7k
void Z_INTERNAL gz_state_free(gz_state *state) {
55
11.7k
    zng_free(state);
56
11.7k
}
57
58
/* Reset gzip file state */
59
23.4k
static void gz_reset(gz_state *state) {
60
23.4k
    state->x.have = 0;              /* no output data available */
61
23.4k
    if (state->mode == GZ_READ) {   /* for reading ... */
62
11.7k
        state->eof = 0;             /* not at end of file */
63
11.7k
        state->past = 0;            /* have not read past end yet */
64
11.7k
        state->how = LOOK;          /* look for gzip header */
65
11.7k
    }
66
11.7k
    else                            /* for writing ... */
67
11.7k
        state->reset = 0;           /* no deflateReset pending */
68
23.4k
    state->skip = 0;                /* no seek request pending */
69
23.4k
    PREFIX(gz_error)(state, Z_OK, NULL);    /* clear error */
70
23.4k
    state->x.pos = 0;               /* no uncompressed data yet */
71
23.4k
    state->strm.avail_in = 0;       /* no input data yet */
72
23.4k
}
73
74
/* Allocate in/out buffers for gzFile */
75
23.4k
int Z_INTERNAL gz_buffer_alloc(gz_state *state) {
76
23.4k
    unsigned want = state->want;
77
23.4k
    unsigned in_size = want, out_size = want;
78
79
23.4k
    if (state->mode == GZ_WRITE) {
80
11.7k
        in_size = want * 2; // double input buffer for compression (ref: gzprintf)
81
11.7k
        if (state->direct)
82
11
            out_size = 0; // output buffer not needed in write + direct mode
83
11.7k
    } else if (state->mode == GZ_READ) {
84
11.7k
        out_size = want * 2;  // double output buffer for decompression
85
11.7k
    }
86
87
23.4k
    state->buffers = (unsigned char *)zng_alloc_aligned((in_size + out_size), 64);
88
23.4k
    state->in = state->buffers;
89
23.4k
    if (out_size) {
90
23.4k
        state->out = state->buffers + (in_size); // Outbuffer goes after inbuffer
91
23.4k
    }
92
93
    /* Return error if memory allocation failed */
94
23.4k
    if (state->in == NULL || (out_size && state->out == NULL)) {
95
0
        gz_buffer_free(state);
96
0
        PREFIX(gz_error)(state, Z_MEM_ERROR, "out of memory");
97
0
        return -1;
98
0
    }
99
100
23.4k
    state->size = want; // mark state as initialized
101
23.4k
    return 0;
102
23.4k
}
103
104
23.4k
void Z_INTERNAL gz_buffer_free(gz_state *state) {
105
23.4k
    zng_free_aligned(state->buffers);
106
23.4k
    state->buffers = NULL;
107
23.4k
    state->out = NULL;
108
23.4k
    state->in = NULL;
109
23.4k
    state->size = 0;
110
23.4k
}
111
112
/* Open a gzip file either by name or file descriptor. */
113
23.4k
static gzFile gz_open(const void *path, int fd, const char *mode) {
114
23.4k
    gz_state *state;
115
23.4k
    size_t len;
116
23.4k
    int oflag;
117
23.4k
#ifdef O_CLOEXEC
118
23.4k
    int cloexec = 0;
119
23.4k
#endif
120
23.4k
#ifdef O_EXCL
121
23.4k
    int exclusive = 0;
122
23.4k
#endif
123
124
    /* check input */
125
23.4k
    if (path == NULL || mode == NULL)
126
0
        return NULL;
127
128
    /* Initialize gzFile state */
129
23.4k
    state = (gz_state *)gz_state_init();
130
23.4k
    if (state == NULL)
131
0
        return NULL;
132
133
    /* interpret mode */
134
90.4k
    while (*mode) {
135
66.9k
        if (*mode >= '0' && *mode <= '9') {
136
11.7k
            state->level = *mode - '0';
137
55.2k
        } else {
138
55.2k
            switch (*mode) {
139
11.7k
            case 'r':
140
11.7k
                state->mode = GZ_READ;
141
11.7k
                break;
142
0
#ifndef NO_GZCOMPRESS
143
11.7k
            case 'w':
144
11.7k
                state->mode = GZ_WRITE;
145
11.7k
                break;
146
0
            case 'a':
147
0
                state->mode = GZ_APPEND;
148
0
                break;
149
0
#endif
150
0
            case '+':       /* can't read and write at the same time */
151
0
                gz_state_free(state);
152
0
                return NULL;
153
23.4k
            case 'b':       /* ignore -- will request binary anyway */
154
23.4k
                break;
155
0
#ifdef O_CLOEXEC
156
0
            case 'e':
157
0
                cloexec = 1;
158
0
                break;
159
0
#endif
160
0
#ifdef O_EXCL
161
0
            case 'x':
162
0
                exclusive = 1;
163
0
                break;
164
0
#endif
165
2.16k
            case 'f':
166
2.16k
                state->strategy = Z_FILTERED;
167
2.16k
                break;
168
864
            case 'h':
169
864
                state->strategy = Z_HUFFMAN_ONLY;
170
864
                break;
171
4.04k
            case 'R':
172
4.04k
                state->strategy = Z_RLE;
173
4.04k
                break;
174
1.17k
            case 'F':
175
1.17k
                state->strategy = Z_FIXED;
176
1.17k
                break;
177
0
            case 'G':
178
0
                state->direct = -1;
179
0
                break;
180
11
            case 'T':
181
11
                state->direct = 1;
182
11
                break;
183
0
            default:        /* could consider as an error, but just ignore */
184
0
                {}
185
55.2k
            }
186
55.2k
        }
187
66.9k
        mode++;
188
66.9k
    }
189
190
    /* must provide an "r", "w", or "a" */
191
23.4k
    if (state->mode == GZ_NONE) {
192
0
        gz_state_free(state);
193
0
        return NULL;
194
0
    }
195
196
    /* direct is 0, 1 if "T", or -1 if "G" (last "G" or "T" wins) */
197
23.4k
    if (state->mode == GZ_READ) {
198
11.7k
        if (state->direct == 1) {
199
            /* can't force a transparent read */
200
0
            gz_state_free(state);
201
0
            return NULL;
202
0
        }
203
11.7k
        if (state->direct == 0)
204
            /* default when reading is auto-detect of gzip vs. transparent --
205
               start with a transparent assumption in case of an empty file */
206
11.7k
            state->direct = 1;
207
11.7k
    }
208
11.7k
    else if (state->direct == -1) {
209
        /* "G" has no meaning when writing -- disallow it */
210
0
        gz_state_free(state);
211
0
        return NULL;
212
0
    }
213
    /* if reading, direct == 1 for auto-detect, -1 for gzip only; if writing or
214
       appending, direct == 0 for gzip, 1 for transparent (copy in to out) */
215
216
    /* save the path name for error messages */
217
#ifdef WIDECHAR
218
    if (fd == -2) {
219
        len = wcstombs(NULL, (const wchar_t *)path, 0);
220
        if (len == (size_t)-1)
221
            len = 0;
222
    } else
223
#endif
224
23.4k
        len = strlen((const char *)path);
225
23.4k
    state->path = malloc(len + 1);
226
23.4k
    if (state->path == NULL) {
227
0
        gz_state_free(state);
228
0
        return NULL;
229
0
    }
230
#ifdef WIDECHAR
231
    if (fd == -2)
232
        if (len) {
233
            wcstombs(state->path, (const wchar_t *)path, len + 1);
234
        } else {
235
            *(state->path) = 0;
236
        }
237
    else
238
#endif
239
23.4k
        (void)snprintf(state->path, len + 1, "%s", (const char *)path);
240
241
    /* compute the flags for open() */
242
23.4k
    oflag =
243
23.4k
#ifdef O_LARGEFILE
244
23.4k
        O_LARGEFILE |
245
23.4k
#endif
246
#ifdef O_BINARY
247
        O_BINARY |
248
#endif
249
23.4k
#ifdef O_CLOEXEC
250
23.4k
        (cloexec ? O_CLOEXEC : 0) |
251
23.4k
#endif
252
23.4k
        (state->mode == GZ_READ ?
253
23.4k
         O_RDONLY :
254
23.4k
         (O_WRONLY | O_CREAT |
255
11.7k
#ifdef O_EXCL
256
11.7k
          (exclusive ? O_EXCL : 0) |
257
11.7k
#endif
258
11.7k
          (state->mode == GZ_WRITE ?
259
11.7k
           O_TRUNC :
260
11.7k
           O_APPEND)));
261
262
    /* open the file with the appropriate flags (or just use fd) */
263
23.4k
    state->fd = fd > -1 ? fd : (
264
#if defined(_WIN32)
265
        fd == -2 ? _wopen((const wchar_t *)path, oflag, 0666) :
266
#elif defined(__CYGWIN__)
267
        fd == -2 ? open(state->path, oflag, 0666) :
268
#endif
269
23.4k
        open((const char *)path, oflag, 0666));
270
23.4k
    if (state->fd == -1) {
271
0
        free(state->path);
272
0
        gz_state_free(state);
273
0
        return NULL;
274
0
    }
275
23.4k
    if (state->mode == GZ_APPEND) {
276
0
        LSEEK(state->fd, 0, SEEK_END);  /* so gzoffset() is correct */
277
0
        state->mode = GZ_WRITE;         /* simplify later checks */
278
0
    }
279
280
    /* save the current position for rewinding (only if reading) */
281
23.4k
    if (state->mode == GZ_READ) {
282
11.7k
        state->start = LSEEK(state->fd, 0, SEEK_CUR);
283
11.7k
        if (state->start == -1) state->start = 0;
284
11.7k
    }
285
286
    /* initialize stream */
287
23.4k
    gz_reset(state);
288
289
    /* return stream */
290
23.4k
    return (gzFile)state;
291
23.4k
}
292
293
/* -- see zlib.h -- */
294
23.4k
gzFile Z_EXPORT PREFIX(gzopen)(const char *path, const char *mode) {
295
23.4k
    return gz_open(path, -1, mode);
296
23.4k
}
297
298
#ifdef ZLIB_COMPAT
299
gzFile Z_EXPORT PREFIX4(gzopen)(const char *path, const char *mode) {
300
    return gz_open(path, -1, mode);
301
}
302
#endif
303
304
/* -- see zlib.h -- */
305
0
gzFile Z_EXPORT PREFIX(gzdopen)(int fd, const char *mode) {
306
0
    gzFile gz;
307
0
    char path[32];         /* identifier for error messages */
308
309
0
    if (fd == -1)
310
0
        return NULL;
311
0
    (void)snprintf(path, 32, "<fd:%d>", fd); /* for debugging */
312
0
    gz = gz_open(path, fd, mode);
313
0
    return gz;
314
0
}
315
316
/* -- see zlib.h -- */
317
#ifdef WIDECHAR
318
gzFile Z_EXPORT PREFIX(gzopen_w)(const wchar_t *path, const char *mode) {
319
    return gz_open(path, -2, mode);
320
}
321
#endif
322
323
23.4k
z_int32_t Z_EXPORT PREFIX(gzclose)(gzFile file) {
324
23.4k
#ifndef NO_GZCOMPRESS
325
23.4k
    gz_state *state;
326
327
23.4k
    if (file == NULL)
328
0
        return Z_STREAM_ERROR;
329
23.4k
    state = (gz_state *)file;
330
331
23.4k
    return state->mode == GZ_READ ? PREFIX(gzclose_r)(file) : PREFIX(gzclose_w)(file);
332
#else
333
    return PREFIX(gzclose_r)(file);
334
#endif
335
23.4k
}
336
337
/* -- see zlib.h -- */
338
0
z_int32_t Z_EXPORT PREFIX(gzbuffer)(gzFile file, z_uint32_t size) {
339
0
    gz_state *state;
340
341
    /* get internal structure and check integrity */
342
0
    if (file == NULL)
343
0
        return -1;
344
0
    state = (gz_state *)file;
345
0
    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
346
0
        return -1;
347
348
    /* make sure we haven't already allocated memory */
349
0
    if (state->size != 0)
350
0
        return -1;
351
352
    /* check and set requested size */
353
0
    if (size > GZBUFSIZE_MAX) {
354
#ifdef ZLIB_COMPAT
355
        size = GZBUFSIZE_MAX;   /* silently clamp to stay compatible with zlib */
356
#else
357
0
        PREFIX(gz_error)(state, Z_MEM_ERROR, "out of memory");
358
0
        return -1;              /* too large */
359
0
#endif
360
0
    }
361
0
    if (size < 8)
362
0
        size = 8;               /* needed to behave well with flushing */
363
0
    state->want = size;
364
0
    return 0;
365
0
}
366
367
/* -- see zlib.h -- */
368
0
z_int32_t Z_EXPORT PREFIX(gzrewind)(gzFile file) {
369
0
    gz_state *state;
370
371
    /* get internal structure */
372
0
    if (file == NULL)
373
0
        return -1;
374
0
    state = (gz_state *)file;
375
376
    /* check that we're reading and that there's no error */
377
0
    if (state->mode != GZ_READ || (state->err != Z_OK && state->err != Z_BUF_ERROR))
378
0
        return -1;
379
380
    /* back up and start over */
381
0
    if (LSEEK(state->fd, state->start, SEEK_SET) == -1)
382
0
        return -1;
383
0
    gz_reset(state);
384
0
    return 0;
385
0
}
386
387
/* -- see zlib.h -- */
388
0
z_off64_t Z_EXPORT PREFIX4(gzseek)(gzFile file, z_off64_t offset, int whence) {
389
0
    unsigned n;
390
0
    z_off64_t ret;
391
0
    gz_state *state;
392
393
    /* get internal structure and check integrity */
394
0
    if (file == NULL)
395
0
        return -1;
396
0
    state = (gz_state *)file;
397
0
    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
398
0
        return -1;
399
400
    /* check that there's no error */
401
0
    if (state->err != Z_OK && state->err != Z_BUF_ERROR)
402
0
        return -1;
403
404
    /* can only seek from start or relative to current position */
405
0
    if (whence != SEEK_SET && whence != SEEK_CUR)
406
0
        return -1;
407
408
    /* normalize offset to a SEEK_CUR specification */
409
0
    if (whence == SEEK_SET)
410
0
        offset -= state->x.pos;
411
0
    else {
412
0
        offset += state->past ? 0 : state->skip;
413
0
        state->skip = 0;
414
0
    }
415
416
    /* if within raw area while reading, just go there */
417
0
    if (state->mode == GZ_READ && state->how == COPY && state->x.pos + offset >= 0) {
418
0
        ret = LSEEK(state->fd, offset - (z_off64_t)state->x.have, SEEK_CUR);
419
0
        if (ret == -1)
420
0
            return -1;
421
0
        state->x.have = 0;
422
0
        state->eof = 0;
423
0
        state->past = 0;
424
0
        state->skip = 0;
425
0
        PREFIX(gz_error)(state, Z_OK, NULL);
426
0
        state->strm.avail_in = 0;
427
0
        state->x.pos += offset;
428
0
        return state->x.pos;
429
0
    }
430
431
    /* calculate skip amount, rewinding if needed for back seek when reading */
432
0
    if (offset < 0) {
433
0
        if (state->mode != GZ_READ)         /* writing -- can't go backwards */
434
0
            return -1;
435
0
        offset += state->x.pos;
436
0
        if (offset < 0)                     /* before start of file! */
437
0
            return -1;
438
0
        if (PREFIX(gzrewind)(file) == -1)   /* rewind, then skip to offset */
439
0
            return -1;
440
0
    }
441
442
    /* if reading, skip what's in output buffer (one less gzgetc() check) */
443
0
    if (state->mode == GZ_READ) {
444
0
        n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ? (unsigned)offset : state->x.have;
445
0
        state->x.have -= n;
446
0
        state->x.next += n;
447
0
        state->x.pos += n;
448
0
        offset -= n;
449
0
    }
450
451
    /* request skip (if not zero) */
452
0
    state->skip = offset;
453
0
    return state->x.pos + offset;
454
0
}
455
456
/* -- see zlib.h -- */
457
#ifdef ZLIB_COMPAT
458
z_off_t Z_EXPORT PREFIX(gzseek)(gzFile file, z_off_t offset, int whence) {
459
    z_off64_t ret;
460
461
    ret = PREFIX4(gzseek)(file, (z_off64_t)offset, whence);
462
    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
463
}
464
#endif
465
466
/* -- see zlib.h -- */
467
0
z_off64_t Z_EXPORT PREFIX4(gztell)(gzFile file) {
468
0
    gz_state *state;
469
470
    /* get internal structure and check integrity */
471
0
    if (file == NULL)
472
0
        return -1;
473
0
    state = (gz_state *)file;
474
0
    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
475
0
        return -1;
476
477
    /* return position */
478
0
    return state->x.pos + (state->past ? 0 : state->skip);
479
0
}
480
481
/* -- see zlib.h -- */
482
#ifdef ZLIB_COMPAT
483
z_off_t Z_EXPORT PREFIX(gztell)(gzFile file) {
484
485
    z_off64_t ret;
486
487
    ret = PREFIX4(gztell)(file);
488
    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
489
}
490
#endif
491
492
/* -- see zlib.h -- */
493
0
z_off64_t Z_EXPORT PREFIX4(gzoffset)(gzFile file) {
494
0
    z_off64_t offset;
495
0
    gz_state *state;
496
497
    /* get internal structure and check integrity */
498
0
    if (file == NULL)
499
0
        return -1;
500
0
    state = (gz_state *)file;
501
0
    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
502
0
        return -1;
503
504
    /* compute and return effective offset in file */
505
0
    offset = LSEEK(state->fd, 0, SEEK_CUR);
506
0
    if (offset == -1)
507
0
        return -1;
508
0
    if (state->mode == GZ_READ)             /* reading */
509
0
        offset -= state->strm.avail_in;     /* don't count buffered input */
510
0
    return offset;
511
0
}
512
513
/* -- see zlib.h -- */
514
#ifdef ZLIB_COMPAT
515
z_off_t Z_EXPORT PREFIX(gzoffset)(gzFile file) {
516
    z_off64_t ret;
517
518
    ret = PREFIX4(gzoffset)(file);
519
    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
520
}
521
#endif
522
523
/* -- see zlib.h -- */
524
0
z_int32_t Z_EXPORT PREFIX(gzeof)(gzFile file) {
525
0
    gz_state *state;
526
527
    /* get internal structure and check integrity */
528
0
    if (file == NULL)
529
0
        return 0;
530
0
    state = (gz_state *)file;
531
0
    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
532
0
        return 0;
533
534
    /* return end-of-file state */
535
0
    return state->mode == GZ_READ ? state->past : 0;
536
0
}
537
538
/* -- see zlib.h -- */
539
0
const char * Z_EXPORT PREFIX(gzerror)(gzFile file, z_int32_t *errnum) {
540
0
    gz_state *state;
541
542
    /* get internal structure and check integrity */
543
0
    if (file == NULL)
544
0
        return NULL;
545
0
    state = (gz_state *)file;
546
0
    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
547
0
        return NULL;
548
549
    /* return error information */
550
0
    if (errnum != NULL)
551
0
        *errnum = state->err;
552
0
    return state->err == Z_MEM_ERROR ? "out of memory" : (state->msg == NULL ? "" : state->msg);
553
0
}
554
555
/* -- see zlib.h -- */
556
0
void Z_EXPORT PREFIX(gzclearerr)(gzFile file) {
557
0
    gz_state *state;
558
559
    /* get internal structure and check integrity */
560
0
    if (file == NULL)
561
0
        return;
562
0
    state = (gz_state *)file;
563
0
    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
564
0
        return;
565
566
    /* clear error and end-of-file */
567
0
    if (state->mode == GZ_READ) {
568
0
        state->eof = 0;
569
0
        state->past = 0;
570
0
    }
571
0
    PREFIX(gz_error)(state, Z_OK, NULL);
572
0
}
573
574
/* Create an error message in allocated memory and set state->err and
575
   state->msg accordingly.  Free any previous error message already there.  Do
576
   not try to free or allocate space if the error is Z_MEM_ERROR (out of
577
   memory).  Simply save the error message as a static string.  If there is an
578
   allocation failure constructing the error message, then convert the error to
579
   out of memory. */
580
46.9k
void Z_INTERNAL PREFIX(gz_error)(gz_state *state, int err, const char *msg) {
581
    /* free previously allocated message and clear */
582
46.9k
    if (state->msg != NULL) {
583
0
        if (state->err != Z_MEM_ERROR)
584
0
            free(state->msg);
585
0
        state->msg = NULL;
586
0
    }
587
588
    /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */
589
46.9k
    if (err != Z_OK && err != Z_BUF_ERROR)
590
0
        state->x.have = 0;
591
592
    /* set error code, and if no message, then done */
593
46.9k
    state->err = err;
594
46.9k
    if (msg == NULL)
595
46.9k
        return;
596
597
    /* for an out of memory error, return literal string when requested */
598
0
    if (err == Z_MEM_ERROR)
599
0
        return;
600
601
    /* construct error message with path */
602
0
    if ((state->msg = malloc(strlen(state->path) + strlen(msg) + 3)) == NULL) {
603
0
        state->err = Z_MEM_ERROR;
604
0
        return;
605
0
    }
606
0
    (void)snprintf(state->msg, strlen(state->path) + strlen(msg) + 3, "%s%s%s", state->path, ": ", msg);
607
0
}
608
609
#ifdef ZLIB_COMPAT
610
unsigned Z_INTERNAL gz_intmax(void) {
611
    return INT_MAX;
612
}
613
#endif