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 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
);