2 * Copyright (c) 2003-2007 Tim Kientzle
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "archive_platform.h"
28 __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.17 2008/02/19 05:44:59 kientzle Exp $");
48 #include "archive_private.h"
49 #include "archive_read_private.h"
54 char *uncompressed_buffer
;
55 size_t uncompressed_buffer_size
;
58 char eof
; /* True = found end of compressed data. */
61 static int finish(struct archive_read
*);
62 static ssize_t
read_ahead(struct archive_read
*, const void **, size_t);
63 static ssize_t
read_consume(struct archive_read
*, size_t);
64 static int drive_decompressor(struct archive_read
*a
, struct private_data
*);
67 /* These two functions are defined even if we lack the library. See below. */
68 static int bid(const void *, size_t);
69 static int init(struct archive_read
*, const void *, size_t);
72 archive_read_support_compression_bzip2(struct archive
*_a
)
74 struct archive_read
*a
= (struct archive_read
*)_a
;
75 if (__archive_read_register_compression(a
, bid
, init
) != NULL
)
77 return (ARCHIVE_FATAL
);
81 * Test whether we can handle this data.
83 * This logic returns zero if any part of the signature fails. It
84 * also tries to Do The Right Thing if a very short buffer prevents us
85 * from verifying as much as we would like.
88 bid(const void *buff
, size_t len
)
90 const unsigned char *buffer
;
96 buffer
= (const unsigned char *)buff
;
98 if (buffer
[0] != 'B') /* Verify first ID byte. */
102 return (bits_checked
);
104 if (buffer
[1] != 'Z') /* Verify second ID byte. */
108 return (bits_checked
);
110 if (buffer
[2] != 'h') /* Verify third ID byte. */
114 return (bits_checked
);
116 if (buffer
[3] < '1' || buffer
[3] > '9')
120 return (bits_checked
);
122 /* After BZh[1-9], there must be either a data block
123 * which begins with 0x314159265359 or an end-of-data
124 * marker of 0x177245385090. */
126 if (buffer
[4] == 0x31) {
127 /* Verify the data block signature. */
130 if (memcmp(buffer
+ 4, "\x31\x41\x59\x26\x53\x59", s
- 4) != 0)
132 bits_checked
+= 8 * (s
- 4);
133 } else if (buffer
[4] == 0x17) {
134 /* Verify the end-of-data marker. */
137 if (memcmp(buffer
+ 4, "\x17\x72\x45\x38\x50\x90", s
- 4) != 0)
139 bits_checked
+= 8 * (s
- 4);
143 return (bits_checked
);
149 * If we don't have the library on this system, we can't actually do the
150 * decompression. We can, however, still detect compressed archives
151 * and emit a useful message.
154 init(struct archive_read
*a
, const void *buff
, size_t n
)
156 (void)a
; /* UNUSED */
157 (void)buff
; /* UNUSED */
158 (void)n
; /* UNUSED */
160 archive_set_error(&a
->archive
, -1,
161 "This version of libarchive was compiled without bzip2 support");
162 return (ARCHIVE_FATAL
);
169 * Setup the callbacks.
172 init(struct archive_read
*a
, const void *buff
, size_t n
)
174 struct private_data
*state
;
177 a
->archive
.compression_code
= ARCHIVE_COMPRESSION_BZIP2
;
178 a
->archive
.compression_name
= "bzip2";
180 state
= (struct private_data
*)malloc(sizeof(*state
));
182 archive_set_error(&a
->archive
, ENOMEM
,
183 "Can't allocate data for %s decompression",
184 a
->archive
.compression_name
);
185 return (ARCHIVE_FATAL
);
187 memset(state
, 0, sizeof(*state
));
189 state
->uncompressed_buffer_size
= 64 * 1024;
190 state
->uncompressed_buffer
= (char *)malloc(state
->uncompressed_buffer_size
);
191 state
->stream
.next_out
= state
->uncompressed_buffer
;
192 state
->read_next
= state
->uncompressed_buffer
;
193 state
->stream
.avail_out
= state
->uncompressed_buffer_size
;
195 if (state
->uncompressed_buffer
== NULL
) {
196 archive_set_error(&a
->archive
, ENOMEM
,
197 "Can't allocate %s decompression buffers",
198 a
->archive
.compression_name
);
200 return (ARCHIVE_FATAL
);
204 * A bug in bzlib.h: stream.next_in should be marked 'const'
205 * but isn't (the library never alters data through the
206 * next_in pointer, only reads it). The result: this ugly
207 * cast to remove 'const'.
209 state
->stream
.next_in
= (char *)(uintptr_t)(const void *)buff
;
210 state
->stream
.avail_in
= n
;
212 a
->decompressor
->read_ahead
= read_ahead
;
213 a
->decompressor
->consume
= read_consume
;
214 a
->decompressor
->skip
= NULL
; /* not supported */
215 a
->decompressor
->finish
= finish
;
217 /* Initialize compression library. */
218 ret
= BZ2_bzDecompressInit(&(state
->stream
),
219 0 /* library verbosity */,
220 0 /* don't use slow low-mem algorithm */);
222 /* If init fails, try using low-memory algorithm instead. */
223 if (ret
== BZ_MEM_ERROR
) {
224 ret
= BZ2_bzDecompressInit(&(state
->stream
),
225 0 /* library verbosity */,
226 1 /* do use slow low-mem algorithm */);
230 a
->decompressor
->data
= state
;
234 /* Library setup failed: Clean up. */
235 archive_set_error(&a
->archive
, ARCHIVE_ERRNO_MISC
,
236 "Internal error initializing %s library",
237 a
->archive
.compression_name
);
238 free(state
->uncompressed_buffer
);
241 /* Override the error message if we know what really went wrong. */
244 archive_set_error(&a
->archive
, ARCHIVE_ERRNO_MISC
,
245 "Internal error initializing compression library: "
246 "invalid setup parameter");
249 archive_set_error(&a
->archive
, ENOMEM
,
250 "Internal error initializing compression library: "
253 case BZ_CONFIG_ERROR
:
254 archive_set_error(&a
->archive
, ARCHIVE_ERRNO_MISC
,
255 "Internal error initializing compression library: "
256 "mis-compiled library");
260 return (ARCHIVE_FATAL
);
264 * Return a block of data from the decompression buffer. Decompress more
268 read_ahead(struct archive_read
*a
, const void **p
, size_t min
)
270 struct private_data
*state
;
271 size_t read_avail
, was_avail
;
274 state
= (struct private_data
*)a
->decompressor
->data
;
275 if (!a
->client_reader
) {
276 archive_set_error(&a
->archive
, ARCHIVE_ERRNO_PROGRAMMER
,
277 "No read callback is registered? "
278 "This is probably an internal programming error.");
279 return (ARCHIVE_FATAL
);
282 read_avail
= state
->stream
.next_out
- state
->read_next
;
284 if (read_avail
+ state
->stream
.avail_out
< min
) {
285 memmove(state
->uncompressed_buffer
, state
->read_next
,
287 state
->read_next
= state
->uncompressed_buffer
;
288 state
->stream
.next_out
= state
->read_next
+ read_avail
;
289 state
->stream
.avail_out
290 = state
->uncompressed_buffer_size
- read_avail
;
293 while (read_avail
< min
&& /* Haven't satisfied min. */
294 read_avail
< state
->uncompressed_buffer_size
) { /* !full */
295 was_avail
= read_avail
;
296 if ((ret
= drive_decompressor(a
, state
)) < ARCHIVE_OK
)
298 if (ret
== ARCHIVE_EOF
)
299 break; /* Break on EOF even if we haven't met min. */
300 read_avail
= state
->stream
.next_out
- state
->read_next
;
301 if (was_avail
== read_avail
) /* No progress? */
305 *p
= state
->read_next
;
310 * Mark a previously-returned block of data as read.
313 read_consume(struct archive_read
*a
, size_t n
)
315 struct private_data
*state
;
317 state
= (struct private_data
*)a
->decompressor
->data
;
318 a
->archive
.file_position
+= n
;
319 state
->read_next
+= n
;
320 if (state
->read_next
> state
->stream
.next_out
)
321 __archive_errx(1, "Request to consume too many "
322 "bytes from bzip2 decompressor");
327 * Clean up the decompressor.
330 finish(struct archive_read
*a
)
332 struct private_data
*state
;
335 state
= (struct private_data
*)a
->decompressor
->data
;
337 switch (BZ2_bzDecompressEnd(&(state
->stream
))) {
341 archive_set_error(&a
->archive
, ARCHIVE_ERRNO_MISC
,
342 "Failed to clean up %s compressor",
343 a
->archive
.compression_name
);
347 free(state
->uncompressed_buffer
);
350 a
->decompressor
->data
= NULL
;
355 * Utility function to pull data through decompressor, reading input
356 * blocks as necessary.
359 drive_decompressor(struct archive_read
*a
, struct private_data
*state
)
362 int decompressed
, total_decompressed
;
364 const void *read_buf
;
367 return (ARCHIVE_EOF
);
368 total_decompressed
= 0;
370 if (state
->stream
.avail_in
== 0) {
371 read_buf
= state
->stream
.next_in
;
372 ret
= (a
->client_reader
)(&a
->archive
, a
->client_data
,
374 state
->stream
.next_in
= (void *)(uintptr_t)read_buf
;
377 * TODO: Find a better way to handle
382 if (ret
== 0 && total_decompressed
== 0) {
383 archive_set_error(&a
->archive
, EIO
,
384 "Premature end of %s compressed data",
385 a
->archive
.compression_name
);
386 return (ARCHIVE_FATAL
);
388 a
->archive
.raw_position
+= ret
;
389 state
->stream
.avail_in
= ret
;
393 output
= state
->stream
.next_out
;
395 /* Decompress some data. */
396 ret
= BZ2_bzDecompress(&(state
->stream
));
397 decompressed
= state
->stream
.next_out
- output
;
399 /* Accumulate the total bytes of output. */
400 state
->total_out
+= decompressed
;
401 total_decompressed
+= decompressed
;
404 case BZ_OK
: /* Decompressor made some progress. */
405 if (decompressed
> 0)
408 case BZ_STREAM_END
: /* Found end of stream. */
412 /* Any other return value is an error. */
419 /* Return a fatal error. */
421 archive_set_error(&a
->archive
, ARCHIVE_ERRNO_MISC
,
422 "%s decompression failed", a
->archive
.compression_name
);
423 return (ARCHIVE_FATAL
);
426 #endif /* HAVE_BZLIB_H */