Coverage Report

Created: 2025-07-11 06:15

/src/zlib-ng/gzwrite.c
Line
Count
Source (jump to first uncovered line)
1
/* gzwrite.c -- zlib functions for writing gzip files
2
 * Copyright (C) 2004-2019 Mark Adler
3
 * For conditions of distribution and use, see copyright notice in zlib.h
4
 */
5
6
#include "zbuild.h"
7
#include "zutil_p.h"
8
#include <stdarg.h>
9
#include "gzguts.h"
10
11
/* Local functions */
12
static int gz_init(gz_state *);
13
static int gz_comp(gz_state *, int);
14
static int gz_zero(gz_state *, z_off64_t);
15
static size_t gz_write(gz_state *, void const *, size_t);
16
17
/* Initialize state for writing a gzip file.  Mark initialization by setting
18
   state->size to non-zero.  Return -1 on a memory allocation failure, or 0 on
19
   success. */
20
6.28k
static int gz_init(gz_state *state) {
21
6.28k
    int ret;
22
6.28k
    PREFIX3(stream) *strm = &(state->strm);
23
24
    /* allocate input buffer (double size for gzprintf) */
25
6.28k
    state->in = (unsigned char *)zng_alloc(state->want << 1);
26
6.28k
    if (state->in == NULL) {
27
0
        gz_error(state, Z_MEM_ERROR, "out of memory");
28
0
        return -1;
29
0
    }
30
6.28k
    memset(state->in, 0, state->want << 1);
31
32
    /* only need output buffer and deflate state if compressing */
33
6.28k
    if (!state->direct) {
34
        /* allocate output buffer */
35
6.25k
        state->out = (unsigned char *)zng_alloc(state->want);
36
6.25k
        if (state->out == NULL) {
37
0
            zng_free(state->in);
38
0
            gz_error(state, Z_MEM_ERROR, "out of memory");
39
0
            return -1;
40
0
        }
41
42
        /* allocate deflate memory, set up for gzip compression */
43
6.25k
        strm->zalloc = NULL;
44
6.25k
        strm->zfree = NULL;
45
6.25k
        strm->opaque = NULL;
46
6.25k
        ret = PREFIX(deflateInit2)(strm, state->level, Z_DEFLATED, MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy);
47
6.25k
        if (ret != Z_OK) {
48
0
            zng_free(state->out);
49
0
            zng_free(state->in);
50
0
            gz_error(state, Z_MEM_ERROR, "out of memory");
51
0
            return -1;
52
0
        }
53
6.25k
        strm->next_in = NULL;
54
6.25k
    }
55
56
    /* mark state as initialized */
57
6.28k
    state->size = state->want;
58
59
    /* initialize write buffer if compressing */
60
6.28k
    if (!state->direct) {
61
6.25k
        strm->avail_out = state->size;
62
6.25k
        strm->next_out = state->out;
63
6.25k
        state->x.next = strm->next_out;
64
6.25k
    }
65
6.28k
    return 0;
66
6.28k
}
67
68
/* Compress whatever is at avail_in and next_in and write to the output file.
69
   Return -1 if there is an error writing to the output file or if gz_init()
70
   fails to allocate memory, otherwise 0.  flush is assumed to be a valid
71
   deflate() flush value.  If flush is Z_FINISH, then the deflate() state is
72
   reset to start a new gzip stream.  If gz->direct is true, then simply write
73
   to the output file without compressing, and ignore flush. */
