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