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 <sha2.h>
25 #include <poll.h>
26 #include <unistd.h>
27 #include <zlib.h>
28 #include <time.h>
30 #include "got_error.h"
31 #include "got_object.h"
32 #include "got_path.h"
34 #include "got_lib_inflate.h"
35 #include "got_lib_poll.h"
37 #ifndef MIN
38 #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
39 #endif
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)
44 {
45 const struct got_error *err = NULL;
46 int zerr;
48 memset(zb, 0, sizeof(*zb));
50 zb->z.zalloc = Z_NULL;
51 zb->z.zfree = Z_NULL;
52 zerr = inflateInit(&zb->z);
53 if (zerr != Z_OK) {
54 if (zerr == Z_ERRNO)
55 return got_error_from_errno("inflateInit");
56 if (zerr == Z_MEM_ERROR) {
57 errno = ENOMEM;
58 return got_error_from_errno("inflateInit");
59 }
60 return got_error(GOT_ERR_DECOMPRESSION);
61 }
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");
68 goto done;
69 }
71 zb->flags = 0;
72 if (outbuf == NULL) {
73 zb->outbuf = calloc(1, zb->outlen);
74 if (zb->outbuf == NULL) {
75 err = got_error_from_errno("calloc");
76 goto done;
77 }
78 zb->flags |= GOT_INFLATE_F_OWN_OUTBUF;
79 } else
80 zb->outbuf = outbuf;
82 zb->csum = csum;
83 done:
84 if (err)
85 got_inflate_end(zb);
86 return err;
87 }
89 static void
90 csum_input(struct got_inflate_checksum *csum, const uint8_t *buf, size_t len)
91 {
92 if (csum->input_crc)
93 *csum->input_crc = crc32(*csum->input_crc, buf, len);
95 if (csum->input_sha1)
96 SHA1Update(csum->input_sha1, buf, len);
97 }
99 static void
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,
111 size_t *consumed)
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;
116 int ret = Z_ERRNO;
118 z->next_out = zb->outbuf;
119 z->avail_out = zb->outlen;
121 *outlenp = 0;
122 if (consumed)
123 *consumed = 0;
124 do {
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);
130 if (n == 0) {
131 if (ferror(f))
132 return got_ferror(f, GOT_ERR_IO);
133 /* EOF */
134 ret = Z_STREAM_END;
135 break;
137 z->next_in = zb->inbuf;
138 z->avail_in = n;
140 if (zb->csum) {
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);
147 if (zb->csum) {
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;
157 } else {
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;
164 if (consumed)
165 *consumed += z->total_in - last_total_in;
166 return NULL;
169 const struct got_error *
170 got_inflate_read_fd(struct got_inflate_buf *zb, int fd, size_t *outlenp,
171 size_t *consumed)
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;
177 int ret = Z_ERRNO;
179 z->next_out = zb->outbuf;
180 z->avail_out = zb->outlen;
182 *outlenp = 0;
183 if (consumed)
184 *consumed = 0;
185 do {
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) {
190 ssize_t n;
191 err = got_poll_fd(fd, POLLIN, INFTIM);
192 if (err) {
193 if (err->code == GOT_ERR_EOF) {
194 ret = Z_STREAM_END;
195 break;
197 return err;
199 n = read(fd, zb->inbuf, zb->inlen);
200 if (n < 0)
201 return got_error_from_errno("read");
202 else if (n == 0) {
203 /* EOF */
204 ret = Z_STREAM_END;
205 break;
207 z->next_in = zb->inbuf;
208 z->avail_in = n;
210 if (zb->csum) {
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);
217 if (zb->csum) {
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;
227 } else {
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;
234 if (consumed)
235 *consumed += z->total_in - last_total_in;
236 return NULL;
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;
245 int ret = Z_ERRNO;
247 z->next_out = zb->outbuf;
248 z->avail_out = zb->outlen;
250 *outlenp = 0;
251 *consumed = 0;
253 do {
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) {
259 if (len == 0) {
260 /* EOF */
261 ret = Z_STREAM_END;
262 break;
264 z->next_in = map + offset + *consumed;
265 if (len - *consumed > UINT_MAX)
266 z->avail_in = UINT_MAX;
267 else
268 z->avail_in = len - *consumed;
270 if (zb->csum) {
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);
277 if (zb->csum) {
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;
288 } else {
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;
295 return NULL;
298 void
299 got_inflate_end(struct got_inflate_buf *zb)
301 free(zb->inbuf);
302 if (zb->flags & GOT_INFLATE_F_OWN_OUTBUF)
303 free(zb->outbuf);
304 inflateEnd(&zb->z);
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;
314 void *newbuf;
315 int nbuf = 1;
317 if (outbuf) {
318 *outbuf = malloc(GOT_INFLATE_BUFSIZE);
319 if (*outbuf == NULL)
320 return got_error_from_errno("malloc");
321 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
322 } else
323 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
324 if (err)
325 return err;
327 *outlen = 0;
328 if (consumed_total)
329 *consumed_total = 0;
331 do {
332 err = got_inflate_read(&zb, f, &avail, &consumed);
333 if (err)
334 goto done;
335 *outlen += avail;
336 if (consumed_total)
337 *consumed_total += consumed;
338 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
339 if (outbuf == NULL)
340 continue;
341 newbuf = reallocarray(*outbuf, ++nbuf,
342 GOT_INFLATE_BUFSIZE);
343 if (newbuf == NULL) {
344 err = got_error_from_errno("reallocarray");
345 free(*outbuf);
346 *outbuf = NULL;
347 *outlen = 0;
348 goto done;
350 *outbuf = newbuf;
351 zb.outbuf = newbuf + *outlen;
352 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
354 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
356 done:
357 got_inflate_end(&zb);
358 return err;
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;
369 void *newbuf;
370 int nbuf = 1;
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;
377 if (outbuf) {
378 *outbuf = malloc(bufsize);
379 if (*outbuf == NULL)
380 return got_error_from_errno("malloc");
381 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
382 } else
383 err = got_inflate_init(&zb, NULL, bufsize, csum);
384 if (err)
385 goto done;
387 *outlen = 0;
388 if (consumed_total)
389 *consumed_total = 0;
391 do {
392 err = got_inflate_read_fd(&zb, infd, &avail, &consumed);
393 if (err)
394 goto done;
395 *outlen += avail;
396 if (consumed_total)
397 *consumed_total += consumed;
398 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
399 if (outbuf == NULL)
400 continue;
401 newbuf = reallocarray(*outbuf, ++nbuf,
402 GOT_INFLATE_BUFSIZE);
403 if (newbuf == NULL) {
404 err = got_error_from_errno("reallocarray");
405 free(*outbuf);
406 *outbuf = NULL;
407 *outlen = 0;
408 goto done;
410 *outbuf = newbuf;
411 zb.outbuf = newbuf + *outlen;
412 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
414 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
416 done:
417 got_inflate_end(&zb);
418 return err;
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;
429 void *newbuf;
430 int nbuf = 1;
432 if (outbuf) {
433 *outbuf = malloc(GOT_INFLATE_BUFSIZE);
434 if (*outbuf == NULL)
435 return got_error_from_errno("malloc");
436 err = got_inflate_init(&zb, *outbuf, GOT_INFLATE_BUFSIZE, csum);
437 if (err) {
438 free(*outbuf);
439 *outbuf = NULL;
440 return err;
442 } else {
443 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
444 if (err)
445 return err;
448 *outlen = 0;
449 if (consumed_total)
450 *consumed_total = 0;
451 do {
452 err = got_inflate_read_mmap(&zb, map, offset, len, &avail,
453 &consumed);
454 if (err)
455 goto done;
456 offset += consumed;
457 if (consumed_total)
458 *consumed_total += consumed;
459 len -= consumed;
460 *outlen += avail;
461 if (len == 0)
462 break;
463 if (zb.flags & GOT_INFLATE_F_HAVE_MORE) {
464 if (outbuf == NULL)
465 continue;
466 newbuf = reallocarray(*outbuf, ++nbuf,
467 GOT_INFLATE_BUFSIZE);
468 if (newbuf == NULL) {
469 err = got_error_from_errno("reallocarray");
470 free(*outbuf);
471 *outbuf = NULL;
472 *outlen = 0;
473 goto done;
475 *outbuf = newbuf;
476 zb.outbuf = newbuf + *outlen;
477 zb.outlen = (nbuf * GOT_INFLATE_BUFSIZE) - *outlen;
479 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
480 done:
481 got_inflate_end(&zb);
482 return err;
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;
490 size_t avail;
491 struct got_inflate_buf zb;
493 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
494 if (err)
495 goto done;
497 *outlen = 0;
499 do {
500 err = got_inflate_read(&zb, infile, &avail, NULL);
501 if (err)
502 goto done;
503 if (avail > 0) {
504 ssize_t n;
505 n = write(outfd, zb.outbuf, avail);
506 if (n != avail) {
507 err = got_error_from_errno("write");
508 goto done;
510 *outlen += avail;
512 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
514 done:
515 if (err == NULL) {
516 if (lseek(outfd, SEEK_SET, 0) == -1)
517 err = got_error_from_errno("lseek");
519 got_inflate_end(&zb);
520 return err;
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;
528 size_t avail;
529 struct got_inflate_buf zb;
531 err = got_inflate_init(&zb, NULL, GOT_INFLATE_BUFSIZE, csum);
532 if (err)
533 goto done;
535 *outlen = 0;
537 do {
538 err = got_inflate_read(&zb, infile, &avail, NULL);
539 if (err)
540 goto done;
541 if (avail > 0) {
542 size_t n;
543 n = fwrite(zb.outbuf, avail, 1, outfile);
544 if (n != 1) {
545 err = got_ferror(outfile, GOT_ERR_IO);
546 goto done;
548 *outlen += avail;
550 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
552 done:
553 if (err == NULL)
554 rewind(outfile);
555 got_inflate_end(&zb);
556 return err;
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);
568 if (err)
569 goto done;
571 *outlen = 0;
572 if (consumed_total)
573 *consumed_total = 0;
574 do {
575 err = got_inflate_read_fd(&zb, infd, &avail, &consumed);
576 if (err)
577 goto done;
578 if (avail > 0) {
579 size_t n;
580 n = fwrite(zb.outbuf, avail, 1, outfile);
581 if (n != 1) {
582 err = got_ferror(outfile, GOT_ERR_IO);
583 goto done;
585 *outlen += avail;
586 if (consumed_total)
587 *consumed_total += consumed;
589 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
591 done:
592 if (err == NULL)
593 rewind(outfile);
594 got_inflate_end(&zb);
595 return err;
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);
608 if (err)
609 goto done;
611 *outlen = 0;
612 if (consumed_total)
613 *consumed_total = 0;
614 do {
615 err = got_inflate_read_mmap(&zb, map, offset, len, &avail,
616 &consumed);
617 if (err)
618 goto done;
619 offset += consumed;
620 if (consumed_total)
621 *consumed_total += consumed;
622 len -= consumed;
623 if (avail > 0) {
624 size_t n;
625 n = fwrite(zb.outbuf, avail, 1, outfile);
626 if (n != 1) {
627 err = got_ferror(outfile, GOT_ERR_IO);
628 goto done;
630 *outlen += avail;
632 } while (zb.flags & GOT_INFLATE_F_HAVE_MORE);
634 done:
635 if (err == NULL)
636 rewind(outfile);
637 got_inflate_end(&zb);
638 return err;