From: Tim Kientzle Date: Sat, 8 Nov 2008 21:44:40 +0000 (-0500) Subject: Hack up the lzma decoder to at least compile; there aren't X-Git-Tag: v2.6.0~54 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a28b87d761fb4fd7e86172c02b7a862882171774;p=thirdparty%2Flibarchive.git Hack up the lzma decoder to at least compile; there aren't any tests for it yet, so I'm not sure if it actually works. SVN-Revision: 239 --- diff --git a/libarchive/archive_read_support_compression_lzma.c b/libarchive/archive_read_support_compression_lzma.c index 85b886840..0f8e4b36f 100644 --- a/libarchive/archive_read_support_compression_lzma.c +++ b/libarchive/archive_read_support_compression_lzma.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2008 Miklos Vajna + * Copyright (c) 2003-2008 Tim Kientzle and Miklos Vajna * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,12 +25,12 @@ #include "archive_platform.h" -__FBSDID("FIXME"); - +__FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include #endif +#include #ifdef HAVE_STDLIB_H #include #endif @@ -48,33 +48,52 @@ __FBSDID("FIXME"); #include "archive_private.h" #include "archive_read_private.h" -#ifdef HAVE_LZMADEC_H +#if HAVE_LZMADEC_H struct private_data { lzmadec_stream stream; - unsigned char *uncompressed_buffer; - size_t uncompressed_buffer_size; - unsigned char *read_next; + unsigned char *out_block; + size_t out_block_size; 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 *); +/* Lzma source */ +static ssize_t lzma_source_read(struct archive_read_source *, const void **); +static int lzma_source_close(struct archive_read_source *); #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); +/* + * Note that we can detect lzma archives even if we can't decompress + * them. (In fact, we like detecting them because we can give better + * error messages.) So the bid framework here gets compiled even + * if lzmadec is unavailable. + */ +static int lzma_reader_bid(struct archive_reader *, const void *, size_t); +static struct archive_read_source *lzma_reader_init(struct archive_read *, + struct archive_reader *, struct archive_read_source *, + const void *, size_t); +static int lzma_reader_free(struct archive_reader *); int archive_read_support_compression_lzma(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); + struct archive_reader *reader = __archive_read_get_reader(a); + + if (reader == NULL) + return (ARCHIVE_FATAL); + + reader->data = NULL; + reader->bid = lzma_reader_bid; + reader->init = lzma_reader_init; + reader->free = lzma_reader_free; + return (ARCHIVE_OK); +} + +static int +lzma_reader_free(struct archive_reader *self){ + (void)self; /* UNUSED */ + return (ARCHIVE_OK); } /* @@ -85,11 +104,13 @@ archive_read_support_compression_lzma(struct archive *_a) * from verifying as much as we would like. */ static int -bid(const void *buff, size_t len) +lzma_reader_bid(struct archive_reader *self, const void *buff, size_t len) { const unsigned char *buffer; int bits_checked; + (void)self; /* UNUSED */ + if (len < 1) return (0); @@ -102,7 +123,6 @@ bid(const void *buff, size_t len) return (bits_checked); } - #ifndef HAVE_LZMADEC_H /* @@ -111,9 +131,12 @@ bid(const void *buff, size_t len) * and emit a useful message. */ static int -init(struct archive_read *a, const void *buff, size_t n) +lzma_reader_init(struct archive_read *a, struct archive_reader *reader, + struct archive_read_source *upstream, const void *buff, size_t n) { (void)a; /* UNUSED */ + (void)reader; /* UNUSED */ + (void)upstream; /* UNUSED */ (void)buff; /* UNUSED */ (void)n; /* UNUSED */ @@ -128,37 +151,43 @@ init(struct archive_read *a, const void *buff, size_t n) /* * Setup the callbacks. */ -static int -init(struct archive_read *a, const void *buff, size_t n) +static struct archive_read_source * +lzma_reader_init(struct archive_read *a, struct archive_reader *reader, + struct archive_read_source *upstream, const void *buff, size_t n) { + static const size_t out_block_size = 64 * 1024; + void *out_block; + struct archive_read_source *self; struct private_data *state; int ret; + (void)reader; /* UNUSED */ + a->archive.compression_code = ARCHIVE_COMPRESSION_LZMA; a->archive.compression_name = "lzma"; - state = (struct private_data *)malloc(sizeof(*state)); - if (state == NULL) { + self = calloc(sizeof(*self), 1); + state = (struct private_data *)calloc(sizeof(*state), 1); + out_block = (unsigned char *)malloc(out_block_size); + if (self == NULL || state == NULL || out_block == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate data for %s decompression", a->archive.compression_name); - return (ARCHIVE_FATAL); + free(out_block); + free(state); + free(self); + return (NULL); } - memset(state, 0, sizeof(*state)); - state->uncompressed_buffer_size = 64 * 1024; - state->uncompressed_buffer = (unsigned 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); - } + self->archive = a; + self->data = state; + state->out_block_size = out_block_size; + state->out_block = out_block; + self->upstream = upstream; + self->read = lzma_source_read; + self->skip = NULL; /* not supported */ + self->close = lzma_source_close; /* * A bug in lzmadec.h: stream.next_in should be marked 'const' @@ -166,27 +195,22 @@ init(struct archive_read *a, const void *buff, size_t n) * next_in pointer, only reads it). The result: this ugly * cast to remove 'const'. */ - state->stream.next_in = (uint8_t *)(uintptr_t)(const void *)buff; + 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; + state->stream.next_out = state->out_block; + state->stream.avail_out = state->out_block_size; /* Initialize compression library. */ ret = lzmadec_init(&(state->stream)); - if (ret == LZMADEC_OK) { - a->decompressor->data = state; - return (ARCHIVE_OK); - } + + if (ret == LZMADEC_OK) + return (self); /* 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) { @@ -198,174 +222,113 @@ init(struct archive_read *a, const void *buff, size_t n) case LZMADEC_MEM_ERROR: archive_set_error(&a->archive, ENOMEM, "Internal error initializing compression library: " - "not enough memory"); + "out of memory"); break; } - return (ARCHIVE_FATAL); + free(state->out_block); + free(state); + free(self); + return (NULL); } /* - * Return a block of data from the decompression buffer. Decompress more - * as necessary. + * Return the next block of decompressed data. */ static ssize_t -read_ahead(struct archive_read *a, const void **p, size_t min) +lzma_source_read(struct archive_read_source *self, const void **p) { struct private_data *state; - size_t read_avail, was_avail; + size_t read_avail, decompressed; + const void *read_buf; 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); - } + state = (struct private_data *)self->data; + read_avail = 0; - read_avail = state->stream.next_out - state->read_next; + /* Empty our output buffer. */ + state->stream.next_out = state->out_block; + state->stream.avail_out = state->out_block_size; - 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; - } + /* Try to fill the output buffer. */ + for (;;) { + /* If the last upstream block is done, get another one. */ + if (state->stream.avail_in == 0) { + ret = (self->upstream->read)(self->upstream, + &read_buf); + /* stream.next_in is really const, but lzmadec + * doesn't declare it so. */ + state->stream.next_in + = (unsigned char *)(uintptr_t)read_buf; + if (ret < 0) + return (ARCHIVE_FATAL); + /* There is no more data, return whatever we have. */ + if (ret == 0) { + *p = state->out_block; + decompressed = state->stream.next_out + - state->out_block; + state->total_out += decompressed; + return (decompressed); + } + state->stream.avail_in = ret; + } - 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? */ + /* Decompress as much as we can in one pass. */ + ret = lzmadec_decode(&(state->stream), + state->stream.avail_in == 0); + switch (ret) { + case LZMADEC_STREAM_END: /* Found end of stream. */ + /* TODO: Peek ahead to see if there's another + * stream so we can mimic the behavior of gunzip + * on concatenated streams. */ + state->eof = 1; + case LZMADEC_OK: /* Decompressor made some progress. */ + /* If we filled our buffer, update stats and return. */ + if (state->eof || state->stream.avail_out == 0) { + *p = state->out_block; + decompressed = state->stream.next_out + - state->out_block; + state->total_out += decompressed; + return (decompressed); + } break; + default: + /* Return an error. */ + archive_set_error(&self->archive->archive, + ARCHIVE_ERRNO_MISC, + "%s decompression failed", + self->archive->archive.compression_name); + return (ARCHIVE_FATAL); + } } - - *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 lzma decompressor"); - return (n); } /* * Clean up the decompressor. */ static int -finish(struct archive_read *a) +lzma_source_close(struct archive_read_source *self) { struct private_data *state; int ret; - state = (struct private_data *)a->decompressor->data; + state = (struct private_data *)self->data; ret = ARCHIVE_OK; switch (lzmadec_end(&(state->stream))) { case LZMADEC_OK: break; default: - archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + archive_set_error(&(self->archive->archive), + ARCHIVE_ERRNO_MISC, "Failed to clean up %s compressor", - a->archive.compression_name); + self->archive->archive.compression_name); ret = ARCHIVE_FATAL; } - free(state->uncompressed_buffer); + free(state->out_block); free(state); - - a->decompressor->data = NULL; + free(self); 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; - uint8_t *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 = lzmadec_decode(&(state->stream), state->stream.avail_in == 0); - decompressed = state->stream.next_out - output; - - /* Accumulate the total bytes of output. */ - state->total_out += decompressed; - total_decompressed += decompressed; - - switch (ret) { - case LZMADEC_OK: /* Decompressor made some progress. */ - if (decompressed > 0) - return (ARCHIVE_OK); - break; - case LZMADEC_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_LZMADEC_H */