diff options
Diffstat (limited to 'libarchive/libarchive-2.5.5/libarchive/archive_read_support_compression_bzip2.c')
| -rw-r--r-- | libarchive/libarchive-2.5.5/libarchive/archive_read_support_compression_bzip2.c | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/libarchive/libarchive-2.5.5/libarchive/archive_read_support_compression_bzip2.c b/libarchive/libarchive-2.5.5/libarchive/archive_read_support_compression_bzip2.c new file mode 100644 index 0000000..824b0cd --- /dev/null +++ b/libarchive/libarchive-2.5.5/libarchive/archive_read_support_compression_bzip2.c @@ -0,0 +1,426 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "archive_platform.h" + +__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.18 2008/05/26 17:00:22 kientzle Exp $"); + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_BZLIB_H +#include <bzlib.h> +#endif + +#include "archive.h" +#include "archive_private.h" +#include "archive_read_private.h" + +#if HAVE_BZLIB_H +struct private_data { + bz_stream stream; + char *uncompressed_buffer; + size_t uncompressed_buffer_size; + char *read_next; + int64_t total_out; + char eof; /* True = found end of compressed data. */ +}; + +static int finish(struct archive_read *); +static ssize_t read_ahead(struct archive_read *, const void **, size_t); +static ssize_t read_consume(struct archive_read *, size_t); +static int drive_decompressor(struct archive_read *a, struct private_data *); +#endif + +/* These two functions are defined even if we lack the library. See below. */ +static int bid(const void *, size_t); +static int init(struct archive_read *, const void *, size_t); + +int +archive_read_support_compression_bzip2(struct archive *_a) +{ + struct archive_read *a = (struct archive_read *)_a; + if (__archive_read_register_compression(a, bid, init) != NULL) + return (ARCHIVE_OK); + return (ARCHIVE_FATAL); +} + +/* + * Test whether we can handle this data. + * + * This logic returns zero if any part of the signature fails. It + * also tries to Do The Right Thing if a very short buffer prevents us + * from verifying as much as we would like. + */ +static int +bid(const void *buff, size_t len) +{ + const unsigned char *buffer; + int bits_checked; + + if (len < 1) + return (0); + + buffer = (const unsigned char *)buff; + bits_checked = 0; + if (buffer[0] != 'B') /* Verify first ID byte. */ + return (0); + bits_checked += 8; + if (len < 2) + return (bits_checked); + + if (buffer[1] != 'Z') /* Verify second ID byte. */ + return (0); + bits_checked += 8; + if (len < 3) + return (bits_checked); + + if (buffer[2] != 'h') /* Verify third ID byte. */ + return (0); + bits_checked += 8; + if (len < 4) + return (bits_checked); + + if (buffer[3] < '1' || buffer[3] > '9') + return (0); + bits_checked += 5; + if (len < 5) + return (bits_checked); + + /* After BZh[1-9], there must be either a data block + * which begins with 0x314159265359 or an end-of-data + * marker of 0x177245385090. */ + + if (buffer[4] == 0x31) { + /* Verify the data block signature. */ + size_t s = len; + if (s > 10) s = 10; + if (memcmp(buffer + 4, "\x31\x41\x59\x26\x53\x59", s - 4) != 0) + return (0); + bits_checked += 8 * (s - 4); + } else if (buffer[4] == 0x17) { + /* Verify the end-of-data marker. */ + size_t s = len; + if (s > 10) s = 10; + if (memcmp(buffer + 4, "\x17\x72\x45\x38\x50\x90", s - 4) != 0) + return (0); + bits_checked += 8 * (s - 4); + } else + return (0); + + return (bits_checked); +} + +#ifndef HAVE_BZLIB_H + +/* + * If we don't have the library on this system, we can't actually do the + * decompression. We can, however, still detect compressed archives + * and emit a useful message. + */ +static int +init(struct archive_read *a, const void *buff, size_t n) +{ + (void)a; /* UNUSED */ + (void)buff; /* UNUSED */ + (void)n; /* UNUSED */ + + archive_set_error(&a->archive, -1, + "This version of libarchive was compiled without bzip2 support"); + return (ARCHIVE_FATAL); +} + + +#else + +/* + * Setup the callbacks. + */ +static int +init(struct archive_read *a, const void *buff, size_t n) +{ + struct private_data *state; + int ret; + + a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2; + a->archive.compression_name = "bzip2"; + + state = (struct private_data *)malloc(sizeof(*state)); + if (state == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate data for %s decompression", + a->archive.compression_name); + return (ARCHIVE_FATAL); + } + memset(state, 0, sizeof(*state)); + + state->uncompressed_buffer_size = 64 * 1024; + state->uncompressed_buffer = (char *)malloc(state->uncompressed_buffer_size); + state->stream.next_out = state->uncompressed_buffer; + state->read_next = state->uncompressed_buffer; + state->stream.avail_out = state->uncompressed_buffer_size; + + if (state->uncompressed_buffer == NULL) { + archive_set_error(&a->archive, ENOMEM, + "Can't allocate %s decompression buffers", + a->archive.compression_name); + free(state); + return (ARCHIVE_FATAL); + } + + /* + * A bug in bzlib.h: stream.next_in should be marked 'const' + * but isn't (the library never alters data through the + * next_in pointer, only reads it). The result: this ugly + * cast to remove 'const'. + */ + state->stream.next_in = (char *)(uintptr_t)(const void *)buff; + state->stream.avail_in = n; + + a->decompressor->read_ahead = read_ahead; + a->decompressor->consume = read_consume; + a->decompressor->skip = NULL; /* not supported */ + a->decompressor->finish = finish; + + /* Initialize compression library. */ + ret = BZ2_bzDecompressInit(&(state->stream), + 0 /* library verbosity */, + 0 /* don't use slow low-mem algorithm */); + + /* If init fails, try using low-memory algorithm instead. */ + if (ret == BZ_MEM_ERROR) { + ret = BZ2_bzDecompressInit(&(state->stream), + 0 /* library verbosity */, + 1 /* do use slow low-mem algorithm */); + } + + if (ret == BZ_OK) { + a->decompressor->data = state; + return (ARCHIVE_OK); + } + + /* Library setup failed: Clean up. */ + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing %s library", + a->archive.compression_name); + free(state->uncompressed_buffer); + free(state); + + /* Override the error message if we know what really went wrong. */ + switch (ret) { + case BZ_PARAM_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "invalid setup parameter"); + break; + case BZ_MEM_ERROR: + archive_set_error(&a->archive, ENOMEM, + "Internal error initializing compression library: " + "out of memory"); + break; + case BZ_CONFIG_ERROR: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Internal error initializing compression library: " + "mis-compiled library"); + break; + } + + return (ARCHIVE_FATAL); +} + +/* + * Return a block of data from the decompression buffer. Decompress more + * as necessary. + */ +static ssize_t +read_ahead(struct archive_read *a, const void **p, size_t min) +{ + struct private_data *state; + size_t read_avail, was_avail; + int ret; + + state = (struct private_data *)a->decompressor->data; + if (!a->client_reader) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER, + "No read callback is registered? " + "This is probably an internal programming error."); + return (ARCHIVE_FATAL); + } + + read_avail = state->stream.next_out - state->read_next; + + if (read_avail + state->stream.avail_out < min) { + memmove(state->uncompressed_buffer, state->read_next, + read_avail); + state->read_next = state->uncompressed_buffer; + state->stream.next_out = state->read_next + read_avail; + state->stream.avail_out + = state->uncompressed_buffer_size - read_avail; + } + + while (read_avail < min && /* Haven't satisfied min. */ + read_avail < state->uncompressed_buffer_size) { /* !full */ + was_avail = read_avail; + if ((ret = drive_decompressor(a, state)) < ARCHIVE_OK) + return (ret); + if (ret == ARCHIVE_EOF) + break; /* Break on EOF even if we haven't met min. */ + read_avail = state->stream.next_out - state->read_next; + if (was_avail == read_avail) /* No progress? */ + break; + } + + *p = state->read_next; + return (read_avail); +} + +/* + * Mark a previously-returned block of data as read. + */ +static ssize_t +read_consume(struct archive_read *a, size_t n) +{ + struct private_data *state; + + state = (struct private_data *)a->decompressor->data; + a->archive.file_position += n; + state->read_next += n; + if (state->read_next > state->stream.next_out) + __archive_errx(1, "Request to consume too many " + "bytes from bzip2 decompressor"); + return (n); +} + +/* + * Clean up the decompressor. + */ +static int +finish(struct archive_read *a) +{ + struct private_data *state; + int ret; + + state = (struct private_data *)a->decompressor->data; + ret = ARCHIVE_OK; + switch (BZ2_bzDecompressEnd(&(state->stream))) { + case BZ_OK: + break; + default: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "Failed to clean up %s compressor", + a->archive.compression_name); + ret = ARCHIVE_FATAL; + } + + free(state->uncompressed_buffer); + free(state); + + a->decompressor->data = NULL; + return (ret); +} + +/* + * Utility function to pull data through decompressor, reading input + * blocks as necessary. + */ +static int +drive_decompressor(struct archive_read *a, struct private_data *state) +{ + ssize_t ret; + int decompressed, total_decompressed; + char *output; + const void *read_buf; + + if (state->eof) + return (ARCHIVE_EOF); + total_decompressed = 0; + for (;;) { + if (state->stream.avail_in == 0) { + read_buf = state->stream.next_in; + ret = (a->client_reader)(&a->archive, a->client_data, + &read_buf); + state->stream.next_in = (void *)(uintptr_t)read_buf; + if (ret < 0) { + /* + * TODO: Find a better way to handle + * this read failure. + */ + goto fatal; + } + if (ret == 0 && total_decompressed == 0) { + archive_set_error(&a->archive, EIO, + "Premature end of %s compressed data", + a->archive.compression_name); + return (ARCHIVE_FATAL); + } + a->archive.raw_position += ret; + state->stream.avail_in = ret; + } + + { + output = state->stream.next_out; + + /* Decompress some data. */ + ret = BZ2_bzDecompress(&(state->stream)); + decompressed = state->stream.next_out - output; + + /* Accumulate the total bytes of output. */ + state->total_out += decompressed; + total_decompressed += decompressed; + + switch (ret) { + case BZ_OK: /* Decompressor made some progress. */ + if (decompressed > 0) + return (ARCHIVE_OK); + break; + case BZ_STREAM_END: /* Found end of stream. */ + state->eof = 1; + return (ARCHIVE_OK); + default: + /* Any other return value is an error. */ + goto fatal; + } + } + } + return (ARCHIVE_OK); + + /* Return a fatal error. */ +fatal: + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "%s decompression failed", a->archive.compression_name); + return (ARCHIVE_FATAL); +} + +#endif /* HAVE_BZLIB_H */ |
