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_hash.h"
35 #include "got_lib_inflate.h"
36 #include "got_lib_poll.h"
39 #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
42 static const struct got_error *
43 wrap_inflate_error(int zerr, const char *prefix)
46 return got_error_from_errno(prefix);
47 if (zerr == Z_MEM_ERROR)
48 return got_error_set_errno(ENOMEM, prefix);
50 return got_error(GOT_ERR_DECOMPRESSION);
53 const struct got_error *
54 got_inflate_init(struct got_inflate_buf *zb, uint8_t *outbuf, size_t bufsize,
55 struct got_inflate_checksum *csum)
57 const struct got_error *err = NULL;
60 memset(zb, 0, sizeof(*zb));
62 zb->z.zalloc = Z_NULL;
64 zerr = inflateInit(&zb->z);
66 return wrap_inflate_error(zerr, "inflateInit");
68 zb->inlen = zb->outlen = bufsize;
70 zb->inbuf = calloc(1, zb->inlen);
71 if (zb->inbuf == NULL) {
72 err = got_error_from_errno("calloc");
78 zb->outbuf = calloc(1, zb->outlen);
79 if (zb->outbuf == NULL) {
80 err = got_error_from_errno("calloc");
83 zb->flags |= GOT_INFLATE_F_OWN_OUTBUF;
95 csum_input(struct got_inflate_checksum *csum, const uint8_t *buf, size_t len)
98 *csum->input_crc = crc32(*csum->input_crc, buf, len);
101 got_hash_update(csum->input_ctx, buf, len);
105 csum_output(struct got_inflate_checksum *csum, const uint8_t *buf, size_t len)
107 if (csum->output_crc)
108 *csum->output_crc = crc32(*csum->output_crc, buf, len);
110 if (csum->output_ctx)
111 got_hash_update(csum->output_ctx, buf, len);
114 const struct got_error *
115 got_inflate_read(struct got_inflate_buf *zb, FILE *f, size_t *outlenp,
118 size_t last_total_out = zb->z.total_out;
119 size_t last_total_in = zb->z.total_in;
120 z_stream *z = &zb->z;
123 z->next_out = zb->outbuf;
124 z->avail_out = zb->outlen;
130 uint8_t *csum_in = NULL, *csum_out = NULL;
131 size_t csum_avail_in = 0, csum_avail_out = 0;
133 if (z->avail_in == 0) {
134 size_t n = fread(zb->inbuf, 1, zb->inlen, f);
137 return got_ferror(f, GOT_ERR_IO);
142 z->next_in = zb->inbuf;
146 csum_in = z->next_in;
147 csum_avail_in = z->avail_in;
148 csum_out = z->next_out;
149 csum_avail_out = z->avail_out;
151 ret = inflate(z, Z_SYNC_FLUSH);
153 csum_input(zb->csum, csum_in,
154 csum_avail_in - z->avail_in);
155 csum_output(zb->csum, csum_out,
156 csum_avail_out - z->avail_out);
158 } while (ret == Z_OK && z->avail_out > 0);
160 if (ret == Z_OK || ret == Z_BUF_ERROR) {
161 zb->flags |= GOT_INFLATE_F_HAVE_MORE;
163 if (ret != Z_STREAM_END)
164 return wrap_inflate_error(ret, "inflate");
165 zb->flags &= ~GOT_INFLATE_F_HAVE_MORE;
168 *outlenp = z->total_out - last_total_out;
170 *consumed += z->total_in - last_total_in;
174 const struct got_error *
175 got_inflate_read_fd(struct got_inflate_buf *zb, int fd, size_t *outlenp,
178 const struct got_error *err = NULL;
179 size_t last_total_out = zb->z.total_out;
180 size_t last_total_in = zb->z.total_in;
181 z_stream *z = &zb->z;
184 z->next_out = zb->outbuf;
185 z->avail_out = zb->outlen;
191 uint8_t *csum_in = NULL, *csum_out = NULL;
192 size_t csum_avail_in = 0, csum_avail_out = 0;
194 if (z->avail_in == 0) {
196 err = got_poll_fd(fd, POLLIN, INFTIM);
198 if (err->code == GOT_ERR_EOF) {
204 n = read(fd, zb->inbuf, zb->inlen);
206 return got_error_from_errno("read");
212 z->next_in = zb->inbuf;
216 csum_in = z->next_in;
217 csum_avail_in = z->avail_in;
218 csum_out = z->next_out;
219 csum_avail_out = z->avail_out;
221 ret = inflate(z, Z_SYNC_FLUSH);
223 csum_input(zb->csum, csum_in,
224 csum_avail_in - z->avail_in);
225 csum_output(zb->csum, csum_out,
226 csum_avail_out - z->avail_out);
228 } while (ret == Z_OK && z->avail_out > 0);
230 if (ret == Z_OK || ret == Z_BUF_ERROR) {
231 zb->flags |= GOT_INFLATE_F_HAVE_MORE;
233 if (ret != Z_STREAM_END)
234 return wrap_inflate_error(ret, "inflate");
235 zb->flags &= ~GOT_INFLATE_F_HAVE_MORE;
238 *outlenp = z->total_out - last_total_out;
240 *consumed += z->total_in - last_total_in;
244 const struct got_error *
245 got_inflate_read_mmap(struct got_inflate_buf *zb, uint8_t *map, size_t offset,
246 size_t len, size_t *outlenp, size_t *consumed)
248 size_t last_total_out = zb->z.total_out;
249 z_stream *z = &zb->z;
252 z->next_out = zb->outbuf;
253 z->avail_out = zb->outlen;
259 uint8_t *csum_in = NULL, *csum_out = NULL;
260 size_t csum_avail_in = 0, csum_avail_out = 0;
261 size_t last_total_in = zb->z.total_in;
263 if (z->avail_in == 0) {
269 z->next_in = map + offset + *consumed;
270 if (len - *consumed > UINT_MAX)
271 z->avail_in = UINT_MAX;
273 z->avail_in = len - *consumed;
276 csum_in = z->next_in;
277 csum_avail_in = z->avail_in;
278 csum_out = z->next_out;
279 csum_avail_out = z->avail_out;
281 ret = inflate(z, Z_SYNC_FLUSH);
283 csum_input(zb->csum, csum_in,
284 csum_avail_in - z->avail_in);
285 csum_output(zb->csum, csum_out,
286 csum_avail_out - z->avail_out);
288 *consumed += z->total_in - last_total_in;
289 } while (ret == Z_OK && z->avail_out > 0);
291 if (ret == Z_OK || ret == Z_BUF_ERROR) {
292 zb->flags |= GOT_INFLATE_F_HAVE_MORE;
294 if (ret != Z_STREAM_END)
295 return wrap_inflate_error(ret, "inflate");
296 zb->flags &= ~GOT_INFLATE_F_HAVE_MORE;
299 *outlenp = z->total_out - last_total_out;
304 got_inflate_end(struct got_inflate_buf *zb)
307 if (zb->flags & GOT_INFLATE_F_OWN_OUTBUF)
312 const struct got_error *
313 got_inflate_to_mem(uint8_t **outbuf, size_t *outlen,
314 size_t *consumed_total, struct got_inflate_checksum *csum, FILE *f)
316 const struct got_error *err;
317 size_t avail, consumed;
318 struct got_inflate_buf zb;
323 *outbuf = malloc(GOT_INFLATE_BUFSIZE);
325 return got_error_from_errno("malloc");
326 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
328 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
337 err = got_inflate_read(&zb, f, &avail, &consumed);
342 *consumed_total += consumed;
343 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
346 newbuf = reallocarray(*outbuf, ++nbuf,
347 GOT_INFLATE_BUFSIZE);
348 if (newbuf == NULL) {
349 err = got_error_from_errno("reallocarray");
356 zb.outbuf = newbuf + *outlen;
357 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
359 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
362 got_inflate_end(&zb);
366 const struct got_error *
367 got_inflate_to_mem_fd(uint8_t **outbuf, size_t *outlen,
368 size_t *consumed_total, struct got_inflate_checksum *csum,
369 size_t expected_size, int infd)
371 const struct got_error *err;
372 size_t avail, consumed;
373 struct got_inflate_buf zb;
376 size_t bufsize = GOT_INFLATE_BUFSIZE;
378 /* Optimize buffer size in case short reads should suffice. */
379 if (expected_size > 0 && expected_size < bufsize)
380 bufsize = expected_size;
383 *outbuf = malloc(bufsize);
385 return got_error_from_errno("malloc");
386 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
388 err = got_inflate_init(&zb, NULL, bufsize, csum);
397 err = got_inflate_read_fd(&zb, infd, &avail, &consumed);
402 *consumed_total += consumed;
403 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
406 newbuf = reallocarray(*outbuf, ++nbuf,
407 GOT_INFLATE_BUFSIZE);
408 if (newbuf == NULL) {
409 err = got_error_from_errno("reallocarray");
416 zb.outbuf = newbuf + *outlen;
417 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
419 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
422 got_inflate_end(&zb);
426 const struct got_error *
427 got_inflate_to_mem_mmap(uint8_t **outbuf, size_t *outlen,
428 size_t *consumed_total, struct got_inflate_checksum *csum, uint8_t *map,
429 size_t offset, size_t len)
431 const struct got_error *err;
432 size_t avail, consumed;
433 struct got_inflate_buf zb;
438 *outbuf = malloc(GOT_INFLATE_BUFSIZE);
440 return got_error_from_errno("malloc");
441 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
448 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
457 err = got_inflate_read_mmap(&zb, map, offset, len, &avail,
463 *consumed_total += consumed;
468 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
471 newbuf = reallocarray(*outbuf, ++nbuf,
472 GOT_INFLATE_BUFSIZE);
473 if (newbuf == NULL) {
474 err = got_error_from_errno("reallocarray");
481 zb.outbuf = newbuf + *outlen;
482 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
484 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
486 got_inflate_end(&zb);
490 const struct got_error *
491 got_inflate_to_fd(size_t *outlen, FILE *infile,
492 struct got_inflate_checksum *csum, int outfd)
494 const struct got_error *err = NULL;
496 struct got_inflate_buf zb;
498 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
505 err = got_inflate_read(&zb, infile, &avail, NULL);
510 n = write(outfd, zb.outbuf, avail);
512 err = got_error_from_errno("write");
517 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
521 if (lseek(outfd, SEEK_SET, 0) == -1)
522 err = got_error_from_errno("lseek");
524 got_inflate_end(&zb);
528 const struct got_error *
529 got_inflate_to_file(size_t *outlen, FILE *infile,
530 struct got_inflate_checksum *csum, FILE *outfile)
532 const struct got_error *err;
534 struct got_inflate_buf zb;
536 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
543 err = got_inflate_read(&zb, infile, &avail, NULL);
548 n = fwrite(zb.outbuf, avail, 1, outfile);
550 err = got_ferror(outfile, GOT_ERR_IO);
555 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
560 got_inflate_end(&zb);
564 const struct got_error *
565 got_inflate_to_file_fd(size_t *outlen, size_t *consumed_total,
566 struct got_inflate_checksum *csum, int infd, FILE *outfile)
568 const struct got_error *err;
569 size_t avail, consumed;
570 struct got_inflate_buf zb;
572 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
580 err = got_inflate_read_fd(&zb, infd, &avail, &consumed);
585 n = fwrite(zb.outbuf, avail, 1, outfile);
587 err = got_ferror(outfile, GOT_ERR_IO);
592 *consumed_total += consumed;
594 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
599 got_inflate_end(&zb);
603 const struct got_error *
604 got_inflate_to_file_mmap(size_t *outlen, size_t *consumed_total,
605 struct got_inflate_checksum *csum, uint8_t *map, size_t offset,
606 size_t len, FILE *outfile)
608 const struct got_error *err;
609 size_t avail, consumed;
610 struct got_inflate_buf zb;
612 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
620 err = got_inflate_read_mmap(&zb, map, offset, len, &avail,
626 *consumed_total += consumed;
630 n = fwrite(zb.outbuf, avail, 1, outfile);
632 err = got_ferror(outfile, GOT_ERR_IO);
637 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
642 got_inflate_end(&zb);