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 "got_compat.h"
19 #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 const struct got_error *
43 got_inflate_init(struct got_inflate_buf *zb, uint8_t *outbuf, size_t bufsize,
44 struct got_inflate_checksum *csum)
46 const struct got_error *err = NULL;
49 memset(zb, 0, sizeof(*zb));
51 zb->z.zalloc = Z_NULL;
53 zerr = inflateInit(&zb->z);
56 return got_error_from_errno("inflateInit");
57 if (zerr == Z_MEM_ERROR) {
59 return got_error_from_errno("inflateInit");
61 return got_error(GOT_ERR_DECOMPRESSION);
64 zb->inlen = zb->outlen = bufsize;
66 zb->inbuf = calloc(1, zb->inlen);
67 if (zb->inbuf == NULL) {
68 err = got_error_from_errno("calloc");
74 zb->outbuf = calloc(1, zb->outlen);
75 if (zb->outbuf == NULL) {
76 err = got_error_from_errno("calloc");
79 zb->flags |= GOT_INFLATE_F_OWN_OUTBUF;
91 csum_input(struct got_inflate_checksum *csum, const uint8_t *buf, size_t len)
94 *csum->input_crc = crc32(*csum->input_crc, buf, len);
97 got_hash_update(csum->input_ctx, buf, len);
101 csum_output(struct got_inflate_checksum *csum, const uint8_t *buf, size_t len)
103 if (csum->output_crc)
104 *csum->output_crc = crc32(*csum->output_crc, buf, len);
106 if (csum->output_ctx)
107 got_hash_update(csum->output_ctx, buf, len);
110 const struct got_error *
111 got_inflate_read(struct got_inflate_buf *zb, FILE *f, size_t *outlenp,
114 size_t last_total_out = zb->z.total_out;
115 size_t last_total_in = zb->z.total_in;
116 z_stream *z = &zb->z;
119 z->next_out = zb->outbuf;
120 z->avail_out = zb->outlen;
126 uint8_t *csum_in = NULL, *csum_out = NULL;
127 size_t csum_avail_in = 0, csum_avail_out = 0;
129 if (z->avail_in == 0) {
130 size_t n = fread(zb->inbuf, 1, zb->inlen, f);
133 return got_ferror(f, GOT_ERR_IO);
138 z->next_in = zb->inbuf;
142 csum_in = z->next_in;
143 csum_avail_in = z->avail_in;
144 csum_out = z->next_out;
145 csum_avail_out = z->avail_out;
147 ret = inflate(z, Z_SYNC_FLUSH);
149 csum_input(zb->csum, csum_in,
150 csum_avail_in - z->avail_in);
151 csum_output(zb->csum, csum_out,
152 csum_avail_out - z->avail_out);
154 } while (ret == Z_OK && z->avail_out > 0);
156 if (ret == Z_OK || ret == Z_BUF_ERROR) {
157 zb->flags |= GOT_INFLATE_F_HAVE_MORE;
159 if (ret != Z_STREAM_END)
160 return got_error(GOT_ERR_DECOMPRESSION);
161 zb->flags &= ~GOT_INFLATE_F_HAVE_MORE;
164 *outlenp = z->total_out - last_total_out;
166 *consumed += z->total_in - last_total_in;
170 const struct got_error *
171 got_inflate_read_fd(struct got_inflate_buf *zb, int fd, size_t *outlenp,
174 const struct got_error *err = NULL;
175 size_t last_total_out = zb->z.total_out;
176 size_t last_total_in = zb->z.total_in;
177 z_stream *z = &zb->z;
180 z->next_out = zb->outbuf;
181 z->avail_out = zb->outlen;
187 uint8_t *csum_in = NULL, *csum_out = NULL;
188 size_t csum_avail_in = 0, csum_avail_out = 0;
190 if (z->avail_in == 0) {
192 err = got_poll_fd(fd, POLLIN, INFTIM);
194 if (err->code == GOT_ERR_EOF) {
200 n = read(fd, zb->inbuf, zb->inlen);
202 return got_error_from_errno("read");
208 z->next_in = zb->inbuf;
212 csum_in = z->next_in;
213 csum_avail_in = z->avail_in;
214 csum_out = z->next_out;
215 csum_avail_out = z->avail_out;
217 ret = inflate(z, Z_SYNC_FLUSH);
219 csum_input(zb->csum, csum_in,
220 csum_avail_in - z->avail_in);
221 csum_output(zb->csum, csum_out,
222 csum_avail_out - z->avail_out);
224 } while (ret == Z_OK && z->avail_out > 0);
226 if (ret == Z_OK || ret == Z_BUF_ERROR) {
227 zb->flags |= GOT_INFLATE_F_HAVE_MORE;
229 if (ret != Z_STREAM_END)
230 return got_error(GOT_ERR_DECOMPRESSION);
231 zb->flags &= ~GOT_INFLATE_F_HAVE_MORE;
234 *outlenp = z->total_out - last_total_out;
236 *consumed += z->total_in - last_total_in;
240 const struct got_error *
241 got_inflate_read_mmap(struct got_inflate_buf *zb, uint8_t *map, size_t offset,
242 size_t len, size_t *outlenp, size_t *consumed)
244 size_t last_total_out = zb->z.total_out;
245 z_stream *z = &zb->z;
248 z->next_out = zb->outbuf;
249 z->avail_out = zb->outlen;
255 uint8_t *csum_in = NULL, *csum_out = NULL;
256 size_t csum_avail_in = 0, csum_avail_out = 0;
257 size_t last_total_in = zb->z.total_in;
259 if (z->avail_in == 0) {
265 z->next_in = map + offset + *consumed;
266 if (len - *consumed > UINT_MAX)
267 z->avail_in = UINT_MAX;
269 z->avail_in = len - *consumed;
272 csum_in = z->next_in;
273 csum_avail_in = z->avail_in;
274 csum_out = z->next_out;
275 csum_avail_out = z->avail_out;
277 ret = inflate(z, Z_SYNC_FLUSH);
279 csum_input(zb->csum, csum_in,
280 csum_avail_in - z->avail_in);
281 csum_output(zb->csum, csum_out,
282 csum_avail_out - z->avail_out);
284 *consumed += z->total_in - last_total_in;
285 } while (ret == Z_OK && z->avail_out > 0);
287 if (ret == Z_OK || ret == Z_BUF_ERROR) {
288 zb->flags |= GOT_INFLATE_F_HAVE_MORE;
290 if (ret != Z_STREAM_END)
291 return got_error(GOT_ERR_DECOMPRESSION);
292 zb->flags &= ~GOT_INFLATE_F_HAVE_MORE;
295 *outlenp = z->total_out - last_total_out;
300 got_inflate_end(struct got_inflate_buf *zb)
303 if (zb->flags & GOT_INFLATE_F_OWN_OUTBUF)
308 const struct got_error *
309 got_inflate_to_mem(uint8_t **outbuf, size_t *outlen,
310 size_t *consumed_total, struct got_inflate_checksum *csum, FILE *f)
312 const struct got_error *err;
313 size_t avail, consumed;
314 struct got_inflate_buf zb;
319 *outbuf = malloc(GOT_INFLATE_BUFSIZE);
321 return got_error_from_errno("malloc");
322 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
324 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
333 err = got_inflate_read(&zb, f, &avail, &consumed);
338 *consumed_total += consumed;
339 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
342 newbuf = reallocarray(*outbuf, ++nbuf,
343 GOT_INFLATE_BUFSIZE);
344 if (newbuf == NULL) {
345 err = got_error_from_errno("reallocarray");
352 zb.outbuf = newbuf + *outlen;
353 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
355 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
358 got_inflate_end(&zb);
362 const struct got_error *
363 got_inflate_to_mem_fd(uint8_t **outbuf, size_t *outlen,
364 size_t *consumed_total, struct got_inflate_checksum *csum,
365 size_t expected_size, int infd)
367 const struct got_error *err;
368 size_t avail, consumed;
369 struct got_inflate_buf zb;
372 size_t bufsize = GOT_INFLATE_BUFSIZE;
374 /* Optimize buffer size in case short reads should suffice. */
375 if (expected_size > 0 && expected_size < bufsize)
376 bufsize = expected_size;
379 *outbuf = malloc(bufsize);
381 return got_error_from_errno("malloc");
382 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
384 err = got_inflate_init(&zb, NULL, bufsize, csum);
393 err = got_inflate_read_fd(&zb, infd, &avail, &consumed);
398 *consumed_total += consumed;
399 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
402 newbuf = reallocarray(*outbuf, ++nbuf,
403 GOT_INFLATE_BUFSIZE);
404 if (newbuf == NULL) {
405 err = got_error_from_errno("reallocarray");
412 zb.outbuf = newbuf + *outlen;
413 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
415 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
418 got_inflate_end(&zb);
422 const struct got_error *
423 got_inflate_to_mem_mmap(uint8_t **outbuf, size_t *outlen,
424 size_t *consumed_total, struct got_inflate_checksum *csum, uint8_t *map,
425 size_t offset, size_t len)
427 const struct got_error *err;
428 size_t avail, consumed;
429 struct got_inflate_buf zb;
434 *outbuf = malloc(GOT_INFLATE_BUFSIZE);
436 return got_error_from_errno("malloc");
437 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
444 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
453 err = got_inflate_read_mmap(&zb, map, offset, len, &avail,
459 *consumed_total += consumed;
464 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
467 newbuf = reallocarray(*outbuf, ++nbuf,
468 GOT_INFLATE_BUFSIZE);
469 if (newbuf == NULL) {
470 err = got_error_from_errno("reallocarray");
477 zb.outbuf = newbuf + *outlen;
478 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
480 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
482 got_inflate_end(&zb);
486 const struct got_error *
487 got_inflate_to_fd(size_t *outlen, FILE *infile,
488 struct got_inflate_checksum *csum, int outfd)
490 const struct got_error *err = NULL;
492 struct got_inflate_buf zb;
494 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
501 err = got_inflate_read(&zb, infile, &avail, NULL);
506 n = write(outfd, zb.outbuf, avail);
508 err = got_error_from_errno("write");
513 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
517 if (lseek(outfd, SEEK_SET, 0) == -1)
518 err = got_error_from_errno("lseek");
520 got_inflate_end(&zb);
524 const struct got_error *
525 got_inflate_to_file(size_t *outlen, FILE *infile,
526 struct got_inflate_checksum *csum, FILE *outfile)
528 const struct got_error *err;
530 struct got_inflate_buf zb;
532 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
539 err = got_inflate_read(&zb, infile, &avail, NULL);
544 n = fwrite(zb.outbuf, avail, 1, outfile);
546 err = got_ferror(outfile, GOT_ERR_IO);
551 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
556 got_inflate_end(&zb);
560 const struct got_error *
561 got_inflate_to_file_fd(size_t *outlen, size_t *consumed_total,
562 struct got_inflate_checksum *csum, int infd, FILE *outfile)
564 const struct got_error *err;
565 size_t avail, consumed;
566 struct got_inflate_buf zb;
568 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
576 err = got_inflate_read_fd(&zb, infd, &avail, &consumed);
581 n = fwrite(zb.outbuf, avail, 1, outfile);
583 err = got_ferror(outfile, GOT_ERR_IO);
588 *consumed_total += consumed;
590 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
595 got_inflate_end(&zb);
599 const struct got_error *
600 got_inflate_to_file_mmap(size_t *outlen, size_t *consumed_total,
601 struct got_inflate_checksum *csum, uint8_t *map, size_t offset,
602 size_t len, FILE *outfile)
604 const struct got_error *err;
605 size_t avail, consumed;
606 struct got_inflate_buf zb;
608 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
616 err = got_inflate_read_mmap(&zb, map, offset, len, &avail,
622 *consumed_total += consumed;
626 n = fwrite(zb.outbuf, avail, 1, outfile);
628 err = got_ferror(outfile, GOT_ERR_IO);
633 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
638 got_inflate_end(&zb);