2 * Copyright (c) 2018 Stefan Sperling <stsp@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include <sys/queue.h>
29 #include "got_error.h"
30 #include "got_object.h"
33 #include "got_lib_inflate.h"
34 #include "got_lib_poll.h"
37 #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
40 const struct got_error *
41 got_inflate_init(struct got_inflate_buf *zb, uint8_t *outbuf, size_t bufsize,
42 struct got_inflate_checksum *csum)
44 const struct got_error *err = NULL;
47 memset(&zb->z, 0, sizeof(zb->z));
49 zb->z.zalloc = Z_NULL;
51 zerr = inflateInit(&zb->z);
54 return got_error_from_errno("inflateInit");
55 if (zerr == Z_MEM_ERROR) {
57 return got_error_from_errno("inflateInit");
59 return got_error(GOT_ERR_DECOMPRESSION);
62 zb->inlen = zb->outlen = bufsize;
64 zb->inbuf = calloc(1, zb->inlen);
65 if (zb->inbuf == NULL) {
66 err = got_error_from_errno("calloc");
72 zb->outbuf = calloc(1, zb->outlen);
73 if (zb->outbuf == NULL) {
74 err = got_error_from_errno("calloc");
77 zb->flags |= GOT_INFLATE_F_OWN_OUTBUF;
89 csum_input(struct got_inflate_checksum *csum, const uint8_t *buf, size_t len)
92 *csum->input_crc = crc32(*csum->input_crc, buf, len);
95 SHA1Update(csum->input_sha1, buf, len);
99 csum_output(struct got_inflate_checksum *csum, const uint8_t *buf, size_t len)
101 if (csum->output_crc)
102 *csum->output_crc = crc32(*csum->output_crc, buf, len);
104 if (csum->output_sha1)
105 SHA1Update(csum->output_sha1, buf, len);
108 const struct got_error *
109 got_inflate_read(struct got_inflate_buf *zb, FILE *f, size_t *outlenp,
112 size_t last_total_out = zb->z.total_out;
113 size_t last_total_in = zb->z.total_in;
114 z_stream *z = &zb->z;
117 z->next_out = zb->outbuf;
118 z->avail_out = zb->outlen;
124 uint8_t *csum_in = NULL, *csum_out = NULL;
125 size_t csum_avail_in = 0, csum_avail_out = 0;
127 if (z->avail_in == 0) {
128 size_t n = fread(zb->inbuf, 1, zb->inlen, f);
131 return got_ferror(f, GOT_ERR_IO);
136 z->next_in = zb->inbuf;
140 csum_in = z->next_in;
141 csum_avail_in = z->avail_in;
142 csum_out = z->next_out;
143 csum_avail_out = z->avail_out;
145 ret = inflate(z, Z_SYNC_FLUSH);
147 csum_input(zb->csum, csum_in,
148 csum_avail_in - z->avail_in);
149 csum_output(zb->csum, csum_out,
150 csum_avail_out - z->avail_out);
152 } while (ret == Z_OK && z->avail_out > 0);
154 if (ret == Z_OK || ret == Z_BUF_ERROR) {
155 zb->flags |= GOT_INFLATE_F_HAVE_MORE;
157 if (ret != Z_STREAM_END)
158 return got_error(GOT_ERR_DECOMPRESSION);
159 zb->flags &= ~GOT_INFLATE_F_HAVE_MORE;
162 *outlenp = z->total_out - last_total_out;
164 *consumed += z->total_in - last_total_in;
168 const struct got_error *
169 got_inflate_read_fd(struct got_inflate_buf *zb, int fd, size_t *outlenp,
172 const struct got_error *err = NULL;
173 size_t last_total_out = zb->z.total_out;
174 size_t last_total_in = zb->z.total_in;
175 z_stream *z = &zb->z;
178 z->next_out = zb->outbuf;
179 z->avail_out = zb->outlen;
185 uint8_t *csum_in = NULL, *csum_out = NULL;
186 size_t csum_avail_in = 0, csum_avail_out = 0;
188 if (z->avail_in == 0) {
190 err = got_poll_fd(fd, POLLIN, INFTIM);
192 if (err->code == GOT_ERR_EOF) {
198 n = read(fd, zb->inbuf, zb->inlen);
200 return got_error_from_errno("read");
206 z->next_in = zb->inbuf;
210 csum_in = z->next_in;
211 csum_avail_in = z->avail_in;
212 csum_out = z->next_out;
213 csum_avail_out = z->avail_out;
215 ret = inflate(z, Z_SYNC_FLUSH);
217 csum_input(zb->csum, csum_in,
218 csum_avail_in - z->avail_in);
219 csum_output(zb->csum, csum_out,
220 csum_avail_out - z->avail_out);
222 } while (ret == Z_OK && z->avail_out > 0);
224 if (ret == Z_OK || ret == Z_BUF_ERROR) {
225 zb->flags |= GOT_INFLATE_F_HAVE_MORE;
227 if (ret != Z_STREAM_END)
228 return got_error(GOT_ERR_DECOMPRESSION);
229 zb->flags &= ~GOT_INFLATE_F_HAVE_MORE;
232 *outlenp = z->total_out - last_total_out;
234 *consumed += z->total_in - last_total_in;
238 const struct got_error *
239 got_inflate_read_mmap(struct got_inflate_buf *zb, uint8_t *map, size_t offset,
240 size_t len, size_t *outlenp, size_t *consumed)
242 size_t last_total_out = zb->z.total_out;
243 z_stream *z = &zb->z;
246 z->next_out = zb->outbuf;
247 z->avail_out = zb->outlen;
253 uint8_t *csum_in = NULL, *csum_out = NULL;
254 size_t csum_avail_in = 0, csum_avail_out = 0;
255 size_t last_total_in = zb->z.total_in;
257 if (z->avail_in == 0) {
263 z->next_in = map + offset + *consumed;
264 if (len - *consumed > UINT_MAX)
265 z->avail_in = UINT_MAX;
267 z->avail_in = len - *consumed;
270 csum_in = z->next_in;
271 csum_avail_in = z->avail_in;
272 csum_out = z->next_out;
273 csum_avail_out = z->avail_out;
275 ret = inflate(z, Z_SYNC_FLUSH);
277 csum_input(zb->csum, csum_in,
278 csum_avail_in - z->avail_in);
279 csum_output(zb->csum, csum_out,
280 csum_avail_out - z->avail_out);
282 *consumed += z->total_in - last_total_in;
283 } while (ret == Z_OK && z->avail_out > 0);
285 if (ret == Z_OK || ret == Z_BUF_ERROR) {
286 zb->flags |= GOT_INFLATE_F_HAVE_MORE;
288 if (ret != Z_STREAM_END)
289 return got_error(GOT_ERR_DECOMPRESSION);
290 zb->flags &= ~GOT_INFLATE_F_HAVE_MORE;
293 *outlenp = z->total_out - last_total_out;
298 got_inflate_end(struct got_inflate_buf *zb)
301 if (zb->flags & GOT_INFLATE_F_OWN_OUTBUF)
306 const struct got_error *
307 got_inflate_to_mem(uint8_t **outbuf, size_t *outlen,
308 size_t *consumed_total, struct got_inflate_checksum *csum, FILE *f)
310 const struct got_error *err;
311 size_t avail, consumed;
312 struct got_inflate_buf zb;
317 *outbuf = malloc(GOT_INFLATE_BUFSIZE);
319 return got_error_from_errno("malloc");
320 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
322 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
331 err = got_inflate_read(&zb, f, &avail, &consumed);
336 *consumed_total += consumed;
337 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
340 newbuf = reallocarray(*outbuf, ++nbuf,
341 GOT_INFLATE_BUFSIZE);
342 if (newbuf == NULL) {
343 err = got_error_from_errno("reallocarray");
350 zb.outbuf = newbuf + *outlen;
351 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
353 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
356 got_inflate_end(&zb);
360 const struct got_error *
361 got_inflate_to_mem_fd(uint8_t **outbuf, size_t *outlen,
362 size_t *consumed_total, struct got_inflate_checksum *csum,
363 size_t expected_size, int infd)
365 const struct got_error *err;
366 size_t avail, consumed;
367 struct got_inflate_buf zb;
370 size_t bufsize = GOT_INFLATE_BUFSIZE;
372 /* Optimize buffer size in case short reads should suffice. */
373 if (expected_size > 0 && expected_size < bufsize)
374 bufsize = expected_size;
377 *outbuf = malloc(bufsize);
379 return got_error_from_errno("malloc");
380 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
382 err = got_inflate_init(&zb, NULL, bufsize, csum);
391 err = got_inflate_read_fd(&zb, infd, &avail, &consumed);
396 *consumed_total += consumed;
397 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
400 newbuf = reallocarray(*outbuf, ++nbuf,
401 GOT_INFLATE_BUFSIZE);
402 if (newbuf == NULL) {
403 err = got_error_from_errno("reallocarray");
410 zb.outbuf = newbuf + *outlen;
411 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
413 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
416 got_inflate_end(&zb);
420 const struct got_error *
421 got_inflate_to_mem_mmap(uint8_t **outbuf, size_t *outlen,
422 size_t *consumed_total, struct got_inflate_checksum *csum, uint8_t *map,
423 size_t offset, size_t len)
425 const struct got_error *err;
426 size_t avail, consumed;
427 struct got_inflate_buf zb;
432 *outbuf = malloc(GOT_INFLATE_BUFSIZE);
434 return got_error_from_errno("malloc");
435 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
442 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
451 err = got_inflate_read_mmap(&zb, map, offset, len, &avail,
457 *consumed_total += consumed;
462 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
465 newbuf = reallocarray(*outbuf, ++nbuf,
466 GOT_INFLATE_BUFSIZE);
467 if (newbuf == NULL) {
468 err = got_error_from_errno("reallocarray");
475 zb.outbuf = newbuf + *outlen;
476 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
478 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
480 got_inflate_end(&zb);
484 const struct got_error *
485 got_inflate_to_fd(size_t *outlen, FILE *infile,
486 struct got_inflate_checksum *csum, int outfd)
488 const struct got_error *err = NULL;
490 struct got_inflate_buf zb;
492 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
499 err = got_inflate_read(&zb, infile, &avail, NULL);
504 n = write(outfd, zb.outbuf, avail);
506 err = got_error_from_errno("write");
511 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
515 if (lseek(outfd, SEEK_SET, 0) == -1)
516 err = got_error_from_errno("lseek");
518 got_inflate_end(&zb);
522 const struct got_error *
523 got_inflate_to_file(size_t *outlen, FILE *infile,
524 struct got_inflate_checksum *csum, FILE *outfile)
526 const struct got_error *err;
528 struct got_inflate_buf zb;
530 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
537 err = got_inflate_read(&zb, infile, &avail, NULL);
542 n = fwrite(zb.outbuf, avail, 1, outfile);
544 err = got_ferror(outfile, GOT_ERR_IO);
549 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
554 got_inflate_end(&zb);
558 const struct got_error *
559 got_inflate_to_file_fd(size_t *outlen, size_t *consumed_total,
560 struct got_inflate_checksum *csum, int infd, FILE *outfile)
562 const struct got_error *err;
563 size_t avail, consumed;
564 struct got_inflate_buf zb;
566 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
574 err = got_inflate_read_fd(&zb, infd, &avail, &consumed);
579 n = fwrite(zb.outbuf, avail, 1, outfile);
581 err = got_ferror(outfile, GOT_ERR_IO);
586 *consumed_total += consumed;
588 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
593 got_inflate_end(&zb);
597 const struct got_error *
598 got_inflate_to_file_mmap(size_t *outlen, size_t *consumed_total,
599 struct got_inflate_checksum *csum, uint8_t *map, size_t offset,
600 size_t len, FILE *outfile)
602 const struct got_error *err;
603 size_t avail, consumed;
604 struct got_inflate_buf zb;
606 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
614 err = got_inflate_read_mmap(&zb, map, offset, len, &avail,
620 *consumed_total += consumed;
624 n = fwrite(zb.outbuf, avail, 1, outfile);
626 err = got_ferror(outfile, GOT_ERR_IO);
631 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
636 got_inflate_end(&zb);