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