2007-12-08 22:42:33 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
/// \file stream_decoder.c
|
|
|
|
/// \brief Decodes .lzma Streams
|
|
|
|
//
|
|
|
|
// Copyright (C) 2007 Lasse Collin
|
|
|
|
//
|
|
|
|
// This library is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
|
|
// License as published by the Free Software Foundation; either
|
|
|
|
// version 2.1 of the License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
// Lesser General Public License for more details.
|
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#include "stream_common.h"
|
|
|
|
#include "check.h"
|
|
|
|
#include "stream_flags_decoder.h"
|
|
|
|
#include "block_decoder.h"
|
|
|
|
#include "metadata_decoder.h"
|
|
|
|
|
|
|
|
|
|
|
|
struct lzma_coder_s {
|
|
|
|
enum {
|
|
|
|
SEQ_STREAM_HEADER_CODE,
|
|
|
|
SEQ_BLOCK_HEADER_INIT,
|
|
|
|
SEQ_BLOCK_HEADER_CODE,
|
|
|
|
SEQ_METADATA_CODE,
|
|
|
|
SEQ_DATA_CODE,
|
|
|
|
SEQ_STREAM_TAIL_INIT,
|
|
|
|
SEQ_STREAM_TAIL_CODE,
|
|
|
|
} sequence;
|
|
|
|
|
|
|
|
/// Position in variable-length integers and in some other things.
|
|
|
|
size_t pos;
|
|
|
|
|
|
|
|
/// Block or Metadata decoder. This takes little memory and the same
|
|
|
|
/// data structure can be used to decode every Block Header, so it's
|
|
|
|
/// a good idea to have a separate lzma_next_coder structure for it.
|
|
|
|
lzma_next_coder block_decoder;
|
|
|
|
|
|
|
|
/// Block Header decoder; this is separate
|
|
|
|
lzma_next_coder block_header_decoder;
|
|
|
|
|
|
|
|
lzma_options_block block_options;
|
|
|
|
|
|
|
|
/// Information about the sizes of the Blocks
|
|
|
|
lzma_info *info;
|
|
|
|
|
|
|
|
/// Current Block in *info
|
|
|
|
lzma_info_iter iter;
|
|
|
|
|
|
|
|
/// Number of bytes not yet processed from Data Blocks in the Stream.
|
|
|
|
/// This can be LZMA_VLI_VALUE_UNKNOWN. If it is known, it is
|
|
|
|
/// decremented while decoding and verified to match the reality.
|
|
|
|
lzma_vli total_left;
|
|
|
|
|
|
|
|
/// Like uncompressed_left above but for uncompressed data from
|
|
|
|
/// Data Blocks.
|
|
|
|
lzma_vli uncompressed_left;
|
|
|
|
|
|
|
|
/// Stream Flags from Stream Header
|
|
|
|
lzma_stream_flags header_flags;
|
|
|
|
|
|
|
|
/// Stream Flags from Stream tail
|
|
|
|
lzma_stream_flags tail_flags;
|
|
|
|
|
|
|
|
/// Decoder for Stream Header and Stream tail. This takes very
|
|
|
|
/// little memory and the same data structure can be used for
|
|
|
|
/// both Header and tail, so it's a good idea to have a separate
|
|
|
|
/// lzma_next_coder structure for it.
|
|
|
|
lzma_next_coder flags_decoder;
|
|
|
|
|
|
|
|
/// Temporary destination for the decoded Metadata.
|
|
|
|
lzma_metadata metadata;
|
|
|
|
|
|
|
|
/// Pointer to application-supplied pointer where to store the list
|
|
|
|
/// of Extra Records from the Header Metadata Block.
|
|
|
|
lzma_extra **header_extra;
|
|
|
|
|
|
|
|
/// Same as above but Footer Metadata Block
|
|
|
|
lzma_extra **footer_extra;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static lzma_ret
|
|
|
|
metadata_init(lzma_coder *coder, lzma_allocator *allocator)
|
|
|
|
{
|
|
|
|
assert(coder->metadata.index == NULL);
|
|
|
|
assert(coder->metadata.extra == NULL);
|
|
|
|
|
|
|
|
// Single-Block Streams don't have Metadata Blocks.
|
|
|
|
if (!coder->header_flags.is_multi)
|
|
|
|
return LZMA_DATA_ERROR;
|
|
|
|
|
|
|
|
coder->block_options.total_limit = LZMA_VLI_VALUE_UNKNOWN;
|
|
|
|
|
|
|
|
// Limit the Uncompressed Size of a Metadata Block. This is to
|
|
|
|
// prevent security issues where input file would have very huge
|
|
|
|
// Metadata.
|
|
|
|
//
|
|
|
|
// FIXME: Hardcoded constant is ugly. Maybe we should provide
|
|
|
|
// some way to specify this from the application.
|
|
|
|
coder->block_options.uncompressed_limit = LZMA_VLI_C(1) << 23;
|
|
|
|
|
|
|
|
lzma_info_size size_type;
|
|
|
|
bool want_extra;
|
|
|
|
|
|
|
|
// If we haven't decoded any Data Blocks yet, this is Header
|
|
|
|
// Metadata Block.
|
|
|
|
if (lzma_info_index_count_get(coder->info) == 0) {
|
|
|
|
coder->block_options.has_backward_size = false;
|
|
|
|
coder->block_options.handle_padding = true;
|
|
|
|
size_type = LZMA_INFO_HEADER_METADATA;
|
|
|
|
want_extra = coder->header_extra != NULL;
|
|
|
|
} else {
|
|
|
|
if (lzma_info_index_finish(coder->info))
|
|
|
|
return LZMA_DATA_ERROR;
|
|
|
|
|
|
|
|
coder->block_options.has_backward_size = true;
|
|
|
|
coder->block_options.handle_padding = false;
|
|
|
|
size_type = LZMA_INFO_FOOTER_METADATA;
|
|
|
|
want_extra = coder->footer_extra != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
coder->block_options.has_uncompressed_size_in_footer = false;
|
|
|
|
coder->block_options.total_size = lzma_info_size_get(
|
|
|
|
coder->info, size_type);
|
|
|
|
|
|
|
|
coder->sequence = SEQ_METADATA_CODE;
|
|
|
|
|
|
|
|
return lzma_metadata_decoder_init(&coder->block_decoder, allocator,
|
|
|
|
&coder->block_options, &coder->metadata, want_extra);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static lzma_ret
|
|
|
|
data_init(lzma_coder *coder, lzma_allocator *allocator)
|
|
|
|
{
|
2007-12-09 15:06:45 +00:00
|
|
|
return_if_error(lzma_info_iter_next(&coder->iter, allocator));
|
2007-12-08 22:42:33 +00:00
|
|
|
|
2007-12-09 15:06:45 +00:00
|
|
|
return_if_error(lzma_info_iter_set(
|
|
|
|
&coder->iter, LZMA_VLI_VALUE_UNKNOWN,
|
|
|
|
coder->block_options.uncompressed_size));
|
2007-12-08 22:42:33 +00:00
|
|
|
|
|
|
|
coder->block_options.total_size = coder->iter.total_size;
|
|
|
|
coder->block_options.uncompressed_size = coder->iter.uncompressed_size;
|
|
|
|
coder->block_options.total_limit = coder->total_left;
|
|
|
|
coder->block_options.uncompressed_limit = coder->uncompressed_left;
|
|
|
|
|
|
|
|
if (coder->header_flags.is_multi) {
|
|
|
|
coder->block_options.has_uncompressed_size_in_footer = false;
|
|
|
|
coder->block_options.has_backward_size = false;
|
|
|
|
coder->block_options.handle_padding = true;
|
|
|
|
} else {
|
|
|
|
coder->block_options.has_uncompressed_size_in_footer
|
|
|
|
= coder->iter.uncompressed_size
|
|
|
|
== LZMA_VLI_VALUE_UNKNOWN;
|
|
|
|
coder->block_options.has_backward_size = true;
|
|
|
|
coder->block_options.handle_padding = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
coder->sequence = SEQ_DATA_CODE;
|
|
|
|
|
|
|
|
return lzma_block_decoder_init(&coder->block_decoder, allocator,
|
|
|
|
&coder->block_options);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static lzma_ret
|
|
|
|
stream_decode(lzma_coder *coder, lzma_allocator *allocator,
|
|
|
|
const uint8_t *restrict in, size_t *restrict in_pos,
|
|
|
|
size_t in_size, uint8_t *restrict out,
|
|
|
|
size_t *restrict out_pos, size_t out_size, lzma_action action)
|
|
|
|
{
|
|
|
|
while (*out_pos < out_size && (*in_pos < in_size
|
|
|
|
|| coder->sequence == SEQ_DATA_CODE))
|
|
|
|
switch (coder->sequence) {
|
|
|
|
case SEQ_STREAM_HEADER_CODE: {
|
|
|
|
const lzma_ret ret = coder->flags_decoder.code(
|
|
|
|
coder->flags_decoder.coder,
|
|
|
|
allocator, in, in_pos, in_size,
|
|
|
|
NULL, NULL, 0, LZMA_RUN);
|
|
|
|
if (ret != LZMA_STREAM_END)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
coder->sequence = SEQ_BLOCK_HEADER_INIT;
|
|
|
|
|
|
|
|
// Detect if the Check type is supported and give appropriate
|
|
|
|
// warning if it isn't. We don't warn every time a new Block
|
|
|
|
// is started.
|
|
|
|
lzma_check tmp;
|
|
|
|
if (lzma_check_init(&tmp, coder->header_flags.check))
|
|
|
|
return LZMA_UNSUPPORTED_CHECK;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SEQ_BLOCK_HEADER_INIT: {
|
|
|
|
coder->block_options.check = coder->header_flags.check;
|
|
|
|
coder->block_options.has_crc32 = coder->header_flags.has_crc32;
|
|
|
|
|
2008-01-16 12:48:04 +00:00
|
|
|
for (size_t i = 0;
|
|
|
|
i < ARRAY_SIZE(coder->block_options.filters);
|
|
|
|
++i) {
|
|
|
|
lzma_free(coder->block_options.filters[i].options,
|
|
|
|
allocator);
|
|
|
|
coder->block_options.filters[i].options = NULL;
|
|
|
|
}
|
|
|
|
|
2007-12-09 15:06:45 +00:00
|
|
|
return_if_error(lzma_block_header_decoder_init(
|
2007-12-08 22:42:33 +00:00
|
|
|
&coder->block_header_decoder, allocator,
|
2007-12-09 15:06:45 +00:00
|
|
|
&coder->block_options));
|
2007-12-08 22:42:33 +00:00
|
|
|
|
|
|
|
coder->sequence = SEQ_BLOCK_HEADER_CODE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fall through
|
|
|
|
|
|
|
|
case SEQ_BLOCK_HEADER_CODE: {
|
|
|
|
lzma_ret ret = coder->block_header_decoder.code(
|
|
|
|
coder->block_header_decoder.coder,
|
|
|
|
allocator, in, in_pos, in_size,
|
|
|
|
NULL, NULL, 0, LZMA_RUN);
|
|
|
|
|
|
|
|
if (ret != LZMA_STREAM_END)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (coder->block_options.is_metadata)
|
|
|
|
ret = metadata_init(coder, allocator);
|
|
|
|
else
|
|
|
|
ret = data_init(coder, allocator);
|
|
|
|
|
|
|
|
if (ret != LZMA_OK)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SEQ_METADATA_CODE: {
|
|
|
|
lzma_ret ret = coder->block_decoder.code(
|
|
|
|
coder->block_decoder.coder, allocator,
|
|
|
|
in, in_pos, in_size, NULL, NULL, 0, LZMA_RUN);
|
|
|
|
if (ret != LZMA_STREAM_END)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
const bool is_header_metadata = lzma_info_index_count_get(
|
|
|
|
coder->info) == 0;
|
|
|
|
|
|
|
|
if (is_header_metadata) {
|
|
|
|
if (coder->header_extra != NULL) {
|
|
|
|
*coder->header_extra = coder->metadata.extra;
|
|
|
|
coder->metadata.extra = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lzma_info_size_set(coder->info,
|
|
|
|
LZMA_INFO_HEADER_METADATA,
|
|
|
|
coder->block_options.total_size)
|
|
|
|
!= LZMA_OK)
|
|
|
|
return LZMA_PROG_ERROR;
|
|
|
|
|
|
|
|
coder->sequence = SEQ_BLOCK_HEADER_INIT;
|
|
|
|
} else {
|
|
|
|
if (coder->footer_extra != NULL) {
|
|
|
|
*coder->footer_extra = coder->metadata.extra;
|
|
|
|
coder->metadata.extra = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
coder->sequence = SEQ_STREAM_TAIL_INIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(coder->metadata.extra == NULL);
|
|
|
|
|
|
|
|
ret = lzma_info_metadata_set(coder->info, allocator,
|
|
|
|
&coder->metadata, is_header_metadata, true);
|
|
|
|
if (ret != LZMA_OK)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
// Intialize coder->total_size and coder->uncompressed_size
|
|
|
|
// from Header Metadata.
|
|
|
|
if (is_header_metadata) {
|
|
|
|
coder->total_left = lzma_info_size_get(
|
|
|
|
coder->info, LZMA_INFO_TOTAL);
|
|
|
|
coder->uncompressed_left = lzma_info_size_get(
|
|
|
|
coder->info, LZMA_INFO_UNCOMPRESSED);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SEQ_DATA_CODE: {
|
|
|
|
lzma_ret ret = coder->block_decoder.code(
|
|
|
|
coder->block_decoder.coder, allocator,
|
|
|
|
in, in_pos, in_size, out, out_pos, out_size,
|
|
|
|
action);
|
|
|
|
|
|
|
|
if (ret != LZMA_STREAM_END)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = lzma_info_iter_set(&coder->iter,
|
|
|
|
coder->block_options.total_size,
|
|
|
|
coder->block_options.uncompressed_size);
|
|
|
|
if (ret != LZMA_OK)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
// These won't overflow since lzma_info_iter_set() succeeded.
|
|
|
|
if (coder->total_left != LZMA_VLI_VALUE_UNKNOWN)
|
|
|
|
coder->total_left -= coder->block_options.total_size;
|
|
|
|
if (coder->uncompressed_left != LZMA_VLI_VALUE_UNKNOWN)
|
|
|
|
coder->uncompressed_left -= coder->block_options
|
|
|
|
.uncompressed_size;
|
|
|
|
|
|
|
|
if (!coder->header_flags.is_multi) {
|
|
|
|
ret = lzma_info_index_finish(coder->info);
|
|
|
|
if (ret != LZMA_OK)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
coder->sequence = SEQ_STREAM_TAIL_INIT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
coder->sequence = SEQ_BLOCK_HEADER_INIT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SEQ_STREAM_TAIL_INIT: {
|
|
|
|
lzma_ret ret = lzma_info_index_finish(coder->info);
|
|
|
|
if (ret != LZMA_OK)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = lzma_stream_tail_decoder_init(&coder->flags_decoder,
|
|
|
|
allocator, &coder->tail_flags);
|
|
|
|
if (ret != LZMA_OK)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
coder->sequence = SEQ_STREAM_TAIL_CODE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fall through
|
|
|
|
|
|
|
|
case SEQ_STREAM_TAIL_CODE: {
|
|
|
|
const lzma_ret ret = coder->flags_decoder.code(
|
|
|
|
coder->flags_decoder.coder, allocator,
|
|
|
|
in, in_pos, in_size, NULL, NULL, 0, LZMA_RUN);
|
|
|
|
if (ret != LZMA_STREAM_END)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (!lzma_stream_flags_is_equal(
|
|
|
|
coder->header_flags, coder->tail_flags))
|
|
|
|
return LZMA_DATA_ERROR;
|
|
|
|
|
|
|
|
return LZMA_STREAM_END;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return LZMA_PROG_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return LZMA_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
stream_decoder_end(lzma_coder *coder, lzma_allocator *allocator)
|
|
|
|
{
|
2008-01-16 12:48:04 +00:00
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(coder->block_options.filters); ++i)
|
|
|
|
lzma_free(coder->block_options.filters[i].options, allocator);
|
|
|
|
|
2007-12-08 22:42:33 +00:00
|
|
|
lzma_next_coder_end(&coder->block_decoder, allocator);
|
|
|
|
lzma_next_coder_end(&coder->block_header_decoder, allocator);
|
|
|
|
lzma_next_coder_end(&coder->flags_decoder, allocator);
|
|
|
|
lzma_info_free(coder->info, allocator);
|
|
|
|
lzma_index_free(coder->metadata.index, allocator);
|
|
|
|
lzma_extra_free(coder->metadata.extra, allocator);
|
|
|
|
lzma_free(coder, allocator);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static lzma_ret
|
|
|
|
stream_decoder_init(lzma_next_coder *next, lzma_allocator *allocator,
|
|
|
|
lzma_extra **header, lzma_extra **footer)
|
|
|
|
{
|
|
|
|
if (next->coder == NULL) {
|
|
|
|
next->coder = lzma_alloc(sizeof(lzma_coder), allocator);
|
|
|
|
if (next->coder == NULL)
|
|
|
|
return LZMA_MEM_ERROR;
|
|
|
|
|
|
|
|
next->code = &stream_decode;
|
|
|
|
next->end = &stream_decoder_end;
|
|
|
|
|
|
|
|
next->coder->block_decoder = LZMA_NEXT_CODER_INIT;
|
|
|
|
next->coder->block_header_decoder = LZMA_NEXT_CODER_INIT;
|
|
|
|
next->coder->info = NULL;
|
|
|
|
next->coder->flags_decoder = LZMA_NEXT_CODER_INIT;
|
|
|
|
next->coder->metadata.index = NULL;
|
|
|
|
next->coder->metadata.extra = NULL;
|
|
|
|
} else {
|
2008-01-16 12:48:04 +00:00
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(
|
|
|
|
next->coder->block_options.filters); ++i)
|
|
|
|
lzma_free(next->coder->block_options
|
|
|
|
.filters[i].options, allocator);
|
|
|
|
|
2007-12-08 22:42:33 +00:00
|
|
|
lzma_index_free(next->coder->metadata.index, allocator);
|
|
|
|
next->coder->metadata.index = NULL;
|
|
|
|
|
|
|
|
lzma_extra_free(next->coder->metadata.extra, allocator);
|
|
|
|
next->coder->metadata.extra = NULL;
|
|
|
|
}
|
|
|
|
|
2008-01-16 12:48:04 +00:00
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(next->coder->block_options.filters);
|
|
|
|
++i)
|
|
|
|
next->coder->block_options.filters[i].options = NULL;
|
|
|
|
|
2007-12-08 22:42:33 +00:00
|
|
|
next->coder->info = lzma_info_init(next->coder->info, allocator);
|
|
|
|
if (next->coder->info == NULL)
|
|
|
|
return LZMA_MEM_ERROR;
|
|
|
|
|
|
|
|
lzma_info_iter_begin(next->coder->info, &next->coder->iter);
|
|
|
|
|
|
|
|
// Initialize Stream Header decoder.
|
|
|
|
return_if_error(lzma_stream_header_decoder_init(
|
|
|
|
&next->coder->flags_decoder, allocator,
|
|
|
|
&next->coder->header_flags));
|
|
|
|
|
|
|
|
// Reset the *foo_extra pointers to NULL. This way the caller knows
|
|
|
|
// if there were no Extra Records. (We don't support appending
|
|
|
|
// Records to Extra list.)
|
|
|
|
if (header != NULL)
|
|
|
|
*header = NULL;
|
|
|
|
if (footer != NULL)
|
|
|
|
*footer = NULL;
|
|
|
|
|
|
|
|
// Reset some variables.
|
|
|
|
next->coder->sequence = SEQ_STREAM_HEADER_CODE;
|
|
|
|
next->coder->pos = 0;
|
|
|
|
next->coder->uncompressed_left = LZMA_VLI_VALUE_UNKNOWN;
|
|
|
|
next->coder->total_left = LZMA_VLI_VALUE_UNKNOWN;
|
|
|
|
next->coder->header_extra = header;
|
|
|
|
next->coder->footer_extra = footer;
|
|
|
|
|
|
|
|
return LZMA_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extern lzma_ret
|
|
|
|
lzma_stream_decoder_init(lzma_next_coder *next, lzma_allocator *allocator,
|
|
|
|
lzma_extra **header, lzma_extra **footer)
|
|
|
|
{
|
|
|
|
lzma_next_coder_init(
|
|
|
|
stream_decoder_init, next, allocator, header, footer);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extern LZMA_API lzma_ret
|
|
|
|
lzma_stream_decoder(lzma_stream *strm,
|
|
|
|
lzma_extra **header, lzma_extra **footer)
|
|
|
|
{
|
|
|
|
lzma_next_strm_init(strm, stream_decoder_init, header, footer);
|
|
|
|
|
|
|
|
strm->internal->supported_actions[LZMA_RUN] = true;
|
|
|
|
strm->internal->supported_actions[LZMA_SYNC_FLUSH] = true;
|
|
|
|
|
|
|
|
return LZMA_OK;
|
|
|
|
}
|