xref: /openbsd/lib/libz/gzwrite.c (revision 703d4924)
1 /*	$OpenBSD: gzwrite.c,v 1.2 2022/05/08 14:04:22 tb Exp $ */
2 /* gzwrite.c -- zlib functions for writing gzip files
3  * Copyright (C) 2004-2019 Mark Adler
4  * For conditions of distribution and use, see copyright notice in zlib.h
5  */
6 
7 #include "gzguts.h"
8 
9 /* Local functions */
10 local int gz_init OF((gz_statep));
11 local int gz_comp OF((gz_statep, int));
12 local int gz_zero OF((gz_statep, z_off64_t));
13 local z_size_t gz_write OF((gz_statep, voidpc, z_size_t));
14 
15 /* Initialize state for writing a gzip file.  Mark initialization by setting
16    state->size to non-zero.  Return -1 on a memory allocation failure, or 0 on
17    success. */
18 local int gz_init(state)
19     gz_statep state;
20 {
21     int ret;
22     z_streamp strm = &(state->strm);
23 
24     /* allocate input buffer (double size for gzprintf) */
25     state->in = (unsigned char *)malloc(state->want << 1);
26     if (state->in == NULL) {
27         gz_error(state, Z_MEM_ERROR, "out of memory");
28         return -1;
29     }
30 
31     /* only need output buffer and deflate state if compressing */
32     if (!state->direct) {
33         /* allocate output buffer */
34         state->out = (unsigned char *)malloc(state->want);
35         if (state->out == NULL) {
36             free(state->in);
37             gz_error(state, Z_MEM_ERROR, "out of memory");
38             return -1;
39         }
40 
41         /* allocate deflate memory, set up for gzip compression */
42         strm->zalloc = Z_NULL;
43         strm->zfree = Z_NULL;
44         strm->opaque = Z_NULL;
45         ret = deflateInit2(strm, state->level, Z_DEFLATED,
46                            MAX_WBITS + 16, DEF_MEM_LEVEL, state->strategy);
47         if (ret != Z_OK) {
48             free(state->out);
49             free(state->in);
50             gz_error(state, Z_MEM_ERROR, "out of memory");
51             return -1;
52         }
53         strm->next_in = NULL;
54     }
55 
56     /* mark state as initialized */
57     state->size = state->want;
58 
59     /* initialize write buffer if compressing */
60     if (!state->direct) {
61         strm->avail_out = state->size;
62         strm->next_out = state->out;
63         state->x.next = strm->next_out;
64     }
65     return 0;
66 }
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 local int gz_comp(state, flush)
75     gz_statep state;
76     int flush;
77 {
78     int ret, writ;
79     unsigned have, put, max = ((unsigned)-1 >> 2) + 1;
80     z_streamp strm = &(state->strm);
81 
82     /* allocate memory if this is the first time through */
83     if (state->size == 0 && gz_init(state) == -1)
84         return -1;
85 
86     /* write directly if requested */
87     if (state->direct) {
88         while (strm->avail_in) {
89             put = strm->avail_in > max ? max : strm->avail_in;
90             writ = write(state->fd, strm->next_in, put);
91             if (writ < 0) {
92                 gz_error(state, Z_ERRNO, zstrerror());
93                 return -1;
94             }
95             strm->avail_in -= (unsigned)writ;
96             strm->next_in += writ;
97         }
98         return 0;
99     }
100 
101     /* check for a pending reset */
102     if (state->reset) {
103         /* don't start a new gzip member unless there is data to write */
104         if (strm->avail_in == 0)
105             return 0;
106         deflateReset(strm);
107         state->reset = 0;
108     }
109 
110     /* run deflate() on provided input until it produces no more output */
111     ret = Z_OK;
112     do {
113         /* write out current buffer contents if full, or if flushing, but if
114            doing Z_FINISH then don't write until we get to Z_STREAM_END */
115         if (strm->avail_out == 0 || (flush != Z_NO_FLUSH &&
116             (flush != Z_FINISH || ret == Z_STREAM_END))) {
117             while (strm->next_out > state->x.next) {
118                 put = strm->next_out - state->x.next > (int)max ? max :
119                       (unsigned)(strm->next_out - state->x.next);
120                 writ = write(state->fd, state->x.next, put);
121                 if (writ < 0) {
122                     gz_error(state, Z_ERRNO, zstrerror());
123                     return -1;
124                 }
125                 state->x.next += writ;
126             }
127             if (strm->avail_out == 0) {
128                 strm->avail_out = state->size;
129                 strm->next_out = state->out;
130                 state->x.next = state->out;
131             }
132         }
133 
134         /* compress */
135         have = strm->avail_out;
136         ret = deflate(strm, flush);
137         if (ret == Z_STREAM_ERROR) {
138             gz_error(state, Z_STREAM_ERROR,
139                       "internal error: deflate stream corrupt");
140             return -1;
141         }
142         have -= strm->avail_out;
143     } while (have);
144 
145     /* if that completed a deflate stream, allow another to start */
146     if (flush == Z_FINISH)
147         state->reset = 1;
148 
149     /* all done, no errors */
150     return 0;
151 }
152 
153 /* Compress len zeros to output.  Return -1 on a write error or memory
154    allocation failure by gz_comp(), or 0 on success. */
155 local int gz_zero(state, len)
156     gz_statep state;
157     z_off64_t len;
158 {
159     int first;
160     unsigned n;
161     z_streamp strm = &(state->strm);
162 
163     /* consume whatever's left in the input buffer */
164     if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
165         return -1;
166 
167     /* compress len zeros (len guaranteed > 0) */
168     first = 1;
169     while (len) {
170         n = GT_OFF(state->size) || (z_off64_t)state->size > len ?
171             (unsigned)len : state->size;
172         if (first) {
173             memset(state->in, 0, n);
174             first = 0;
175         }
176         strm->avail_in = n;
177         strm->next_in = state->in;
178         state->x.pos += n;
179         if (gz_comp(state, Z_NO_FLUSH) == -1)
180             return -1;
181         len -= n;
182     }
183     return 0;
184 }
185 
186 /* Write len bytes from buf to file.  Return the number of bytes written.  If
187    the returned value is less than len, then there was an error. */
188 local z_size_t gz_write(state, buf, len)
189     gz_statep state;
190     voidpc buf;
191     z_size_t len;
192 {
193     z_size_t put = len;
194 
195     /* if len is zero, avoid unnecessary operations */
196     if (len == 0)
197         return 0;
198 
199     /* allocate memory if this is the first time through */
200     if (state->size == 0 && gz_init(state) == -1)
201         return 0;
202 
203     /* check for seek request */
204     if (state->seek) {
205         state->seek = 0;
206         if (gz_zero(state, state->skip) == -1)
207             return 0;
208     }
209 
210     /* for small len, copy to input buffer, otherwise compress directly */
211     if (len < state->size) {
212         /* copy to input buffer, compress when full */
213         do {
214             unsigned have, copy;
215 
216             if (state->strm.avail_in == 0)
217                 state->strm.next_in = state->in;
218             have = (unsigned)((state->strm.next_in + state->strm.avail_in) -
219                               state->in);
220             copy = state->size - have;
221             if (copy > len)
222                 copy = (unsigned)len;
223             memcpy(state->in + have, buf, copy);
224             state->strm.avail_in += copy;
225             state->x.pos += copy;
226             buf = (const char *)buf + copy;
227             len -= copy;
228             if (len && gz_comp(state, Z_NO_FLUSH) == -1)
229                 return 0;
230         } while (len);
231     }
232     else {
233         /* consume whatever's left in the input buffer */
234         if (state->strm.avail_in && gz_comp(state, Z_NO_FLUSH) == -1)
235             return 0;
236 
237         /* directly compress user buffer to file */
238         state->strm.next_in = (z_const Bytef *)buf;
239         do {
240             unsigned n = (unsigned)-1;
241             if (n > len)
242                 n = (unsigned)len;
243             state->strm.avail_in = n;
244             state->x.pos += n;
245             if (gz_comp(state, Z_NO_FLUSH) == -1)
246                 return 0;
247             len -= n;
248         } while (len);
249     }
250 
251     /* input was all buffered or compressed */
252     return put;
253 }
254 
255 /* -- see zlib.h -- */
256 int ZEXPORT gzwrite(file, buf, len)
257     gzFile file;
258     voidpc buf;
259     unsigned len;
260 {
261     gz_statep state;
262 
263     /* get internal structure */
264     if (file == NULL)
265         return 0;
266     state = (gz_statep)file;
267 
268     /* check that we're writing and that there's no error */
269     if (state->mode != GZ_WRITE || state->err != Z_OK)
270         return 0;
271 
272     /* since an int is returned, make sure len fits in one, otherwise return
273        with an error (this avoids a flaw in the interface) */
274     if ((int)len < 0) {
275         gz_error(state, Z_DATA_ERROR, "requested length does not fit in int");
276         return 0;
277     }
278 
279     /* write len bytes from buf (the return value will fit in an int) */
280     return (int)gz_write(state, buf, len);
281 }
282 
283 /* -- see zlib.h -- */
284 z_size_t ZEXPORT gzfwrite(buf, size, nitems, file)
285     voidpc buf;
286     z_size_t size;
287     z_size_t nitems;
288     gzFile file;
289 {
290     z_size_t len;
291     gz_statep state;
292 
293     /* get internal structure */
294     if (file == NULL)
295         return 0;
296     state = (gz_statep)file;
297 
298     /* check that we're writing and that there's no error */
299     if (state->mode != GZ_WRITE || state->err != Z_OK)
300         return 0;
301 
302     /* compute bytes to read -- error on overflow */
303     len = nitems * size;
304     if (size && len / size != nitems) {
305         gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t");
306         return 0;
307     }
308 
309     /* write len bytes to buf, return the number of full items written */
310     return len ? gz_write(state, buf, len) / size : 0;
311 }
312 
313 /* -- see zlib.h -- */
314 int ZEXPORT gzputc(file, c)
315     gzFile file;
316     int c;
317 {
318     unsigned have;
319     unsigned char buf[1];
320     gz_statep state;
321     z_streamp strm;
322 
323     /* get internal structure */
324     if (file == NULL)
325         return -1;
326     state = (gz_statep)file;
327     strm = &(state->strm);
328 
329     /* check that we're writing and that there's no error */
330     if (state->mode != GZ_WRITE || state->err != Z_OK)
331         return -1;
332 
333     /* check for seek request */
334     if (state->seek) {
335         state->seek = 0;
336         if (gz_zero(state, state->skip) == -1)
337             return -1;
338     }
339 
340     /* try writing to input buffer for speed (state->size == 0 if buffer not
341        initialized) */
342     if (state->size) {
343         if (strm->avail_in == 0)
344             strm->next_in = state->in;
345         have = (unsigned)((strm->next_in + strm->avail_in) - state->in);
346         if (have < state->size) {
347             state->in[have] = (unsigned char)c;
348             strm->avail_in++;
349             state->x.pos++;
350             return c & 0xff;
351         }
352     }
353 
354     /* no room in buffer or not initialized, use gz_write() */
355     buf[0] = (unsigned char)c;
356     if (gz_write(state, buf, 1) != 1)
357         return -1;
358     return c & 0xff;
359 }
360 
361 /* -- see zlib.h -- */
362 int ZEXPORT gzputs(file, s)
363     gzFile file;
364     const char *s;
365 {
366     z_size_t len, put;
367     gz_statep state;
368 
369     /* get internal structure */
370     if (file == NULL)
371         return -1;
372     state = (gz_statep)file;
373 
374     /* check that we're writing and that there's no error */
375     if (state->mode != GZ_WRITE || state->err != Z_OK)
376         return -1;
377 
378     /* write string */
379     len = strlen(s);
380     if ((int)len < 0 || (unsigned)len != len) {
381         gz_error(state, Z_STREAM_ERROR, "string length does not fit in int");
382         return -1;
383     }
384     put = gz_write(state, s, len);
385     return put < len ? -1 : (int)len;
386 }
387 
388 #if defined(STDC) || defined(Z_HAVE_STDARG_H)
389 #include <stdarg.h>
390 
391 /* -- see zlib.h -- */
392 int ZEXPORTVA gzvprintf(gzFile file, const char *format, va_list va)
393 {
394     int len;
395     unsigned left;
396     char *next;
397     gz_statep state;
398     z_streamp strm;
399 
400     /* get internal structure */
401     if (file == NULL)
402         return Z_STREAM_ERROR;
403     state = (gz_statep)file;
404     strm = &(state->strm);
405 
406     /* check that we're writing and that there's no error */
407     if (state->mode != GZ_WRITE || state->err != Z_OK)
408         return Z_STREAM_ERROR;
409 
410     /* make sure we have some buffer space */
411     if (state->size == 0 && gz_init(state) == -1)
412         return state->err;
413 
414     /* check for seek request */
415     if (state->seek) {
416         state->seek = 0;
417         if (gz_zero(state, state->skip) == -1)
418             return state->err;
419     }
420 
421     /* do the printf() into the input buffer, put length in len -- the input
422        buffer is double-sized just for this function, so there is guaranteed to
423        be state->size bytes available after the current contents */
424     if (strm->avail_in == 0)
425         strm->next_in = state->in;
426     next = (char *)(state->in + (strm->next_in - state->in) + strm->avail_in);
427     next[state->size - 1] = 0;
428 #ifdef NO_vsnprintf
429 #  ifdef HAS_vsprintf_void
430     (void)vsprintf(next, format, va);
431     for (len = 0; len < state->size; len++)
432         if (next[len] == 0) break;
433 #  else
434     len = vsprintf(next, format, va);
435 #  endif
436 #else
437 #  ifdef HAS_vsnprintf_void
438     (void)vsnprintf(next, state->size, format, va);
439     len = strlen(next);
440 #  else
441     len = vsnprintf(next, state->size, format, va);
442 #  endif
443 #endif
444 
445     /* check that printf() results fit in buffer */
446     if (len == 0 || (unsigned)len >= state->size || next[state->size - 1] != 0)
447         return 0;
448 
449     /* update buffer and position, compress first half if past that */
450     strm->avail_in += (unsigned)len;
451     state->x.pos += len;
452     if (strm->avail_in >= state->size) {
453         left = strm->avail_in - state->size;
454         strm->avail_in = state->size;
455         if (gz_comp(state, Z_NO_FLUSH) == -1)
456             return state->err;
457         memmove(state->in, state->in + state->size, left);
458         strm->next_in = state->in;
459         strm->avail_in = left;
460     }
461     return len;
462 }
463 
464 int ZEXPORTVA gzprintf(gzFile file, const char *format, ...)
465 {
466     va_list va;
467     int ret;
468 
469     va_start(va, format);
470     ret = gzvprintf(file, format, va);
471     va_end(va);
472     return ret;
473 }
474 
475 #else /* !STDC && !Z_HAVE_STDARG_H */
476 
477 /* -- see zlib.h -- */
478 int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
479                        a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
480     gzFile file;
481     const char *format;
482     int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
483         a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
484 {
485     unsigned len, left;
486     char *next;
487     gz_statep state;
488     z_streamp strm;
489 
490     /* get internal structure */
491     if (file == NULL)
492         return Z_STREAM_ERROR;
493     state = (gz_statep)file;
494     strm = &(state->strm);
495 
496     /* check that can really pass pointer in ints */
497     if (sizeof(int) != sizeof(void *))
498         return Z_STREAM_ERROR;
499 
500     /* check that we're writing and that there's no error */
501     if (state->mode != GZ_WRITE || state->err != Z_OK)
502         return Z_STREAM_ERROR;
503 
504     /* make sure we have some buffer space */
505     if (state->size == 0 && gz_init(state) == -1)
506         return state->error;
507 
508     /* check for seek request */
509     if (state->seek) {
510         state->seek = 0;
511         if (gz_zero(state, state->skip) == -1)
512             return state->error;
513     }
514 
515     /* do the printf() into the input buffer, put length in len -- the input
516        buffer is double-sized just for this function, so there is guaranteed to
517        be state->size bytes available after the current contents */
518     if (strm->avail_in == 0)
519         strm->next_in = state->in;
520     next = (char *)(strm->next_in + strm->avail_in);
521     next[state->size - 1] = 0;
522 #ifdef NO_snprintf
523 #  ifdef HAS_sprintf_void
524     sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12,
525             a13, a14, a15, a16, a17, a18, a19, a20);
526     for (len = 0; len < size; len++)
527         if (next[len] == 0)
528             break;
529 #  else
530     len = sprintf(next, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
531                   a12, a13, a14, a15, a16, a17, a18, a19, a20);
532 #  endif
533 #else
534 #  ifdef HAS_snprintf_void
535     snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8, a9,
536              a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
537     len = strlen(next);
538 #  else
539     len = snprintf(next, state->size, format, a1, a2, a3, a4, a5, a6, a7, a8,
540                    a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
541 #  endif
542 #endif
543 
544     /* check that printf() results fit in buffer */
545     if (len == 0 || len >= state->size || next[state->size - 1] != 0)
546         return 0;
547 
548     /* update buffer and position, compress first half if past that */
549     strm->avail_in += len;
550     state->x.pos += len;
551     if (strm->avail_in >= state->size) {
552         left = strm->avail_in - state->size;
553         strm->avail_in = state->size;
554         if (gz_comp(state, Z_NO_FLUSH) == -1)
555             return state->err;
556         memmove(state->in, state->in + state->size, left);
557         strm->next_in = state->in;
558         strm->avail_in = left;
559     }
560     return (int)len;
561 }
562 
563 #endif
564 
565 /* -- see zlib.h -- */
566 int ZEXPORT gzflush(file, flush)
567     gzFile file;
568     int flush;
569 {
570     gz_statep state;
571 
572     /* get internal structure */
573     if (file == NULL)
574         return Z_STREAM_ERROR;
575     state = (gz_statep)file;
576 
577     /* check that we're writing and that there's no error */
578     if (state->mode != GZ_WRITE || state->err != Z_OK)
579         return Z_STREAM_ERROR;
580 
581     /* check flush parameter */
582     if (flush < 0 || flush > Z_FINISH)
583         return Z_STREAM_ERROR;
584 
585     /* check for seek request */
586     if (state->seek) {
587         state->seek = 0;
588         if (gz_zero(state, state->skip) == -1)
589             return state->err;
590     }
591 
592     /* compress remaining data with requested flush */
593     (void)gz_comp(state, flush);
594     return state->err;
595 }
596 
597 /* -- see zlib.h -- */
598 int ZEXPORT gzsetparams(file, level, strategy)
599     gzFile file;
600     int level;
601     int strategy;
602 {
603     gz_statep state;
604     z_streamp strm;
605 
606     /* get internal structure */
607     if (file == NULL)
608         return Z_STREAM_ERROR;
609     state = (gz_statep)file;
610     strm = &(state->strm);
611 
612     /* check that we're writing and that there's no error */
613     if (state->mode != GZ_WRITE || state->err != Z_OK)
614         return Z_STREAM_ERROR;
615 
616     /* if no change is requested, then do nothing */
617     if (level == state->level && strategy == state->strategy)
618         return Z_OK;
619 
620     /* check for seek request */
621     if (state->seek) {
622         state->seek = 0;
623         if (gz_zero(state, state->skip) == -1)
624             return state->err;
625     }
626 
627     /* change compression parameters for subsequent input */
628     if (state->size) {
629         /* flush previous input with previous parameters before changing */
630         if (strm->avail_in && gz_comp(state, Z_BLOCK) == -1)
631             return state->err;
632         deflateParams(strm, level, strategy);
633     }
634     state->level = level;
635     state->strategy = strategy;
636     return Z_OK;
637 }
638 
639 /* -- see zlib.h -- */
640 int ZEXPORT gzclose_w(file)
641     gzFile file;
642 {
643     int ret = Z_OK;
644     gz_statep state;
645 
646     /* get internal structure */
647     if (file == NULL)
648         return Z_STREAM_ERROR;
649     state = (gz_statep)file;
650 
651     /* check that we're writing */
652     if (state->mode != GZ_WRITE)
653         return Z_STREAM_ERROR;
654 
655     /* check for seek request */
656     if (state->seek) {
657         state->seek = 0;
658         if (gz_zero(state, state->skip) == -1)
659             ret = state->err;
660     }
661 
662     /* flush, free memory, and close file */
663     if (gz_comp(state, Z_FINISH) == -1)
664         ret = state->err;
665     if (state->size) {
666         if (!state->direct) {
667             (void)deflateEnd(&(state->strm));
668             free(state->out);
669         }
670         free(state->in);
671     }
672     gz_error(state, Z_OK, NULL);
673     free(state->path);
674     if (close(state->fd) == -1)
675         ret = Z_ERRNO;
676     free(state);
677     return ret;
678 }
679