74
8.64k
static int gz_comp(gz_state *state, int flush) {
75
8.64k
    int ret;
76
8.64k
    ssize_t got;
77
8.64k
    unsigned have;
78
8.64k
    PREFIX3(stream) *strm = &(state->strm);
79
80
    /* allocate memory if this is the first time through */
81
8.64k
    if (state->size == 0 && gz_init(state) == -1)
82
0
        return -1;
83
84
    /* write directly if requested */
85
8.64k
    if (state->direct) {
86
54
        got = write(state->fd, strm->next_in, strm->avail_in);
87
54
        if (got < 0 || (unsigned)got != strm->avail_in) {
88
0
            gz_error(state, Z_ERRNO, zstrerror());
89
0
            return -1;
90
0
        }
91
54
        strm->avail_in = 0;
92
54
        return 0;
93
54
    }
94
95
    /* check for a pending reset */
96
8.58k
    if (state->reset) {
97
        /* don't start a new gzip member unless there is data to write */
98
0
        if (strm->avail_in == 0)
99
0
            return 0;
100
0
        PREFIX(deflateReset)(strm);
101
0
        state->reset = 0;
102
0
    }
103
104
    /* run deflate() on provided input until it produces no more output */
105
8.58k
    ret = Z_OK;
106
18.5k
    do {
107
        /* write out current buffer contents if full, or if flushing, but if
108
           doing Z_FINISH then don't write until we get to Z_STREAM_END */
109
18.5k
        if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && (flush != Z_FINISH || ret == Z_STREAM_END))) {
110
7.73k
            have = (unsigned)(strm->next_out - state->x.next);
111
7.73k
            if (have && ((got = write(state->fd, state->x.next, (unsigned long)have)) < 0 || (unsigned)got != have)) {
112
0
                gz_error(state, Z_ERRNO, zstrerror());
113
0
                return -1;
114
0
            }
115
7.73k
            if (strm->avail_out == 0) {
116
1.48k
                strm->avail_out = state->size;
117
1.48k
                strm->next_out = state->out;
118
1.48k
                state->x.next = state->out;
119
1.48k
            }
120
7.73k
            state->x.next = strm->next_out;
121
7.73k
        }
122
123
        /* compress */
124
18.5k
        have = strm->avail_out;
125
18.5k
        ret = PREFIX(deflate)(strm, flush);
126
18.5k
        if (ret == Z_STREAM_ERROR) {
127
0
            gz_error(state, Z_STREAM_ERROR, "internal error: deflate stream corrupt");
128
0
            return -1;
129
0
        }
130
18.5k
        have -= strm->avail_out;
131
18.5k
    } while (have);
132
133
    /* if that completed a deflate stream, allow another to start */
134
8.58k
    if (flush == Z_FINISH)
135
6.25k
        state->reset = 1;
136
    /* all done, no errors */
137
8.58k
    return 0;
138
8.58k
}
139
140
/* Compress len zeros to output.  Return -1 on a write error or memory
141
   allocation failure by gz_comp(), or 0 on success. */
142
0
static int gz_zero(gz_state *state, z_off64_t len) {
143
0
    int first;
144
0
    unsigned n;
145
0
    PREFIX3(stream) *strm = &(state->strm);
146
147
    /* consume whatever's left in the input buffer */
148
0
    if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
149
0
        return -1;
150
151
    /* compress len zeros (len guaranteed > 0) */
152
0
    first = 1;
153
0
    while (len) {
154
0
        n = GT_OFF(state->size) || (z_off64_t)state->size > len ? (unsigned)len : state->size;
155
0
        if (first) {
156
0
            memset(state->in, 0, n);
157
0
            first = 0;
158
0
        }
159
0
        strm->avail_in = n;
160
0
        strm->next_in = state->in;
161
0
        state->x.pos += n;
162
0
        if (gz_comp(state, Z_NO_FLUSH) == -1)
163
0
            return -1;
164
0
        len -= n;
165
0
    }
166
0
    return 0;
167
0
}
168
169
/* Write len bytes from buf to file.  Return the number of bytes written.  If
170
   the returned value is less than len, then there was an error. */
