mirror of
https://git.tukaani.org/xz.git
synced 2024-04-04 12:36:23 +02:00
99e12af4e2
lzma_metadata.header_metadata_size == LZMA_VLI_VALUE_UNKNOWN is not allowed at all. To indicate missing Header Metadata Block, header_metadata_size must be set to zero. This is what Metadata decoder does after this patch too. Note that other missing fields in lzma_metadata are still indicated with LZMA_VLI_VALUE_UNKNOWN. This isn't as illogical as it sounds at first, because missing Size of Header Metadata Block means that Header Metadata Block is not present in the Stream. With other Metadata fields, a missing field means only that the value is unknown.
814 lines
21 KiB
C
814 lines
21 KiB
C
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
/// \file info.c
|
|
/// \brief Collects and verifies integrity of Stream size information
|
|
//
|
|
// 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 "common.h"
|
|
|
|
|
|
struct lzma_info_s {
|
|
struct {
|
|
/// Known Size of Header Metadata Block; here's some
|
|
/// special things:
|
|
/// - LZMA_VLI_VALUE_UNKNOWN indicates that we don't know
|
|
/// if Header Metadata Block is present.
|
|
/// - 0 indicates that Header Metadata Block is not present.
|
|
lzma_vli header_metadata_size;
|
|
|
|
/// Known Total Size of the Data Blocks in the Stream
|
|
lzma_vli total_size;
|
|
|
|
/// Known Uncompressed Size of the Data Blocks in the Stream
|
|
lzma_vli uncompressed_size;
|
|
|
|
/// Known Size of Footer Metadata Block
|
|
lzma_vli footer_metadata_size;
|
|
} known;
|
|
|
|
struct {
|
|
/// Sum of Total Size fields stored to the Index so far
|
|
lzma_vli total_size;
|
|
|
|
/// Sum of Uncompressed Size fields stored to the Index so far
|
|
lzma_vli uncompressed_size;
|
|
|
|
/// First Index Record in the list, or NULL if Index is empty.
|
|
lzma_index *head;
|
|
|
|
/// Number of Index Records
|
|
size_t record_count;
|
|
|
|
/// Number of Index Records
|
|
size_t incomplete_count;
|
|
|
|
/// True when we know that no more Records will get added
|
|
/// to the Index.
|
|
bool is_final;
|
|
} index;
|
|
|
|
/// Start offset of the Stream. This is needed to calculate
|
|
/// lzma_info_iter.stream_offset.
|
|
lzma_vli stream_start_offset;
|
|
|
|
/// True if Index is present in Header Metadata Block
|
|
bool has_index_in_header_metadata;
|
|
};
|
|
|
|
|
|
//////////////////////
|
|
// Create/Reset/End //
|
|
//////////////////////
|
|
|
|
static void
|
|
index_init(lzma_info *info)
|
|
{
|
|
info->index.total_size = 0;
|
|
info->index.uncompressed_size = 0;
|
|
info->index.head = NULL;
|
|
info->index.record_count = 0;
|
|
info->index.incomplete_count = 0;
|
|
info->index.is_final = false;
|
|
return;
|
|
}
|
|
|
|
|
|
static void
|
|
info_init(lzma_info *info)
|
|
{
|
|
info->known.header_metadata_size = LZMA_VLI_VALUE_UNKNOWN;
|
|
info->known.total_size = LZMA_VLI_VALUE_UNKNOWN;
|
|
info->known.uncompressed_size = LZMA_VLI_VALUE_UNKNOWN;
|
|
info->known.footer_metadata_size = LZMA_VLI_VALUE_UNKNOWN;
|
|
info->stream_start_offset = 0;
|
|
info->has_index_in_header_metadata = false;
|
|
|
|
index_init(info);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
extern LZMA_API lzma_info *
|
|
lzma_info_init(lzma_info *info, lzma_allocator *allocator)
|
|
{
|
|
if (info == NULL)
|
|
info = lzma_alloc(sizeof(lzma_info), allocator);
|
|
else
|
|
lzma_index_free(info->index.head, allocator);
|
|
|
|
if (info != NULL)
|
|
info_init(info);
|
|
|
|
return info;
|
|
}
|
|
|
|
|
|
extern LZMA_API void
|
|
lzma_info_free(lzma_info *info, lzma_allocator *allocator)
|
|
{
|
|
lzma_index_free(info->index.head, allocator);
|
|
lzma_free(info, allocator);
|
|
return;
|
|
}
|
|
|
|
|
|
/////////
|
|
// Set //
|
|
/////////
|
|
|
|
static lzma_ret
|
|
set_size(lzma_vli new_size, lzma_vli *known_size, lzma_vli index_size,
|
|
bool forbid_zero)
|
|
{
|
|
assert(new_size <= LZMA_VLI_VALUE_MAX);
|
|
|
|
lzma_ret ret = LZMA_OK;
|
|
|
|
if (forbid_zero && new_size == 0)
|
|
ret = LZMA_PROG_ERROR;
|
|
else if (index_size > new_size)
|
|
ret = LZMA_DATA_ERROR;
|
|
else if (*known_size == LZMA_VLI_VALUE_UNKNOWN)
|
|
*known_size = new_size;
|
|
else if (*known_size != new_size)
|
|
ret = LZMA_DATA_ERROR;
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
extern LZMA_API lzma_ret
|
|
lzma_info_size_set(lzma_info *info, lzma_info_size type, lzma_vli size)
|
|
{
|
|
if (size > LZMA_VLI_VALUE_MAX)
|
|
return LZMA_PROG_ERROR;
|
|
|
|
switch (type) {
|
|
case LZMA_INFO_STREAM_START:
|
|
info->stream_start_offset = size;
|
|
return LZMA_OK;
|
|
|
|
case LZMA_INFO_HEADER_METADATA:
|
|
return set_size(size, &info->known.header_metadata_size,
|
|
0, false);
|
|
|
|
case LZMA_INFO_TOTAL:
|
|
return set_size(size, &info->known.total_size,
|
|
info->index.total_size, true);
|
|
|
|
case LZMA_INFO_UNCOMPRESSED:
|
|
return set_size(size, &info->known.uncompressed_size,
|
|
info->index.uncompressed_size, false);
|
|
|
|
case LZMA_INFO_FOOTER_METADATA:
|
|
return set_size(size, &info->known.footer_metadata_size,
|
|
0, true);
|
|
}
|
|
|
|
return LZMA_PROG_ERROR;
|
|
}
|
|
|
|
|
|
extern LZMA_API lzma_ret
|
|
lzma_info_index_set(lzma_info *info, lzma_allocator *allocator,
|
|
lzma_index *i_new, lzma_bool eat_index)
|
|
{
|
|
if (i_new == NULL)
|
|
return LZMA_PROG_ERROR;
|
|
|
|
lzma_index *i_old = info->index.head;
|
|
|
|
if (i_old != NULL) {
|
|
while (true) {
|
|
// If the new Index has fewer Records than the old one,
|
|
// the new Index cannot be valid.
|
|
if (i_new == NULL)
|
|
return LZMA_DATA_ERROR;
|
|
|
|
// The new Index must be complete i.e. no unknown
|
|
// values.
|
|
if (i_new->total_size > LZMA_VLI_VALUE_MAX
|
|
|| i_new->uncompressed_size
|
|
> LZMA_VLI_VALUE_MAX) {
|
|
if (eat_index)
|
|
lzma_index_free(i_new, allocator);
|
|
|
|
return LZMA_PROG_ERROR;
|
|
}
|
|
|
|
// Compare the values from the new Index with the old
|
|
// Index. The old Index may be incomplete; in that
|
|
// case we
|
|
// - use the value from the new Index as is;
|
|
// - update the appropriate info->index.foo_size; and
|
|
// - decrease the count of incomplete Index Records.
|
|
bool was_incomplete = false;
|
|
|
|
if (i_old->total_size == LZMA_VLI_VALUE_UNKNOWN) {
|
|
assert(!info->index.is_final);
|
|
was_incomplete = true;
|
|
|
|
i_old->total_size = i_new->total_size;
|
|
|
|
if (lzma_vli_add(info->index.total_size,
|
|
i_new->total_size)) {
|
|
if (eat_index)
|
|
lzma_index_free(i_new,
|
|
allocator);
|
|
|
|
return LZMA_PROG_ERROR;
|
|
}
|
|
} else if (i_old->total_size != i_new->total_size) {
|
|
if (eat_index)
|
|
lzma_index_free(i_new, allocator);
|
|
|
|
return LZMA_DATA_ERROR;
|
|
}
|
|
|
|
if (i_old->uncompressed_size
|
|
== LZMA_VLI_VALUE_UNKNOWN) {
|
|
assert(!info->index.is_final);
|
|
was_incomplete = true;
|
|
|
|
i_old->uncompressed_size
|
|
= i_new->uncompressed_size;
|
|
|
|
if (lzma_vli_add(info->index.uncompressed_size,
|
|
i_new->uncompressed_size)) {
|
|
if (eat_index)
|
|
lzma_index_free(i_new,
|
|
allocator);
|
|
|
|
return LZMA_PROG_ERROR;
|
|
}
|
|
} else if (i_old->uncompressed_size
|
|
!= i_new->uncompressed_size) {
|
|
if (eat_index)
|
|
lzma_index_free(i_new, allocator);
|
|
|
|
return LZMA_DATA_ERROR;
|
|
}
|
|
|
|
if (was_incomplete) {
|
|
assert(!info->index.is_final);
|
|
assert(info->index.incomplete_count > 0);
|
|
--info->index.incomplete_count;
|
|
}
|
|
|
|
// Get rid of *i_new. It's now identical with *i_old.
|
|
lzma_index *tmp = i_new->next;
|
|
if (eat_index)
|
|
lzma_free(i_new, allocator);
|
|
|
|
i_new = tmp;
|
|
|
|
// We want to leave i_old pointing to the last
|
|
// Index Record in the old Index. This way we can
|
|
// concatenate the possible new Records from i_new.
|
|
if (i_old->next == NULL)
|
|
break;
|
|
|
|
i_old = i_old->next;
|
|
}
|
|
}
|
|
|
|
assert(info->index.incomplete_count == 0);
|
|
|
|
// If Index was already known to be final, i_new must be NULL now.
|
|
// The new Index cannot contain more Records that we already have.
|
|
if (info->index.is_final) {
|
|
assert(info->index.head != NULL);
|
|
|
|
if (i_new != NULL) {
|
|
if (eat_index)
|
|
lzma_index_free(i_new, allocator);
|
|
|
|
return LZMA_DATA_ERROR;
|
|
}
|
|
|
|
return LZMA_OK;
|
|
}
|
|
|
|
// The rest of the new Index is merged to the old Index. Keep the
|
|
// current i_new pointer in available. We need it when merging the
|
|
// new Index with the old one, and if an error occurs so we can
|
|
// get rid of the broken part of the new Index.
|
|
lzma_index *i_start = i_new;
|
|
while (i_new != NULL) {
|
|
// The new Index must be complete i.e. no unknown values.
|
|
if (i_new->total_size > LZMA_VLI_VALUE_MAX
|
|
|| i_new->uncompressed_size
|
|
> LZMA_VLI_VALUE_MAX) {
|
|
if (eat_index)
|
|
lzma_index_free(i_start, allocator);
|
|
|
|
return LZMA_PROG_ERROR;
|
|
}
|
|
|
|
// Update info->index.foo_sizes.
|
|
if (lzma_vli_add(info->index.total_size, i_new->total_size)
|
|
|| lzma_vli_add(info->index.uncompressed_size,
|
|
i_new->uncompressed_size)) {
|
|
if (eat_index)
|
|
lzma_index_free(i_start, allocator);
|
|
|
|
return LZMA_PROG_ERROR;
|
|
}
|
|
|
|
++info->index.record_count;
|
|
i_new = i_new->next;
|
|
}
|
|
|
|
// All the Records in the new Index are good, and info->index.foo_sizes
|
|
// were successfully updated.
|
|
if (lzma_info_index_finish(info) != LZMA_OK) {
|
|
if (eat_index)
|
|
lzma_index_free(i_start, allocator);
|
|
|
|
return LZMA_DATA_ERROR;
|
|
}
|
|
|
|
// The Index is ready to be merged. If we aren't supposed to eat
|
|
// the Index, make a copy of it first.
|
|
if (!eat_index && i_start != NULL) {
|
|
i_start = lzma_index_dup(i_start, allocator);
|
|
if (i_start == NULL)
|
|
return LZMA_MEM_ERROR;
|
|
}
|
|
|
|
// Concatenate the new Index with the old one. Note that it is
|
|
// possible that we don't have any old Index.
|
|
if (info->index.head == NULL)
|
|
info->index.head = i_start;
|
|
else
|
|
i_old->next = i_start;
|
|
|
|
return LZMA_OK;
|
|
}
|
|
|
|
|
|
extern LZMA_API lzma_ret
|
|
lzma_info_metadata_set(lzma_info *info, lzma_allocator *allocator,
|
|
lzma_metadata *metadata, lzma_bool is_header_metadata,
|
|
lzma_bool eat_index)
|
|
{
|
|
// Validate *metadata.
|
|
if (metadata->header_metadata_size > LZMA_VLI_VALUE_MAX
|
|
|| !lzma_vli_is_valid(metadata->total_size)
|
|
|| !lzma_vli_is_valid(metadata->uncompressed_size)) {
|
|
if (eat_index) {
|
|
lzma_index_free(metadata->index, allocator);
|
|
metadata->index = NULL;
|
|
}
|
|
|
|
return LZMA_PROG_ERROR;
|
|
}
|
|
|
|
// Index
|
|
if (metadata->index != NULL) {
|
|
if (is_header_metadata)
|
|
info->has_index_in_header_metadata = true;
|
|
|
|
const lzma_ret ret = lzma_info_index_set(
|
|
info, allocator, metadata->index, eat_index);
|
|
|
|
if (eat_index)
|
|
metadata->index = NULL;
|
|
|
|
if (ret != LZMA_OK)
|
|
return ret;
|
|
|
|
} else if (!is_header_metadata
|
|
&& (metadata->total_size == LZMA_VLI_VALUE_UNKNOWN
|
|
|| !info->has_index_in_header_metadata)) {
|
|
// Either Total Size or Index must be present in Footer
|
|
// Metadata Block. If Index is not present, it must have
|
|
// already been in the Header Metadata Block. Since we
|
|
// got here, these conditions weren't met.
|
|
return LZMA_DATA_ERROR;
|
|
}
|
|
|
|
// Size of Header Metadata
|
|
if (!is_header_metadata)
|
|
return_if_error(lzma_info_size_set(
|
|
info, LZMA_INFO_HEADER_METADATA,
|
|
metadata->header_metadata_size));
|
|
|
|
// Total Size
|
|
if (metadata->total_size != LZMA_VLI_VALUE_UNKNOWN)
|
|
return_if_error(lzma_info_size_set(info,
|
|
LZMA_INFO_TOTAL, metadata->total_size));
|
|
|
|
// Uncompressed Size
|
|
if (metadata->uncompressed_size != LZMA_VLI_VALUE_UNKNOWN)
|
|
return_if_error(lzma_info_size_set(info,
|
|
LZMA_INFO_UNCOMPRESSED,
|
|
metadata->uncompressed_size));
|
|
|
|
return LZMA_OK;
|
|
}
|
|
|
|
|
|
/////////
|
|
// Get //
|
|
/////////
|
|
|
|
extern LZMA_API lzma_vli
|
|
lzma_info_size_get(const lzma_info *info, lzma_info_size type)
|
|
{
|
|
switch (type) {
|
|
case LZMA_INFO_STREAM_START:
|
|
return info->stream_start_offset;
|
|
|
|
case LZMA_INFO_HEADER_METADATA:
|
|
return info->known.header_metadata_size;
|
|
|
|
case LZMA_INFO_TOTAL:
|
|
return info->known.total_size;
|
|
|
|
case LZMA_INFO_UNCOMPRESSED:
|
|
return info->known.uncompressed_size;
|
|
|
|
case LZMA_INFO_FOOTER_METADATA:
|
|
return info->known.footer_metadata_size;
|
|
}
|
|
|
|
return LZMA_VLI_VALUE_UNKNOWN;
|
|
}
|
|
|
|
|
|
extern LZMA_API lzma_index *
|
|
lzma_info_index_get(lzma_info *info, lzma_bool detach)
|
|
{
|
|
lzma_index *i = info->index.head;
|
|
|
|
if (detach)
|
|
index_init(info);
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
extern LZMA_API size_t
|
|
lzma_info_index_count_get(const lzma_info *info)
|
|
{
|
|
return info->index.record_count;
|
|
}
|
|
|
|
|
|
/////////////////
|
|
// Incremental //
|
|
/////////////////
|
|
|
|
enum {
|
|
ITER_INFO,
|
|
ITER_INDEX,
|
|
ITER_RESERVED_1,
|
|
ITER_RESERVED_2,
|
|
};
|
|
|
|
|
|
#define iter_info ((lzma_info *)(iter->internal[ITER_INFO]))
|
|
|
|
#define iter_index ((lzma_index *)(iter->internal[ITER_INDEX]))
|
|
|
|
|
|
extern LZMA_API void
|
|
lzma_info_iter_begin(lzma_info *info, lzma_info_iter *iter)
|
|
{
|
|
*iter = (lzma_info_iter){
|
|
.total_size = LZMA_VLI_VALUE_UNKNOWN,
|
|
.uncompressed_size = LZMA_VLI_VALUE_UNKNOWN,
|
|
.stream_offset = LZMA_VLI_VALUE_UNKNOWN,
|
|
.uncompressed_offset = LZMA_VLI_VALUE_UNKNOWN,
|
|
.internal = { info, NULL, NULL, NULL },
|
|
};
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
extern LZMA_API lzma_ret
|
|
lzma_info_iter_next(lzma_info_iter *iter, lzma_allocator *allocator)
|
|
{
|
|
// FIXME debug remove
|
|
lzma_info *info = iter_info;
|
|
(void)info;
|
|
|
|
if (iter_index == NULL) {
|
|
// The first call after lzma_info_iter_begin().
|
|
if (iter_info->known.header_metadata_size
|
|
== LZMA_VLI_VALUE_UNKNOWN)
|
|
iter->stream_offset = LZMA_VLI_VALUE_UNKNOWN;
|
|
else if (lzma_vli_sum3(iter->stream_offset,
|
|
iter_info->stream_start_offset,
|
|
LZMA_STREAM_HEADER_SIZE,
|
|
iter_info->known.header_metadata_size))
|
|
return LZMA_PROG_ERROR;
|
|
|
|
iter->uncompressed_offset = 0;
|
|
|
|
if (iter_info->index.head != NULL) {
|
|
// The first Index Record has already been allocated.
|
|
iter->internal[ITER_INDEX] = iter_info->index.head;
|
|
iter->total_size = iter_index->total_size;
|
|
iter->uncompressed_size
|
|
= iter_index->uncompressed_size;
|
|
return LZMA_OK;
|
|
}
|
|
} else {
|
|
// Update iter->*_offsets.
|
|
if (iter->stream_offset != LZMA_VLI_VALUE_UNKNOWN) {
|
|
if (iter_index->total_size == LZMA_VLI_VALUE_UNKNOWN)
|
|
iter->stream_offset = LZMA_VLI_VALUE_UNKNOWN;
|
|
else if (lzma_vli_add(iter->stream_offset,
|
|
iter_index->total_size))
|
|
return LZMA_DATA_ERROR;
|
|
}
|
|
|
|
if (iter->uncompressed_offset != LZMA_VLI_VALUE_UNKNOWN) {
|
|
if (iter_index->uncompressed_size
|
|
== LZMA_VLI_VALUE_UNKNOWN)
|
|
iter->uncompressed_offset
|
|
= LZMA_VLI_VALUE_UNKNOWN;
|
|
else if (lzma_vli_add(iter->uncompressed_offset,
|
|
iter_index->uncompressed_size))
|
|
return LZMA_DATA_ERROR;
|
|
}
|
|
|
|
if (iter_index->next != NULL) {
|
|
// The next Record has already been allocated.
|
|
iter->internal[ITER_INDEX] = iter_index->next;
|
|
iter->total_size = iter_index->total_size;
|
|
iter->uncompressed_size
|
|
= iter_index->uncompressed_size;
|
|
return LZMA_OK;
|
|
}
|
|
}
|
|
|
|
// Don't add new Records to a final Index.
|
|
if (iter_info->index.is_final)
|
|
return LZMA_DATA_ERROR;
|
|
|
|
// Allocate and initialize a new Index Record.
|
|
lzma_index *i = lzma_alloc(sizeof(lzma_index), allocator);
|
|
if (i == NULL)
|
|
return LZMA_MEM_ERROR;
|
|
|
|
i->total_size = LZMA_VLI_VALUE_UNKNOWN;
|
|
i->uncompressed_size = LZMA_VLI_VALUE_UNKNOWN;
|
|
i->next = NULL;
|
|
|
|
iter->total_size = LZMA_VLI_VALUE_UNKNOWN;
|
|
iter->uncompressed_size = LZMA_VLI_VALUE_UNKNOWN;
|
|
|
|
// Decide where to put the new Index Record.
|
|
if (iter_info->index.head == NULL)
|
|
iter_info->index.head = i;
|
|
|
|
if (iter_index != NULL)
|
|
iter_index->next = i;
|
|
|
|
iter->internal[ITER_INDEX] = i;
|
|
|
|
++iter_info->index.record_count;
|
|
++iter_info->index.incomplete_count;
|
|
|
|
return LZMA_OK;
|
|
}
|
|
|
|
|
|
extern LZMA_API lzma_ret
|
|
lzma_info_iter_set(lzma_info_iter *iter,
|
|
lzma_vli total_size, lzma_vli uncompressed_size)
|
|
{
|
|
// FIXME debug remove
|
|
lzma_info *info = iter_info;
|
|
(void)info;
|
|
|
|
if (iter_index == NULL || !lzma_vli_is_valid(total_size)
|
|
|| !lzma_vli_is_valid(uncompressed_size))
|
|
return LZMA_PROG_ERROR;
|
|
|
|
const bool was_incomplete = iter_index->total_size
|
|
== LZMA_VLI_VALUE_UNKNOWN
|
|
|| iter_index->uncompressed_size
|
|
== LZMA_VLI_VALUE_UNKNOWN;
|
|
|
|
if (total_size != LZMA_VLI_VALUE_UNKNOWN) {
|
|
if (iter_index->total_size == LZMA_VLI_VALUE_UNKNOWN) {
|
|
iter_index->total_size = total_size;
|
|
|
|
if (lzma_vli_add(iter_info->index.total_size,
|
|
total_size)
|
|
|| iter_info->index.total_size
|
|
> iter_info->known.total_size)
|
|
return LZMA_DATA_ERROR;
|
|
|
|
} else if (iter_index->total_size != total_size) {
|
|
return LZMA_DATA_ERROR;
|
|
}
|
|
}
|
|
|
|
if (uncompressed_size != LZMA_VLI_VALUE_UNKNOWN) {
|
|
if (iter_index->uncompressed_size == LZMA_VLI_VALUE_UNKNOWN) {
|
|
iter_index->uncompressed_size = uncompressed_size;
|
|
|
|
if (lzma_vli_add(iter_info->index.uncompressed_size,
|
|
uncompressed_size)
|
|
|| iter_info->index.uncompressed_size
|
|
> iter_info->known.uncompressed_size)
|
|
return LZMA_DATA_ERROR;
|
|
|
|
} else if (iter_index->uncompressed_size
|
|
!= uncompressed_size) {
|
|
return LZMA_DATA_ERROR;
|
|
}
|
|
}
|
|
|
|
// Check if the new information we got managed to finish this
|
|
// Index Record. If so, update the count of incomplete Index Records.
|
|
if (was_incomplete && iter_index->total_size
|
|
!= LZMA_VLI_VALUE_UNKNOWN
|
|
&& iter_index->uncompressed_size
|
|
!= LZMA_VLI_VALUE_UNKNOWN) {
|
|
assert(iter_info->index.incomplete_count > 0);
|
|
--iter_info->index.incomplete_count;
|
|
}
|
|
|
|
// Make sure that the known sizes are now available in *iter.
|
|
iter->total_size = iter_index->total_size;
|
|
iter->uncompressed_size = iter_index->uncompressed_size;
|
|
|
|
return LZMA_OK;
|
|
}
|
|
|
|
|
|
extern LZMA_API lzma_ret
|
|
lzma_info_index_finish(lzma_info *info)
|
|
{
|
|
if (info->index.record_count == 0 || info->index.incomplete_count > 0
|
|
|| lzma_info_size_set(info, LZMA_INFO_TOTAL,
|
|
info->index.total_size)
|
|
|| lzma_info_size_set(info, LZMA_INFO_UNCOMPRESSED,
|
|
info->index.uncompressed_size))
|
|
return LZMA_DATA_ERROR;
|
|
|
|
info->index.is_final = true;
|
|
|
|
return LZMA_OK;
|
|
}
|
|
|
|
|
|
//////////////
|
|
// Locating //
|
|
//////////////
|
|
|
|
extern LZMA_API lzma_vli
|
|
lzma_info_metadata_locate(const lzma_info *info, lzma_bool is_header_metadata)
|
|
{
|
|
bool error = false;
|
|
lzma_vli size = 0;
|
|
|
|
if (info->known.header_metadata_size == LZMA_VLI_VALUE_UNKNOWN) {
|
|
// We don't know if Header Metadata Block is present, thus
|
|
// we cannot locate it either.
|
|
//
|
|
// Well, you could say that just assume that it is present.
|
|
// I'm not sure if this is useful. But it can be useful to
|
|
// be able to use this function and get LZMA_VLI_VALUE_UNKNOWN
|
|
// to detect that Header Metadata Block wasn't present.
|
|
error = true;
|
|
} else if (is_header_metadata) {
|
|
error = lzma_vli_sum(size, info->stream_start_offset,
|
|
LZMA_STREAM_HEADER_SIZE);
|
|
} else if (!info->index.is_final) {
|
|
// Since we don't know if we have all the Index Records yet,
|
|
// we cannot know where the Footer Metadata Block is.
|
|
error = true;
|
|
} else {
|
|
error = lzma_vli_sum4(size, info->stream_start_offset,
|
|
LZMA_STREAM_HEADER_SIZE,
|
|
info->known.header_metadata_size,
|
|
info->known.total_size);
|
|
}
|
|
|
|
return error ? LZMA_VLI_VALUE_UNKNOWN : size;
|
|
}
|
|
|
|
|
|
extern LZMA_API uint32_t
|
|
lzma_info_metadata_alignment_get(
|
|
const lzma_info *info, lzma_bool is_header_metadata)
|
|
{
|
|
uint32_t alignment;
|
|
|
|
if (is_header_metadata) {
|
|
alignment = info->stream_start_offset
|
|
+ LZMA_STREAM_HEADER_SIZE;
|
|
} else {
|
|
alignment = info->stream_start_offset + LZMA_STREAM_HEADER_SIZE
|
|
+ info->known.header_metadata_size
|
|
+ info->known.total_size;
|
|
}
|
|
|
|
return alignment;
|
|
}
|
|
|
|
|
|
extern LZMA_API lzma_ret
|
|
lzma_info_iter_locate(lzma_info_iter *iter, lzma_allocator *allocator,
|
|
lzma_vli uncompressed_offset, lzma_bool allow_alloc)
|
|
{
|
|
if (iter == NULL || uncompressed_offset > LZMA_VLI_VALUE_MAX)
|
|
return LZMA_PROG_ERROR;
|
|
|
|
// Quick check in case Index is final.
|
|
if (iter_info->index.is_final) {
|
|
assert(iter_info->known.uncompressed_size
|
|
== iter_info->index.uncompressed_size);
|
|
if (uncompressed_offset >= iter_info->index.uncompressed_size)
|
|
return LZMA_DATA_ERROR;
|
|
}
|
|
|
|
// TODO: Optimize so that it uses existing info from *iter when
|
|
// seeking forward.
|
|
|
|
// Initialize *iter
|
|
if (iter_info->known.header_metadata_size != LZMA_VLI_VALUE_UNKNOWN) {
|
|
if (lzma_vli_sum3(iter->stream_offset,
|
|
iter_info->stream_start_offset,
|
|
LZMA_STREAM_HEADER_SIZE,
|
|
iter_info->known.header_metadata_size))
|
|
return LZMA_PROG_ERROR;
|
|
} else {
|
|
// We don't know the Size of Header Metadata Block, thus
|
|
// we cannot calculate the Stream offset either.
|
|
iter->stream_offset = LZMA_VLI_VALUE_UNKNOWN;
|
|
}
|
|
|
|
iter->uncompressed_offset = 0;
|
|
|
|
// If we have no Index Records, it's obvious that we need to
|
|
// add a new one.
|
|
if (iter_info->index.head == NULL) {
|
|
assert(!iter_info->index.is_final);
|
|
if (!allow_alloc)
|
|
return LZMA_DATA_ERROR;
|
|
|
|
return lzma_info_iter_next(iter, allocator);
|
|
}
|
|
|
|
// Locate an appropriate Index Record.
|
|
lzma_index *i = iter_info->index.head;
|
|
while (true) {
|
|
// - If Uncompressed Size in the Record is unknown,
|
|
// we have no chance to search further.
|
|
// - If the next Record would go past the requested offset,
|
|
// we have found our target Data Block.
|
|
if (i->uncompressed_size == LZMA_VLI_VALUE_UNKNOWN
|
|
|| iter->uncompressed_offset
|
|
+ i->uncompressed_size > uncompressed_offset) {
|
|
iter->total_size = i->total_size;
|
|
iter->uncompressed_size = i->uncompressed_size;
|
|
iter->internal[ITER_INDEX] = i;
|
|
return LZMA_OK;
|
|
}
|
|
|
|
// Update the stream offset. It may be unknown if we didn't
|
|
// know the size of Header Metadata Block.
|
|
if (iter->stream_offset != LZMA_VLI_VALUE_UNKNOWN)
|
|
if (lzma_vli_add(iter->stream_offset, i->total_size))
|
|
return LZMA_PROG_ERROR;
|
|
|
|
// Update the uncompressed offset. This cannot overflow since
|
|
// the Index is known to be valid.
|
|
iter->uncompressed_offset += i->uncompressed_size;
|
|
|
|
// Move to the next Block.
|
|
if (i->next == NULL) {
|
|
assert(!iter_info->index.is_final);
|
|
if (!allow_alloc)
|
|
return LZMA_DATA_ERROR;
|
|
|
|
iter->internal[ITER_INDEX] = i;
|
|
return lzma_info_iter_next(iter, allocator);
|
|
}
|
|
|
|
i = i->next;
|
|
}
|
|
}
|