Blob


1 /*
2 * Copyright (c) 2018 Stefan Sperling <stsp@openbsd.org>
3 *
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.
7 *
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.
15 */
17 #include <sys/queue.h>
19 #include <errno.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sha1.h>
24 #include <poll.h>
25 #include <unistd.h>
26 #include <zlib.h>
27 #include <time.h>
29 #include "got_error.h"
30 #include "got_object.h"
31 #include "got_path.h"
33 #include "got_lib_inflate.h"
34 #include "got_lib_poll.h"
36 #ifndef MIN
37 #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
38 #endif
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)
43 {
44 const struct got_error *err = NULL;
45 int zerr;
47 memset(&zb->z, 0, sizeof(zb->z));
49 zb->z.zalloc = Z_NULL;
50 zb->z.zfree = Z_NULL;
51 zerr = inflateInit(&zb->z);
52 if (zerr != Z_OK) {
53 if (zerr == Z_ERRNO)
54 return got_error_from_errno("inflateInit");
55 if (zerr == Z_MEM_ERROR) {
56 errno = ENOMEM;
57 return got_error_from_errno("inflateInit");
58 }
59 return got_error(GOT_ERR_DECOMPRESSION);
60 }
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");
67 goto done;
68 }
70 zb->flags = 0;
71 if (outbuf == NULL) {
72 zb->outbuf = calloc(1, zb->outlen);
73 if (zb->outbuf == NULL) {
74 err = got_error_from_errno("calloc");
75 goto done;
76 }
77 zb->flags |= GOT_INFLATE_F_OWN_OUTBUF;
78 } else
79 zb->outbuf = outbuf;
81 zb->csum = csum;
82 done:
83 if (err)
84 got_inflate_end(zb);
85 return err;
86 }
88 static void
89 csum_input(struct got_inflate_checksum *csum, const uint8_t *buf, size_t len)
90 {
91 if (csum->input_crc)
92 *csum->input_crc = crc32(*csum->input_crc, buf, len);
94 if (csum->input_sha1)
95 SHA1Update(csum->input_sha1, buf, len);
96 }
98 static void
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,
110 size_t *consumed)
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;
115 int ret = Z_ERRNO;
117 z->next_out = zb->outbuf;
118 z->avail_out = zb->outlen;
120 *outlenp = 0;
121 if (consumed)
122 *consumed = 0;
123 do {
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);
129 if (n == 0) {
130 if (ferror(f))
131 return got_ferror(f, GOT_ERR_IO);
132 /* EOF */
133 ret = Z_STREAM_END;
134 break;
136 z->next_in = zb->inbuf;
137 z->avail_in = n;
139 if (zb->csum) {
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);
146 if (zb->csum) {
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;
156 } else {
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;
163 if (consumed)
164 *consumed += z->total_in - last_total_in;
165 return NULL;
168 const struct got_error *
169 got_inflate_read_fd(struct got_inflate_buf *zb, int fd, size_t *outlenp,
170 size_t *consumed)
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;
176 int ret = Z_ERRNO;
178 z->next_out = zb->outbuf;
179 z->avail_out = zb->outlen;
181 *outlenp = 0;
182 if (consumed)
183 *consumed = 0;
184 do {
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) {
189 ssize_t n;
190 err = got_poll_fd(fd, POLLIN, INFTIM);
191 if (err) {
192 if (err->code == GOT_ERR_EOF) {
193 ret = Z_STREAM_END;
194 break;
196 return err;
198 n = read(fd, zb->inbuf, zb->inlen);
199 if (n < 0)
200 return got_error_from_errno("read");
201 else if (n == 0) {
202 /* EOF */
203 ret = Z_STREAM_END;
204 break;
206 z->next_in = zb->inbuf;
207 z->avail_in = n;
209 if (zb->csum) {
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);
216 if (zb->csum) {
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;
226 } else {
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;
233 if (consumed)
234 *consumed += z->total_in - last_total_in;
235 return NULL;
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;
244 int ret = Z_ERRNO;
246 z->next_out = zb->outbuf;
247 z->avail_out = zb->outlen;
249 *outlenp = 0;
250 *consumed = 0;
252 do {
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) {
258 if (len == 0) {
259 /* EOF */
260 ret = Z_STREAM_END;
261 break;
263 z->next_in = map + offset + *consumed;
264 if (len - *consumed > UINT_MAX)
265 z->avail_in = UINT_MAX;
266 else
267 z->avail_in = len - *consumed;
269 if (zb->csum) {
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);
276 if (zb->csum) {
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;
287 } else {
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;
294 return NULL;
297 void
298 got_inflate_end(struct got_inflate_buf *zb)
300 free(zb->inbuf);
301 if (zb->flags & GOT_INFLATE_F_OWN_OUTBUF)
302 free(zb->outbuf);
303 inflateEnd(&zb->z);
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;
313 void *newbuf;
314 int nbuf = 1;
316 if (outbuf) {
317 *outbuf = malloc(GOT_INFLATE_BUFSIZE);
318 if (*outbuf == NULL)
319 return got_error_from_errno("malloc");
320 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
321 } else
322 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
323 if (err)
324 return err;
326 *outlen = 0;
327 if (consumed_total)
328 *consumed_total = 0;
330 do {
331 err = got_inflate_read(&zb, f, &avail, &consumed);
332 if (err)
333 goto done;
334 *outlen += avail;
335 if (consumed_total)
336 *consumed_total += consumed;
337 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
338 if (outbuf == NULL)
339 continue;
340 newbuf = reallocarray(*outbuf, ++nbuf,
341 GOT_INFLATE_BUFSIZE);
342 if (newbuf == NULL) {
343 err = got_error_from_errno("reallocarray");
344 free(*outbuf);
345 *outbuf = NULL;
346 *outlen = 0;
347 goto done;
349 *outbuf = newbuf;
350 zb.outbuf = newbuf + *outlen;
351 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
353 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
355 done:
356 got_inflate_end(&zb);
357 return err;
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;
368 void *newbuf;
369 int nbuf = 1;
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;
376 if (outbuf) {
377 *outbuf = malloc(bufsize);
378 if (*outbuf == NULL)
379 return got_error_from_errno("malloc");
380 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
381 } else
382 err = got_inflate_init(&zb, NULL, bufsize, csum);
383 if (err)
384 goto done;
386 *outlen = 0;
387 if (consumed_total)
388 *consumed_total = 0;
390 do {
391 err = got_inflate_read_fd(&zb, infd, &avail, &consumed);
392 if (err)
393 goto done;
394 *outlen += avail;
395 if (consumed_total)
396 *consumed_total += consumed;
397 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
398 if (outbuf == NULL)
399 continue;
400 newbuf = reallocarray(*outbuf, ++nbuf,
401 GOT_INFLATE_BUFSIZE);
402 if (newbuf == NULL) {
403 err = got_error_from_errno("reallocarray");
404 free(*outbuf);
405 *outbuf = NULL;
406 *outlen = 0;
407 goto done;
409 *outbuf = newbuf;
410 zb.outbuf = newbuf + *outlen;
411 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
413 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
415 done:
416 got_inflate_end(&zb);
417 return err;
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;
428 void *newbuf;
429 int nbuf = 1;
431 if (outbuf) {
432 *outbuf = malloc(GOT_INFLATE_BUFSIZE);
433 if (*outbuf == NULL)
434 return got_error_from_errno("malloc");
435 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
436 if (err) {
437 free(*outbuf);
438 *outbuf = NULL;
439 return err;
441 } else {
442 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
443 if (err)
444 return err;
447 *outlen = 0;
448 if (consumed_total)
449 *consumed_total = 0;
450 do {
451 err = got_inflate_read_mmap(&zb, map, offset, len, &avail,
452 &consumed);
453 if (err)
454 goto done;
455 offset += consumed;
456 if (consumed_total)
457 *consumed_total += consumed;
458 len -= consumed;
459 *outlen += avail;
460 if (len == 0)
461 break;
462 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
463 if (outbuf == NULL)
464 continue;
465 newbuf = reallocarray(*outbuf, ++nbuf,
466 GOT_INFLATE_BUFSIZE);
467 if (newbuf == NULL) {
468 err = got_error_from_errno("reallocarray");
469 free(*outbuf);
470 *outbuf = NULL;
471 *outlen = 0;
472 goto done;
474 *outbuf = newbuf;
475 zb.outbuf = newbuf + *outlen;
476 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
478 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
479 done:
480 got_inflate_end(&zb);
481 return err;
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;
489 size_t avail;
490 struct got_inflate_buf zb;
492 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
493 if (err)
494 goto done;
496 *outlen = 0;
498 do {
499 err = got_inflate_read(&zb, infile, &avail, NULL);
500 if (err)
501 goto done;
502 if (avail > 0) {
503 ssize_t n;
504 n = write(outfd, zb.outbuf, avail);
505 if (n != avail) {
506 err = got_error_from_errno("write");
507 goto done;
509 *outlen += avail;
511 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
513 done:
514 if (err == NULL) {
515 if (lseek(outfd, SEEK_SET, 0) == -1)
516 err = got_error_from_errno("lseek");
518 got_inflate_end(&zb);
519 return err;
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;
527 size_t avail;
528 struct got_inflate_buf zb;
530 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
531 if (err)
532 goto done;
534 *outlen = 0;
536 do {
537 err = got_inflate_read(&zb, infile, &avail, NULL);
538 if (err)
539 goto done;
540 if (avail > 0) {
541 size_t n;
542 n = fwrite(zb.outbuf, avail, 1, outfile);
543 if (n != 1) {
544 err = got_ferror(outfile, GOT_ERR_IO);
545 goto done;
547 *outlen += avail;
549 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
551 done:
552 if (err == NULL)
553 rewind(outfile);
554 got_inflate_end(&zb);
555 return err;
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);
567 if (err)
568 goto done;
570 *outlen = 0;
571 if (consumed_total)
572 *consumed_total = 0;
573 do {
574 err = got_inflate_read_fd(&zb, infd, &avail, &consumed);
575 if (err)
576 goto done;
577 if (avail > 0) {
578 size_t n;
579 n = fwrite(zb.outbuf, avail, 1, outfile);
580 if (n != 1) {
581 err = got_ferror(outfile, GOT_ERR_IO);
582 goto done;
584 *outlen += avail;
585 if (consumed_total)
586 *consumed_total += consumed;
588 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
590 done:
591 if (err == NULL)
592 rewind(outfile);
593 got_inflate_end(&zb);
594 return err;
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);
607 if (err)
608 goto done;
610 *outlen = 0;
611 if (consumed_total)
612 *consumed_total = 0;
613 do {
614 err = got_inflate_read_mmap(&zb, map, offset, len, &avail,
615 &consumed);
616 if (err)
617 goto done;
618 offset += consumed;
619 if (consumed_total)
620 *consumed_total += consumed;
621 len -= consumed;
622 if (avail > 0) {
623 size_t n;
624 n = fwrite(zb.outbuf, avail, 1, outfile);
625 if (n != 1) {
626 err = got_ferror(outfile, GOT_ERR_IO);
627 goto done;
629 *outlen += avail;
631 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
633 done:
634 if (err == NULL)
635 rewind(outfile);
636 got_inflate_end(&zb);
637 return err;