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>
30 #include "got_error.h"
31 #include "got_object.h"
34 #include "got_lib_inflate.h"
35 #include "got_lib_poll.h"
38 #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
41 const struct got_error *
42 got_inflate_init(struct got_inflate_buf *zb, uint8_t *outbuf, size_t bufsize,
43 struct got_inflate_checksum *csum)
45 const struct got_error *err = NULL;
48 memset(zb, 0, sizeof(*zb));
50 zb->z.zalloc = Z_NULL;
52 zerr = inflateInit(&zb->z);
55 return got_error_from_errno("inflateInit");
56 if (zerr == Z_MEM_ERROR) {
58 return got_error_from_errno("inflateInit");
60 return got_error(GOT_ERR_DECOMPRESSION);
63 zb->inlen = zb->outlen = bufsize;
65 zb->inbuf = calloc(1, zb->inlen);
66 if (zb->inbuf == NULL) {
67 err = got_error_from_errno("calloc");
73 zb->outbuf = calloc(1, zb->outlen);
74 if (zb->outbuf == NULL) {
75 err = got_error_from_errno("calloc");
78 zb->flags |= GOT_INFLATE_F_OWN_OUTBUF;
90 csum_input(struct got_inflate_checksum *csum, const uint8_t *buf, size_t len)
93 *csum->input_crc = crc32(*csum->input_crc, buf, len);
96 SHA1Update(csum->input_sha1, buf, len);
100 csum_output(struct got_inflate_checksum *csum, const uint8_t *buf, size_t len)
102 if (csum->output_crc)
103 *csum->output_crc = crc32(*csum->output_crc, buf, len);
105 if (csum->output_sha1)
106 SHA1Update(csum->output_sha1, buf, len);
109 const struct got_error *
110 got_inflate_read(struct got_inflate_buf *zb, FILE *f, size_t *outlenp,
113 size_t last_total_out = zb->z.total_out;
114 size_t last_total_in = zb->z.total_in;
115 z_stream *z = &zb->z;
118 z->next_out = zb->outbuf;
119 z->avail_out = zb->outlen;
125 uint8_t *csum_in = NULL, *csum_out = NULL;
126 size_t csum_avail_in = 0, csum_avail_out = 0;
128 if (z->avail_in == 0) {
129 size_t n = fread(zb->inbuf, 1, zb->inlen, f);
132 return got_ferror(f, GOT_ERR_IO);
137 z->next_in = zb->inbuf;
141 csum_in = z->next_in;
142 csum_avail_in = z->avail_in;
143 csum_out = z->next_out;
144 csum_avail_out = z->avail_out;
146 ret = inflate(z, Z_SYNC_FLUSH);
148 csum_input(zb->csum, csum_in,
149 csum_avail_in - z->avail_in);
150 csum_output(zb->csum, csum_out,
151 csum_avail_out - z->avail_out);
153 } while (ret == Z_OK && z->avail_out > 0);
155 if (ret == Z_OK || ret == Z_BUF_ERROR) {
156 zb->flags |= GOT_INFLATE_F_HAVE_MORE;
158 if (ret != Z_STREAM_END)
159 return got_error(GOT_ERR_DECOMPRESSION);
160 zb->flags &= ~GOT_INFLATE_F_HAVE_MORE;
163 *outlenp = z->total_out - last_total_out;
165 *consumed += z->total_in - last_total_in;
169 const struct got_error *
170 got_inflate_read_fd(struct got_inflate_buf *zb, int fd, size_t *outlenp,
173 const struct got_error *err = NULL;
174 size_t last_total_out = zb->z.total_out;
175 size_t last_total_in = zb->z.total_in;
176 z_stream *z = &zb->z;
179 z->next_out = zb->outbuf;
180 z->avail_out = zb->outlen;
186 uint8_t *csum_in = NULL, *csum_out = NULL;
187 size_t csum_avail_in = 0, csum_avail_out = 0;
189 if (z->avail_in == 0) {
191 err = got_poll_fd(fd, POLLIN, INFTIM);
193 if (err->code == GOT_ERR_EOF) {
199 n = read(fd, zb->inbuf, zb->inlen);
201 return got_error_from_errno("read");
207 z->next_in = zb->inbuf;
211 csum_in = z->next_in;
212 csum_avail_in = z->avail_in;
213 csum_out = z->next_out;
214 csum_avail_out = z->avail_out;
216 ret = inflate(z, Z_SYNC_FLUSH);
218 csum_input(zb->csum, csum_in,
219 csum_avail_in - z->avail_in);
220 csum_output(zb->csum, csum_out,
221 csum_avail_out - z->avail_out);
223 } while (ret == Z_OK && z->avail_out > 0);
225 if (ret == Z_OK || ret == Z_BUF_ERROR) {
226 zb->flags |= GOT_INFLATE_F_HAVE_MORE;
228 if (ret != Z_STREAM_END)
229 return got_error(GOT_ERR_DECOMPRESSION);
230 zb->flags &= ~GOT_INFLATE_F_HAVE_MORE;
233 *outlenp = z->total_out - last_total_out;
235 *consumed += z->total_in - last_total_in;
239 const struct got_error *
240 got_inflate_read_mmap(struct got_inflate_buf *zb, uint8_t *map, size_t offset,
241 size_t len, size_t *outlenp, size_t *consumed)
243 size_t last_total_out = zb->z.total_out;
244 z_stream *z = &zb->z;
247 z->next_out = zb->outbuf;
248 z->avail_out = zb->outlen;
254 uint8_t *csum_in = NULL, *csum_out = NULL;
255 size_t csum_avail_in = 0, csum_avail_out = 0;
256 size_t last_total_in = zb->z.total_in;
258 if (z->avail_in == 0) {
264 z->next_in = map + offset + *consumed;
265 if (len - *consumed > UINT_MAX)
266 z->avail_in = UINT_MAX;
268 z->avail_in = len - *consumed;
271 csum_in = z->next_in;
272 csum_avail_in = z->avail_in;
273 csum_out = z->next_out;
274 csum_avail_out = z->avail_out;
276 ret = inflate(z, Z_SYNC_FLUSH);
278 csum_input(zb->csum, csum_in,
279 csum_avail_in - z->avail_in);
280 csum_output(zb->csum, csum_out,
281 csum_avail_out - z->avail_out);
283 *consumed += z->total_in - last_total_in;
284 } while (ret == Z_OK && z->avail_out > 0);
286 if (ret == Z_OK || ret == Z_BUF_ERROR) {
287 zb->flags |= GOT_INFLATE_F_HAVE_MORE;
289 if (ret != Z_STREAM_END)
290 return got_error(GOT_ERR_DECOMPRESSION);
291 zb->flags &= ~GOT_INFLATE_F_HAVE_MORE;
294 *outlenp = z->total_out - last_total_out;
299 got_inflate_end(struct got_inflate_buf *zb)
302 if (zb->flags & GOT_INFLATE_F_OWN_OUTBUF)
307 const struct got_error *
308 got_inflate_to_mem(uint8_t **outbuf, size_t *outlen,
309 size_t *consumed_total, struct got_inflate_checksum *csum, FILE *f)
311 const struct got_error *err;
312 size_t avail, consumed;
313 struct got_inflate_buf zb;
318 *outbuf = malloc(GOT_INFLATE_BUFSIZE);
320 return got_error_from_errno("malloc");
321 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
323 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
332 err = got_inflate_read(&zb, f, &avail, &consumed);
337 *consumed_total += consumed;
338 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
341 newbuf = reallocarray(*outbuf, ++nbuf,
342 GOT_INFLATE_BUFSIZE);
343 if (newbuf == NULL) {
344 err = got_error_from_errno("reallocarray");
351 zb.outbuf = newbuf + *outlen;
352 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
354 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
357 got_inflate_end(&zb);
361 const struct got_error *
362 got_inflate_to_mem_fd(uint8_t **outbuf, size_t *outlen,
363 size_t *consumed_total, struct got_inflate_checksum *csum,
364 size_t expected_size, int infd)
366 const struct got_error *err;
367 size_t avail, consumed;
368 struct got_inflate_buf zb;
371 size_t bufsize = GOT_INFLATE_BUFSIZE;
373 /* Optimize buffer size in case short reads should suffice. */
374 if (expected_size > 0 && expected_size < bufsize)
375 bufsize = expected_size;
378 *outbuf = malloc(bufsize);
380 return got_error_from_errno("malloc");
381 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
383 err = got_inflate_init(&zb, NULL, bufsize, csum);
392 err = got_inflate_read_fd(&zb, infd, &avail, &consumed);
397 *consumed_total += consumed;
398 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
401 newbuf = reallocarray(*outbuf, ++nbuf,
402 GOT_INFLATE_BUFSIZE);
403 if (newbuf == NULL) {
404 err = got_error_from_errno("reallocarray");
411 zb.outbuf = newbuf + *outlen;
412 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
414 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
417 got_inflate_end(&zb);
421 const struct got_error *
422 got_inflate_to_mem_mmap(uint8_t **outbuf, size_t *outlen,
423 size_t *consumed_total, struct got_inflate_checksum *csum, uint8_t *map,
424 size_t offset, size_t len)
426 const struct got_error *err;
427 size_t avail, consumed;
428 struct got_inflate_buf zb;
433 *outbuf = malloc(GOT_INFLATE_BUFSIZE);
435 return got_error_from_errno("malloc");
436 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
443 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
452 err = got_inflate_read_mmap(&zb, map, offset, len, &avail,
458 *consumed_total += consumed;
463 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
466 newbuf = reallocarray(*outbuf, ++nbuf,
467 GOT_INFLATE_BUFSIZE);
468 if (newbuf == NULL) {
469 err = got_error_from_errno("reallocarray");
476 zb.outbuf = newbuf + *outlen;
477 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
479 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
481 got_inflate_end(&zb);
485 const struct got_error *
486 got_inflate_to_fd(size_t *outlen, FILE *infile,
487 struct got_inflate_checksum *csum, int outfd)
489 const struct got_error *err = NULL;
491 struct got_inflate_buf zb;
493 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
500 err = got_inflate_read(&zb, infile, &avail, NULL);
505 n = write(outfd, zb.outbuf, avail);
507 err = got_error_from_errno("write");
512 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
516 if (lseek(outfd, SEEK_SET, 0) == -1)
517 err = got_error_from_errno("lseek");
519 got_inflate_end(&zb);
523 const struct got_error *
524 got_inflate_to_file(size_t *outlen, FILE *infile,
525 struct got_inflate_checksum *csum, FILE *outfile)
527 const struct got_error *err;
529 struct got_inflate_buf zb;
531 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
538 err = got_inflate_read(&zb, infile, &avail, NULL);
543 n = fwrite(zb.outbuf, avail, 1, outfile);
545 err = got_ferror(outfile, GOT_ERR_IO);
550 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
555 got_inflate_end(&zb);
559 const struct got_error *
560 got_inflate_to_file_fd(size_t *outlen, size_t *consumed_total,
561 struct got_inflate_checksum *csum, int infd, FILE *outfile)
563 const struct got_error *err;
564 size_t avail, consumed;
565 struct got_inflate_buf zb;
567 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
575 err = got_inflate_read_fd(&zb, infd, &avail, &consumed);
580 n = fwrite(zb.outbuf, avail, 1, outfile);
582 err = got_ferror(outfile, GOT_ERR_IO);
587 *consumed_total += consumed;
589 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
594 got_inflate_end(&zb);
598 const struct got_error *
599 got_inflate_to_file_mmap(size_t *outlen, size_t *consumed_total,
600 struct got_inflate_checksum *csum, uint8_t *map, size_t offset,
601 size_t len, FILE *outfile)
603 const struct got_error *err;
604 size_t avail, consumed;
605 struct got_inflate_buf zb;
607 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
615 err = got_inflate_read_mmap(&zb, map, offset, len, &avail,
621 *consumed_total += consumed;
625 n = fwrite(zb.outbuf, avail, 1, outfile);
627 err = got_ferror(outfile, GOT_ERR_IO);
632 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
637 got_inflate_end(&zb);