171
30.7k
static size_t gz_write(gz_state *state, void const *buf, size_t len) {
172
30.7k
    size_t put = len;
173
174
    /* if len is zero, avoid unnecessary operations */
175
30.7k
    if (len == 0)
176
0
        return 0;
177
178
    /* allocate memory if this is the first time through */
179
30.7k
    if (state->size == 0 && gz_init(state) == -1)
180
0
        return 0;
181
182
    /* check for seek request */
183
30.7k
    if (state->seek) {
184
0
        state->seek = 0;
185
0
        if (gz_zero(state, state->skip) == -1)
186
0
            return 0;
187
0
    }
188
189
    /* for small len, copy to input buffer, otherwise compress directly */
190
30.7k
    if (len < state->size) {
191
        /* copy to input buffer, compress when full */
192
33.1k
        do {
193
33.1k
            unsigned have, copy;
194
195
33.1k
            if (state->strm.avail_in == 0)
196
8.64k
                state->strm.next_in = state->in;
197
33.1k
            have = (unsigned)((state->strm.next_in + state->strm.avail_in) -
198
33.1k
                              state->in);
199
33.1k
            copy = state->size - have;
200
33.1k
            if (copy > len)
201
28.3k
                copy = (unsigned)len;
202
33.1k
            memcpy(state->in + have, buf, copy);
203
33.1k
            state->strm.avail_in += copy;
204
33.1k
            state->x.pos += copy;
205
33.1k
            buf = (const char *)buf + copy;
206
33.1k
            len -= copy;
207
33.1k
            if (len && gz_comp(state, Z_NO_FLUSH) == -1)
208
0
                return 0;
209
33.1k
        } while (len);
210
30.7k
    } else {
211
        /* consume whatever's left in the input buffer */
212
0
        if (state->strm.avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
213
0
            return 0;
214
215
        /* directly compress user buffer to file */
216
0
        state->strm.next_in = (z_const unsigned char *) buf;
217
0
        do {
218
0
            unsigned n = (unsigned)-1;
219
0
            if (n > len)
220
0
                n = (unsigned)len;
221
0
            state->strm.avail_in = n;
222
0
            state->x.pos += n;
223
0
            if (gz_comp(state, Z_NO_FLUSH) == -1)
224
0
                return 0;
225
0
            len -= n;
226
0
        } while (len);
227
0
    }
228
229
    /* input was all buffered or compressed */
230
30.7k
    return put;
231
30.7k
}
232
233
/* -- see zlib.h -- */
234
30.7k
int Z_EXPORT PREFIX(gzwrite)(gzFile file, void const *buf, unsigned len) {
235
30.7k
    gz_state *state;
236
237
    /* get internal structure */
238
30.7k
    if (file == NULL)
239
0
        return 0;
240
30.7k
    state = (gz_state *)file;
241
242
    /* check that we're writing and that there's no error */
243
30.7k
    if (state->mode != GZ_WRITE || state->err != Z_OK)
244
0
        return 0;
245
246
    /* since an int is returned, make sure len fits in one, otherwise return
247
       with an error (this avoids a flaw in the interface) */
248
30.7k
    if ((int)len < 0) {
249
0
        gz_error(state, Z_DATA_ERROR, "requested length does not fit in int");
250
0
        return 0;
251
0
    }
252
253
    /* write len bytes from buf (the return value will fit in an int) */
254
30.7k
    return (int)gz_write(state, buf, len);
255
30.7k
}
256
257
/* -- see zlib.h -- */
258
0
size_t Z_EXPORT PREFIX(gzfwrite)(void const *buf, size_t size, size_t nitems, gzFile file) {
259
0
    size_t len;
260
0
    gz_state *state;
261
262
    /* Exit early if size is zero, also prevents potential division by zero */
263
0
    if (size == 0)
264
0
        return 0;
265
266
    /* get internal structure */
267
0
    if (file == NULL)
268
0
        return 0;
269
0
    state = (gz_state *)file;
270
271
    /* check that we're writing and that there's no error */
272
0
    if (state->mode != GZ_WRITE || state->err != Z_OK)
273
0
        return 0;
274
275
    /* compute bytes to read -- error on overflow */
276
0
    len = nitems * size;
277
0
    if (len / size != nitems) {
278
0
        gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t");
279
0
        return 0;
280
0
    }
281
282
    /* write len bytes to buf, return the number of full items written */
283
0
    return len ? gz_write(state, buf, len) / size : 0;
284
0
}
285
286
/* -- see zlib.h -- */
287
0
int Z_EXPORT PREFIX(gzputc)(gzFile file, int c) {
288
0
    unsigned have;
289
0
    unsigned char buf[1];
290
0
    gz_state *state;
291
0
    PREFIX3(stream) *strm;
292
293
    /* get internal structure */
294
0
    if (file == NULL)
295
0
        return -1;
296
0
    state = (gz_state *)file;
297
0
    strm = &(state->strm);
298
299
    /* check that we're writing and that there's no error */
300
0
    if (state->mode != GZ_WRITE || state->err != Z_OK)
301
0
        return -1;
302
303
    /* check for seek request */
304
0
    if (state->seek) {
305
0
        state->seek = 0;
306
0
        if (gz_zero(state, state->skip) == -1)
307
0
            return -1;
308
0
    }
309
310
    /* try writing to input buffer for speed (state->size == 0 if buffer not
311
       initialized) */
312
0
    if (state->size) {
313
0
        if (strm->avail_in == 0)
314
0
            strm->next_in = state->in;
315
0
        have = (unsigned)((strm->next_in + strm->avail_in) - state->in);
316
0
        if (have < state->size) {
317
0
            state->in[have] = (unsigned char)c;
318
0
            strm->avail_in++;
319
0
            state->x.pos++;
320
0
            return c & 0xff;
321
0
        }
322
0
    }
323
324
    /* no room in buffer or not initialized, use gz_write() */
325
0
    buf[0] = (unsigned char)c;
326
0
    if (gz_write(state, buf, 1) != 1)
327
0
        return -1;
328
0
    return c & 0xff;
329
0
}
330
331
/* -- see zlib.h -- */
332
0
int Z_EXPORT PREFIX(gzputs)(gzFile file, const char *s) {
333
0
    size_t len, put;
334
0
    gz_state *state;
335
336
    /* get internal structure */
337
0
    if (file == NULL)
338
0
        return -1;
339
0
    state = (gz_state *)file;
340
341
    /* check that we're writing and that there's no error */
342
0
    if (state->mode != GZ_WRITE || state->err != Z_OK)
343
0
        return -1;
344
345
    /* write string */
346
0
    len = strlen(s);
347
0
    if ((int)len < 0 || (unsigned)len != len) {
348
0
        gz_error(state, Z_STREAM_ERROR, "string length does not fit in int");
349
0
        return -1;
350
0
    }
351
0
    put = gz_write(state, s, len);
352
0
    return put < len ? -1 : (int)len;
353
0
}
354
355
/* -- see zlib.h -- */
356
0
int Z_EXPORTVA PREFIX(gzvprintf)(gzFile file, const char *format, va_list va) {
357
0
    int len;
358
0
    unsigned left;
359
0
    char *next;
360
0
    gz_state *state;
361
0
    PREFIX3(stream) *strm;
362
363
    /* get internal structure */
364
0
    if (file == NULL)
365
0
        return Z_STREAM_ERROR;
366
0
    state = (gz_state *)file;
367
0
    strm = &(state->strm);
368
369
    /* check that we're writing and that there's no error */
370
0
    if (state->mode != GZ_WRITE || state->err != Z_OK)
371
0
        return Z_STREAM_ERROR;
372
373
    /* make sure we have some buffer space */
374
0
    if (state->size == 0 && gz_init(state) == -1)
375
0
        return state->err;
376
377
    /* check for seek request */
378
0
    if (state->seek) {
379
0
        state->seek = 0;
380
0
        if (gz_zero(state, state->skip) == -1)
381
0
            return state->err;
382
0
    }
383
384
    /* do the printf() into the input buffer, put length in len -- the input
385
       buffer is double-sized just for this function, so there is guaranteed to
386
       be state->size bytes available after the current contents */
387
0
    if (strm->avail_in == 0)
388
0
        strm->next_in = state->in;
389
0
    next = (char *)(state->in + (strm->next_in - state->in) + strm->avail_in);
390
0
    next[state->size - 1] = 0;
391
0
    len = vsnprintf(next, state->size, format, va);
392
393
    /* check that printf() results fit in buffer */
394
0
    if (len == 0 || (unsigned)len >= state->size || next[state->size - 1] != 0)
395
0
        return 0;
396
397
    /* update buffer and position, compress first half if past that */
398
0
    strm->avail_in += (unsigned)len;
399
0
    state->x.pos += len;
400
0
    if (strm->avail_in >= state->size) {
401
0
        left = strm->avail_in - state->size;
402
0
        strm->avail_in = state->size;
403
0
        if (gz_comp(state, Z_NO_FLUSH) == -1)
404
0
            return state->err;
405
0
        memmove(state->in, state->in + state->size, left);
406
0
        strm->next_in = state->in;
407
0
        strm->avail_in = left;
408
0
    }
409
0
    return len;
410
0
}
411
412
0
int Z_EXPORTVA PREFIX(gzprintf)(gzFile file, const char *format, ...) {
413
0
    va_list va;
414
0
    int ret;
415
416
0
    va_start(va, format);
417
0
    ret = PREFIX(gzvprintf)(file, format, va);
418
0
    va_end(va);
419
0
    return ret;
420
0
}
421
422
/* -- see zlib.h -- */
423
0
int Z_EXPORT PREFIX(gzflush)(gzFile file, int flush) {
424
0
    gz_state *state;
425
426
    /* get internal structure */
427
0
    if (file == NULL)
428
0
        return Z_STREAM_ERROR;
429
0
    state = (gz_state *)file;
430
431
    /* check that we're writing and that there's no error */
432
0
    if (state->mode != GZ_WRITE || state->err != Z_OK)
433
0
        return Z_STREAM_ERROR;
434
435
    /* check flush parameter */
436
0
    if (flush < 0 || flush > Z_FINISH)
437
0
        return Z_STREAM_ERROR;
438
439
    /* check for seek request */
440
0
    if (state->seek) {
441
0
        state->seek = 0;
442
0
        if (gz_zero(state, state->skip) == -1)
443
0
            return state->err;
444
0
    }
445
446
    /* compress remaining data with requested flush */
447
0
    (void)gz_comp(state, flush);
448
0
    return state->err;
449
0
}
450
451
/* -- see zlib.h -- */
452
0
int Z_EXPORT PREFIX(gzsetparams)(gzFile file, int level, int strategy) {
453
0
    gz_state *state;
454
0
    PREFIX3(stream) *strm;
455
456
    /* get internal structure */
457
0
    if (file == NULL)
458
0
        return Z_STREAM_ERROR;
459
0
    state = (gz_state *)file;
460
0
    strm = &(state->strm);
461
462
    /* check that we're writing and that there's no error */
463
0
    if (state->mode != GZ_WRITE || state->err != Z_OK || state->direct)
464
0
        return Z_STREAM_ERROR;
465
466
    /* if no change is requested, then do nothing */
467
0
    if (level == state->level && strategy == state->strategy)
468
0
        return Z_OK;
469
470
    /* check for seek request */
471
0
    if (state->seek) {
472
0
        state->seek = 0;
473
0
        if (gz_zero(state, state->skip) == -1)
474
0
            return state->err;
475
0
    }
476
477
    /* change compression parameters for subsequent input */
478
0
    if (state->size) {
479
        /* flush previous input with previous parameters before changing */
480
0
        if (strm->avail_in && gz_comp(state, Z_BLOCK) == -1)
481
0
            return state->err;
482
0
        PREFIX(deflateParams)(strm, level, strategy);
483
0
    }
484
0
    state->level = level;
485
0
    state->strategy = strategy;
486
0
    return Z_OK;
487
0
}
488
489
/* -- see zlib.h -- */
490
6.28k
int Z_EXPORT PREFIX(gzclose_w)(gzFile file) {
491
6.28k
    int ret = Z_OK;
492
6.28k
    gz_state *state;
493
494
    /* get internal structure */
495
6.28k
    if (file == NULL)
496
0
        return Z_STREAM_ERROR;
497
6.28k
    state = (gz_state *)file;
498
499
    /* check that we're writing */
500
6.28k
    if (state->mode != GZ_WRITE)
501
0
        return Z_STREAM_ERROR;
502
503
    /* check for seek request */
504
6.28k
    if (state->seek) {
505
0
        state->seek = 0;
506
0
        if (gz_zero(state, state->skip) == -1)
507
0
            ret = state->err;
508
0
    }
509
510
    /* flush, free memory, and close file */
511
6.28k
    if (gz_comp(state, Z_FINISH) == -1)
512
0
        ret = state->err;
513
6.28k
    if (state->size) {
514
6.28k
        if (!state->direct) {
515
6.25k
            (void)PREFIX(deflateEnd)(&(state->strm));
516
6.25k
            zng_free(state->out);
517
6.25k
        }
518
6.28k
        zng_free(state->in);
519
6.28k
    }
520
6.28k
    gz_error(state, Z_OK, NULL);
521
6.28k
    free(state->path);
522
6.28k
    if (close(state->fd) == -1)
523
0
        ret = Z_ERRNO;
524
6.28k
    zng_free(state);
525
6.28k
    return ret;
526
6.28k
}