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.
26 #include "got_error.h"
27 #include "got_object.h"
30 #include "got_lib_inflate.h"
33 #define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b))
36 const struct got_error
*
37 got_inflate_init(struct got_inflate_buf
*zb
, uint8_t *outbuf
, size_t bufsize
,
38 struct got_inflate_checksum
*csum
)
40 const struct got_error
*err
= NULL
;
43 memset(&zb
->z
, 0, sizeof(zb
->z
));
45 zb
->z
.zalloc
= Z_NULL
;
47 zerr
= inflateInit(&zb
->z
);
50 return got_error_from_errno("inflateInit");
51 if (zerr
== Z_MEM_ERROR
) {
53 return got_error_from_errno("inflateInit");
55 return got_error(GOT_ERR_DECOMPRESSION
);
58 zb
->inlen
= zb
->outlen
= bufsize
;
60 zb
->inbuf
= calloc(1, zb
->inlen
);
61 if (zb
->inbuf
== NULL
) {
62 err
= got_error_from_errno("calloc");
68 zb
->outbuf
= calloc(1, zb
->outlen
);
69 if (zb
->outbuf
== NULL
) {
70 err
= got_error_from_errno("calloc");
73 zb
->flags
|= GOT_INFLATE_F_OWN_OUTBUF
;
85 csum_input(struct got_inflate_checksum
*csum
, const uint8_t *buf
, size_t len
)
88 *csum
->input_crc
= crc32(*csum
->input_crc
, buf
, len
);
91 SHA1Update(csum
->input_sha1
, buf
, len
);
95 csum_output(struct got_inflate_checksum
*csum
, const uint8_t *buf
, size_t len
)
98 *csum
->output_crc
= crc32(*csum
->output_crc
, buf
, len
);
100 if (csum
->output_sha1
)
101 SHA1Update(csum
->output_sha1
, buf
, len
);
104 const struct got_error
*
105 got_inflate_read(struct got_inflate_buf
*zb
, FILE *f
, size_t *outlenp
,
108 size_t last_total_out
= zb
->z
.total_out
;
109 size_t last_total_in
= zb
->z
.total_in
;
110 z_stream
*z
= &zb
->z
;
113 z
->next_out
= zb
->outbuf
;
114 z
->avail_out
= zb
->outlen
;
120 uint8_t *csum_in
= NULL
, *csum_out
= NULL
;
121 size_t csum_avail_in
= 0, csum_avail_out
= 0;
123 if (z
->avail_in
== 0) {
124 size_t n
= fread(zb
->inbuf
, 1, zb
->inlen
, f
);
127 return got_ferror(f
, GOT_ERR_IO
);
132 z
->next_in
= zb
->inbuf
;
136 csum_in
= z
->next_in
;
137 csum_avail_in
= z
->avail_in
;
138 csum_out
= z
->next_out
;
139 csum_avail_out
= z
->avail_out
;
141 ret
= inflate(z
, Z_SYNC_FLUSH
);
143 csum_input(zb
->csum
, csum_in
,
144 csum_avail_in
- z
->avail_in
);
145 csum_output(zb
->csum
, csum_out
,
146 csum_avail_out
- z
->avail_out
);
148 } while (ret
== Z_OK
&& z
->avail_out
> 0);
150 if (ret
== Z_OK
|| ret
== Z_BUF_ERROR
) {
151 zb
->flags
|= GOT_INFLATE_F_HAVE_MORE
;
153 if (ret
!= Z_STREAM_END
)
154 return got_error(GOT_ERR_DECOMPRESSION
);
155 zb
->flags
&= ~GOT_INFLATE_F_HAVE_MORE
;
158 *outlenp
= z
->total_out
- last_total_out
;
160 *consumed
+= z
->total_in
- last_total_in
;
164 const struct got_error
*
165 got_inflate_read_fd(struct got_inflate_buf
*zb
, int fd
, size_t *outlenp
,
168 size_t last_total_out
= zb
->z
.total_out
;
169 size_t last_total_in
= zb
->z
.total_in
;
170 z_stream
*z
= &zb
->z
;
173 z
->next_out
= zb
->outbuf
;
174 z
->avail_out
= zb
->outlen
;
180 uint8_t *csum_in
= NULL
, *csum_out
= NULL
;
181 size_t csum_avail_in
= 0, csum_avail_out
= 0;
183 if (z
->avail_in
== 0) {
184 ssize_t n
= read(fd
, zb
->inbuf
, zb
->inlen
);
186 return got_error_from_errno("read");
192 z
->next_in
= zb
->inbuf
;
196 csum_in
= z
->next_in
;
197 csum_avail_in
= z
->avail_in
;
198 csum_out
= z
->next_out
;
199 csum_avail_out
= z
->avail_out
;
201 ret
= inflate(z
, Z_SYNC_FLUSH
);
203 csum_input(zb
->csum
, csum_in
,
204 csum_avail_in
- z
->avail_in
);
205 csum_output(zb
->csum
, csum_out
,
206 csum_avail_out
- z
->avail_out
);
208 } while (ret
== Z_OK
&& z
->avail_out
> 0);
210 if (ret
== Z_OK
|| ret
== Z_BUF_ERROR
) {
211 zb
->flags
|= GOT_INFLATE_F_HAVE_MORE
;
213 if (ret
!= Z_STREAM_END
)
214 return got_error(GOT_ERR_DECOMPRESSION
);
215 zb
->flags
&= ~GOT_INFLATE_F_HAVE_MORE
;
218 *outlenp
= z
->total_out
- last_total_out
;
220 *consumed
+= z
->total_in
- last_total_in
;
224 const struct got_error
*
225 got_inflate_read_mmap(struct got_inflate_buf
*zb
, uint8_t *map
, size_t offset
,
226 size_t len
, size_t *outlenp
, size_t *consumed
)
228 size_t last_total_out
= zb
->z
.total_out
;
229 z_stream
*z
= &zb
->z
;
232 z
->next_out
= zb
->outbuf
;
233 z
->avail_out
= zb
->outlen
;
239 uint8_t *csum_in
= NULL
, *csum_out
= NULL
;
240 size_t csum_avail_in
= 0, csum_avail_out
= 0;
241 size_t last_total_in
= zb
->z
.total_in
;
243 if (z
->avail_in
== 0) {
249 z
->next_in
= map
+ offset
+ *consumed
;
250 z
->avail_in
= len
- *consumed
;
253 csum_in
= z
->next_in
;
254 csum_avail_in
= z
->avail_in
;
255 csum_out
= z
->next_out
;
256 csum_avail_out
= z
->avail_out
;
258 ret
= inflate(z
, Z_SYNC_FLUSH
);
260 csum_input(zb
->csum
, csum_in
,
261 csum_avail_in
- z
->avail_in
);
262 csum_output(zb
->csum
, csum_out
,
263 csum_avail_out
- z
->avail_out
);
265 *consumed
+= z
->total_in
- last_total_in
;
266 } while (ret
== Z_OK
&& z
->avail_out
> 0);
268 if (ret
== Z_OK
|| ret
== Z_BUF_ERROR
) {
269 zb
->flags
|= GOT_INFLATE_F_HAVE_MORE
;
271 if (ret
!= Z_STREAM_END
)
272 return got_error(GOT_ERR_DECOMPRESSION
);
273 zb
->flags
&= ~GOT_INFLATE_F_HAVE_MORE
;
276 *outlenp
= z
->total_out
- last_total_out
;
281 got_inflate_end(struct got_inflate_buf
*zb
)
284 if (zb
->flags
& GOT_INFLATE_F_OWN_OUTBUF
)
289 const struct got_error
*
290 got_inflate_to_mem(uint8_t **outbuf
, size_t *outlen
,
291 size_t *consumed_total
, struct got_inflate_checksum
*csum
, FILE *f
)
293 const struct got_error
*err
;
294 size_t avail
, consumed
;
295 struct got_inflate_buf zb
;
300 *outbuf
= malloc(GOT_INFLATE_BUFSIZE
);
302 return got_error_from_errno("malloc");
303 err
= got_inflate_init(&zb
, *outbuf
, GOT_INFLATE_BUFSIZE
, csum
);
305 err
= got_inflate_init(&zb
, NULL
, GOT_INFLATE_BUFSIZE
, csum
);
314 err
= got_inflate_read(&zb
, f
, &avail
, &consumed
);
319 *consumed_total
+= consumed
;
320 if (zb
.flags
& GOT_INFLATE_F_HAVE_MORE
) {
323 zb
.outlen
= (nbuf
* GOT_INFLATE_BUFSIZE
) - *outlen
;
324 newbuf
= reallocarray(*outbuf
, ++nbuf
,
325 GOT_INFLATE_BUFSIZE
);
326 if (newbuf
== NULL
) {
327 err
= got_error_from_errno("reallocarray");
334 zb
.outbuf
= newbuf
+ *outlen
;
336 } while (zb
.flags
& GOT_INFLATE_F_HAVE_MORE
);
339 got_inflate_end(&zb
);
343 const struct got_error
*
344 got_inflate_to_mem_fd(uint8_t **outbuf
, size_t *outlen
,
345 size_t *consumed_total
, struct got_inflate_checksum
*csum
,
346 size_t expected_size
, int infd
)
348 const struct got_error
*err
;
349 size_t avail
, consumed
;
350 struct got_inflate_buf zb
;
353 size_t bufsize
= GOT_INFLATE_BUFSIZE
;
355 /* Optimize buffer size in case short reads should suffice. */
356 if (expected_size
> 0 && expected_size
< bufsize
)
357 bufsize
= expected_size
;
360 *outbuf
= malloc(bufsize
);
362 return got_error_from_errno("malloc");
363 err
= got_inflate_init(&zb
, *outbuf
, GOT_INFLATE_BUFSIZE
, csum
);
365 err
= got_inflate_init(&zb
, NULL
, bufsize
, csum
);
374 err
= got_inflate_read_fd(&zb
, infd
, &avail
, &consumed
);
379 *consumed_total
+= consumed
;
380 if (zb
.flags
& GOT_INFLATE_F_HAVE_MORE
) {
383 zb
.outlen
= (nbuf
* GOT_INFLATE_BUFSIZE
) - *outlen
;
384 newbuf
= reallocarray(*outbuf
, ++nbuf
,
385 GOT_INFLATE_BUFSIZE
);
386 if (newbuf
== NULL
) {
387 err
= got_error_from_errno("reallocarray");
394 zb
.outbuf
= newbuf
+ *outlen
;
396 } while (zb
.flags
& GOT_INFLATE_F_HAVE_MORE
);
399 got_inflate_end(&zb
);
403 const struct got_error
*
404 got_inflate_to_mem_mmap(uint8_t **outbuf
, size_t *outlen
,
405 size_t *consumed_total
, struct got_inflate_checksum
*csum
, uint8_t *map
,
406 size_t offset
, size_t len
)
408 const struct got_error
*err
;
409 size_t avail
, consumed
;
410 struct got_inflate_buf zb
;
415 *outbuf
= malloc(GOT_INFLATE_BUFSIZE
);
417 return got_error_from_errno("malloc");
418 err
= got_inflate_init(&zb
, *outbuf
, GOT_INFLATE_BUFSIZE
, csum
);
425 err
= got_inflate_init(&zb
, NULL
, GOT_INFLATE_BUFSIZE
, csum
);
432 err
= got_inflate_read_mmap(&zb
, map
, offset
, len
, &avail
,
438 *consumed_total
+= consumed
;
443 if (zb
.flags
& GOT_INFLATE_F_HAVE_MORE
) {
446 newbuf
= reallocarray(*outbuf
, ++nbuf
,
447 GOT_INFLATE_BUFSIZE
);
448 if (newbuf
== NULL
) {
449 err
= got_error_from_errno("reallocarray");
456 zb
.outbuf
= newbuf
+ *outlen
;
457 zb
.outlen
= (nbuf
* GOT_INFLATE_BUFSIZE
) - *outlen
;
459 } while (zb
.flags
& GOT_INFLATE_F_HAVE_MORE
);
461 got_inflate_end(&zb
);
465 const struct got_error
*
466 got_inflate_to_fd(size_t *outlen
, FILE *infile
,
467 struct got_inflate_checksum
*csum
, int outfd
)
469 const struct got_error
*err
= NULL
;
471 struct got_inflate_buf zb
;
473 err
= got_inflate_init(&zb
, NULL
, GOT_INFLATE_BUFSIZE
, csum
);
480 err
= got_inflate_read(&zb
, infile
, &avail
, NULL
);
485 n
= write(outfd
, zb
.outbuf
, avail
);
487 err
= got_error_from_errno("write");
492 } while (zb
.flags
& GOT_INFLATE_F_HAVE_MORE
);
496 if (lseek(outfd
, SEEK_SET
, 0) == -1)
497 err
= got_error_from_errno("lseek");
499 got_inflate_end(&zb
);
503 const struct got_error
*
504 got_inflate_to_file(size_t *outlen
, FILE *infile
,
505 struct got_inflate_checksum
*csum
, FILE *outfile
)
507 const struct got_error
*err
;
509 struct got_inflate_buf zb
;
511 err
= got_inflate_init(&zb
, NULL
, GOT_INFLATE_BUFSIZE
, csum
);
518 err
= got_inflate_read(&zb
, infile
, &avail
, NULL
);
523 n
= fwrite(zb
.outbuf
, avail
, 1, outfile
);
525 err
= got_ferror(outfile
, GOT_ERR_IO
);
530 } while (zb
.flags
& GOT_INFLATE_F_HAVE_MORE
);
535 got_inflate_end(&zb
);
539 const struct got_error
*
540 got_inflate_to_file_fd(size_t *outlen
, size_t *consumed_total
,
541 struct got_inflate_checksum
*csum
, int infd
, FILE *outfile
)
543 const struct got_error
*err
;
544 size_t avail
, consumed
;
545 struct got_inflate_buf zb
;
547 err
= got_inflate_init(&zb
, NULL
, GOT_INFLATE_BUFSIZE
, csum
);
555 err
= got_inflate_read_fd(&zb
, infd
, &avail
, &consumed
);
560 n
= fwrite(zb
.outbuf
, avail
, 1, outfile
);
562 err
= got_ferror(outfile
, GOT_ERR_IO
);
567 *consumed_total
+= consumed
;
569 } while (zb
.flags
& GOT_INFLATE_F_HAVE_MORE
);
574 got_inflate_end(&zb
);
578 const struct got_error
*
579 got_inflate_to_file_mmap(size_t *outlen
, size_t *consumed_total
,
580 struct got_inflate_checksum
*csum
, uint8_t *map
, size_t offset
,
581 size_t len
, FILE *outfile
)
583 const struct got_error
*err
;
584 size_t avail
, consumed
;
585 struct got_inflate_buf zb
;
587 err
= got_inflate_init(&zb
, NULL
, GOT_INFLATE_BUFSIZE
, csum
);
595 err
= got_inflate_read_mmap(&zb
, map
, offset
, len
, &avail
,
601 *consumed_total
+= consumed
;
605 n
= fwrite(zb
.outbuf
, avail
, 1, outfile
);
607 err
= got_ferror(outfile
, GOT_ERR_IO
);
612 } while (zb
.flags
& GOT_INFLATE_F_HAVE_MORE
);
617 got_inflate_end(&zb
);