Path: blob/master/Utilities/cmliblzma/liblzma/common/file_info.c
3153 views
// SPDX-License-Identifier: 0BSD12///////////////////////////////////////////////////////////////////////////////3//4/// \file file_info.c5/// \brief Decode .xz file information into a lzma_index structure6//7// Author: Lasse Collin8//9///////////////////////////////////////////////////////////////////////////////1011#include "index_decoder.h"121314typedef struct {15enum {16SEQ_MAGIC_BYTES,17SEQ_PADDING_SEEK,18SEQ_PADDING_DECODE,19SEQ_FOOTER,20SEQ_INDEX_INIT,21SEQ_INDEX_DECODE,22SEQ_HEADER_DECODE,23SEQ_HEADER_COMPARE,24} sequence;2526/// Absolute position of in[*in_pos] in the file. All code that27/// modifies *in_pos also updates this. seek_to_pos() needs this28/// to determine if we need to request the application to seek for29/// us or if we can do the seeking internally by adjusting *in_pos.30uint64_t file_cur_pos;3132/// This refers to absolute positions of interesting parts of the33/// input file. Sometimes it points to the *beginning* of a specific34/// field and sometimes to the *end* of a field. The current target35/// position at each moment is explained in the comments.36uint64_t file_target_pos;3738/// Size of the .xz file (from the application).39uint64_t file_size;4041/// Index decoder42lzma_next_coder index_decoder;4344/// Number of bytes remaining in the Index field that is currently45/// being decoded.46lzma_vli index_remaining;4748/// The Index decoder will store the decoded Index in this pointer.49lzma_index *this_index;5051/// Amount of Stream Padding in the current Stream.52lzma_vli stream_padding;5354/// The final combined index is collected here.55lzma_index *combined_index;5657/// Pointer from the application where to store the index information58/// after successful decoding.59lzma_index **dest_index;6061/// Pointer to lzma_stream.seek_pos to be used when returning62/// LZMA_SEEK_NEEDED. This is set by seek_to_pos() when needed.63uint64_t *external_seek_pos;6465/// Memory usage limit66uint64_t memlimit;6768/// Stream Flags from the very beginning of the file.69lzma_stream_flags first_header_flags;7071/// Stream Flags from Stream Header of the current Stream.72lzma_stream_flags header_flags;7374/// Stream Flags from Stream Footer of the current Stream.75lzma_stream_flags footer_flags;7677size_t temp_pos;78size_t temp_size;79uint8_t temp[8192];8081} lzma_file_info_coder;828384/// Copies data from in[*in_pos] into coder->temp until85/// coder->temp_pos == coder->temp_size. This also keeps coder->file_cur_pos86/// in sync with *in_pos. Returns true if more input is needed.87static bool88fill_temp(lzma_file_info_coder *coder, const uint8_t *restrict in,89size_t *restrict in_pos, size_t in_size)90{91coder->file_cur_pos += lzma_bufcpy(in, in_pos, in_size,92coder->temp, &coder->temp_pos, coder->temp_size);93return coder->temp_pos < coder->temp_size;94}959697/// Seeks to the absolute file position specified by target_pos.98/// This tries to do the seeking by only modifying *in_pos, if possible.99/// The main benefit of this is that if one passes the whole file at once100/// to lzma_code(), the decoder will never need to return LZMA_SEEK_NEEDED101/// as all the seeking can be done by adjusting *in_pos in this function.102///103/// Returns true if an external seek is needed and the caller must return104/// LZMA_SEEK_NEEDED.105static bool106seek_to_pos(lzma_file_info_coder *coder, uint64_t target_pos,107size_t in_start, size_t *in_pos, size_t in_size)108{109// The input buffer doesn't extend beyond the end of the file.110// This has been checked by file_info_decode() already.111assert(coder->file_size - coder->file_cur_pos >= in_size - *in_pos);112113const uint64_t pos_min = coder->file_cur_pos - (*in_pos - in_start);114const uint64_t pos_max = coder->file_cur_pos + (in_size - *in_pos);115116bool external_seek_needed;117118if (target_pos >= pos_min && target_pos <= pos_max) {119// The requested position is available in the current input120// buffer or right after it. That is, in a corner case we121// end up setting *in_pos == in_size and thus will immediately122// need new input bytes from the application.123*in_pos += (size_t)(target_pos - coder->file_cur_pos);124external_seek_needed = false;125} else {126// Ask the application to seek the input file.127*coder->external_seek_pos = target_pos;128external_seek_needed = true;129130// Mark the whole input buffer as used. This way131// lzma_stream.total_in will have a better estimate132// of the amount of data read. It still won't be perfect133// as the value will depend on the input buffer size that134// the application uses, but it should be good enough for135// those few who want an estimate.136*in_pos = in_size;137}138139// After seeking (internal or external) the current position140// will match the requested target position.141coder->file_cur_pos = target_pos;142143return external_seek_needed;144}145146147/// The caller sets coder->file_target_pos so that it points to the *end*148/// of the desired file position. This function then determines how far149/// backwards from that position we can seek. After seeking fill_temp()150/// can be used to read data into coder->temp. When fill_temp() has finished,151/// coder->temp[coder->temp_size] will match coder->file_target_pos.152///153/// This also validates that coder->target_file_pos is sane in sense that154/// we aren't trying to seek too far backwards (too close or beyond the155/// beginning of the file).156static lzma_ret157reverse_seek(lzma_file_info_coder *coder,158size_t in_start, size_t *in_pos, size_t in_size)159{160// Check that there is enough data before the target position161// to contain at least Stream Header and Stream Footer. If there162// isn't, the file cannot be valid.163if (coder->file_target_pos < 2 * LZMA_STREAM_HEADER_SIZE)164return LZMA_DATA_ERROR;165166coder->temp_pos = 0;167168// The Stream Header at the very beginning of the file gets handled169// specially in SEQ_MAGIC_BYTES and thus we will never need to seek170// there. By not seeking to the first LZMA_STREAM_HEADER_SIZE bytes171// we avoid a useless external seek after SEQ_MAGIC_BYTES if the172// application uses an extremely small input buffer and the input173// file is very small.174if (coder->file_target_pos - LZMA_STREAM_HEADER_SIZE175< sizeof(coder->temp))176coder->temp_size = (size_t)(coder->file_target_pos177- LZMA_STREAM_HEADER_SIZE);178else179coder->temp_size = sizeof(coder->temp);180181// The above if-statements guarantee this. This is important because182// the Stream Header/Footer decoders assume that there's at least183// LZMA_STREAM_HEADER_SIZE bytes in coder->temp.184assert(coder->temp_size >= LZMA_STREAM_HEADER_SIZE);185186if (seek_to_pos(coder, coder->file_target_pos - coder->temp_size,187in_start, in_pos, in_size))188return LZMA_SEEK_NEEDED;189190return LZMA_OK;191}192193194/// Gets the number of zero-bytes at the end of the buffer.195static size_t196get_padding_size(const uint8_t *buf, size_t buf_size)197{198size_t padding = 0;199while (buf_size > 0 && buf[--buf_size] == 0x00)200++padding;201202return padding;203}204205206/// With the Stream Header at the very beginning of the file, LZMA_FORMAT_ERROR207/// is used to tell the application that Magic Bytes didn't match. In other208/// Stream Header/Footer fields (in the middle/end of the file) it could be209/// a bit confusing to return LZMA_FORMAT_ERROR as we already know that there210/// is a valid Stream Header at the beginning of the file. For those cases211/// this function is used to convert LZMA_FORMAT_ERROR to LZMA_DATA_ERROR.212static lzma_ret213hide_format_error(lzma_ret ret)214{215if (ret == LZMA_FORMAT_ERROR)216ret = LZMA_DATA_ERROR;217218return ret;219}220221222/// Calls the Index decoder and updates coder->index_remaining.223/// This is a separate function because the input can be either directly224/// from the application or from coder->temp.225static lzma_ret226decode_index(lzma_file_info_coder *coder, const lzma_allocator *allocator,227const uint8_t *restrict in, size_t *restrict in_pos,228size_t in_size, bool update_file_cur_pos)229{230const size_t in_start = *in_pos;231232const lzma_ret ret = coder->index_decoder.code(233coder->index_decoder.coder,234allocator, in, in_pos, in_size,235NULL, NULL, 0, LZMA_RUN);236237coder->index_remaining -= *in_pos - in_start;238239if (update_file_cur_pos)240coder->file_cur_pos += *in_pos - in_start;241242return ret;243}244245246static lzma_ret247file_info_decode(void *coder_ptr, const lzma_allocator *allocator,248const uint8_t *restrict in, size_t *restrict in_pos,249size_t in_size,250uint8_t *restrict out lzma_attribute((__unused__)),251size_t *restrict out_pos lzma_attribute((__unused__)),252size_t out_size lzma_attribute((__unused__)),253lzma_action action lzma_attribute((__unused__)))254{255lzma_file_info_coder *coder = coder_ptr;256const size_t in_start = *in_pos;257258// If the caller provides input past the end of the file, trim259// the extra bytes from the buffer so that we won't read too far.260assert(coder->file_size >= coder->file_cur_pos);261if (coder->file_size - coder->file_cur_pos < in_size - in_start)262in_size = in_start263+ (size_t)(coder->file_size - coder->file_cur_pos);264265while (true)266switch (coder->sequence) {267case SEQ_MAGIC_BYTES:268// Decode the Stream Header at the beginning of the file269// first to check if the Magic Bytes match. The flags270// are stored in coder->first_header_flags so that we271// don't need to seek to it again.272//273// Check that the file is big enough to contain at least274// Stream Header.275if (coder->file_size < LZMA_STREAM_HEADER_SIZE)276return LZMA_FORMAT_ERROR;277278// Read the Stream Header field into coder->temp.279if (fill_temp(coder, in, in_pos, in_size))280return LZMA_OK;281282// This is the only Stream Header/Footer decoding where we283// want to return LZMA_FORMAT_ERROR if the Magic Bytes don't284// match. Elsewhere it will be converted to LZMA_DATA_ERROR.285return_if_error(lzma_stream_header_decode(286&coder->first_header_flags, coder->temp));287288// Now that we know that the Magic Bytes match, check the289// file size. It's better to do this here after checking the290// Magic Bytes since this way we can give LZMA_FORMAT_ERROR291// instead of LZMA_DATA_ERROR when the Magic Bytes don't292// match in a file that is too big or isn't a multiple of293// four bytes.294if (coder->file_size > LZMA_VLI_MAX || (coder->file_size & 3))295return LZMA_DATA_ERROR;296297// Start looking for Stream Padding and Stream Footer298// at the end of the file.299coder->file_target_pos = coder->file_size;300301// Fall through302303case SEQ_PADDING_SEEK:304coder->sequence = SEQ_PADDING_DECODE;305return_if_error(reverse_seek(306coder, in_start, in_pos, in_size));307308// Fall through309310case SEQ_PADDING_DECODE: {311// Copy to coder->temp first. This keeps the code simpler if312// the application only provides input a few bytes at a time.313if (fill_temp(coder, in, in_pos, in_size))314return LZMA_OK;315316// Scan the buffer backwards to get the size of the317// Stream Padding field (if any).318const size_t new_padding = get_padding_size(319coder->temp, coder->temp_size);320coder->stream_padding += new_padding;321322// Set the target position to the beginning of Stream Padding323// that has been observed so far. If all Stream Padding has324// been seen, then the target position will be at the end325// of the Stream Footer field.326coder->file_target_pos -= new_padding;327328if (new_padding == coder->temp_size) {329// The whole buffer was padding. Seek backwards in330// the file to get more input.331coder->sequence = SEQ_PADDING_SEEK;332break;333}334335// Size of Stream Padding must be a multiple of 4 bytes.336if (coder->stream_padding & 3)337return LZMA_DATA_ERROR;338339coder->sequence = SEQ_FOOTER;340341// Calculate the amount of non-padding data in coder->temp.342coder->temp_size -= new_padding;343coder->temp_pos = coder->temp_size;344345// We can avoid an external seek if the whole Stream Footer346// is already in coder->temp. In that case SEQ_FOOTER won't347// read more input and will find the Stream Footer from348// coder->temp[coder->temp_size - LZMA_STREAM_HEADER_SIZE].349//350// Otherwise we will need to seek. The seeking is done so351// that Stream Footer will be at the end of coder->temp.352// This way it's likely that we also get a complete Index353// field into coder->temp without needing a separate seek354// for that (unless the Index field is big).355if (coder->temp_size < LZMA_STREAM_HEADER_SIZE)356return_if_error(reverse_seek(357coder, in_start, in_pos, in_size));358}359360// Fall through361362case SEQ_FOOTER:363// Copy the Stream Footer field into coder->temp.364// If Stream Footer was already available in coder->temp365// in SEQ_PADDING_DECODE, then this does nothing.366if (fill_temp(coder, in, in_pos, in_size))367return LZMA_OK;368369// Make coder->file_target_pos and coder->temp_size point370// to the beginning of Stream Footer and thus to the end371// of the Index field. coder->temp_pos will be updated372// a bit later.373coder->file_target_pos -= LZMA_STREAM_HEADER_SIZE;374coder->temp_size -= LZMA_STREAM_HEADER_SIZE;375376// Decode Stream Footer.377return_if_error(hide_format_error(lzma_stream_footer_decode(378&coder->footer_flags,379coder->temp + coder->temp_size)));380381// Check that we won't seek past the beginning of the file.382//383// LZMA_STREAM_HEADER_SIZE is added because there must be384// space for Stream Header too even though we won't seek385// there before decoding the Index field.386//387// There's no risk of integer overflow here because388// Backward Size cannot be greater than 2^34.389if (coder->file_target_pos < coder->footer_flags.backward_size390+ LZMA_STREAM_HEADER_SIZE)391return LZMA_DATA_ERROR;392393// Set the target position to the beginning of the Index field.394coder->file_target_pos -= coder->footer_flags.backward_size;395coder->sequence = SEQ_INDEX_INIT;396397// We can avoid an external seek if the whole Index field is398// already available in coder->temp.399if (coder->temp_size >= coder->footer_flags.backward_size) {400// Set coder->temp_pos to point to the beginning401// of the Index.402coder->temp_pos = coder->temp_size403- coder->footer_flags.backward_size;404} else {405// These are set to zero to indicate that there's no406// useful data (Index or anything else) in coder->temp.407coder->temp_pos = 0;408coder->temp_size = 0;409410// Seek to the beginning of the Index field.411if (seek_to_pos(coder, coder->file_target_pos,412in_start, in_pos, in_size))413return LZMA_SEEK_NEEDED;414}415416// Fall through417418case SEQ_INDEX_INIT: {419// Calculate the amount of memory already used by the earlier420// Indexes so that we know how big memory limit to pass to421// the Index decoder.422//423// NOTE: When there are multiple Streams, the separate424// lzma_index structures can use more RAM (as measured by425// lzma_index_memused()) than the final combined lzma_index.426// Thus memlimit may need to be slightly higher than the final427// calculated memory usage will be. This is perhaps a bit428// confusing to the application, but I think it shouldn't429// cause problems in practice.430uint64_t memused = 0;431if (coder->combined_index != NULL) {432memused = lzma_index_memused(coder->combined_index);433assert(memused <= coder->memlimit);434if (memused > coder->memlimit) // Extra sanity check435return LZMA_PROG_ERROR;436}437438// Initialize the Index decoder.439return_if_error(lzma_index_decoder_init(440&coder->index_decoder, allocator,441&coder->this_index,442coder->memlimit - memused));443444coder->index_remaining = coder->footer_flags.backward_size;445coder->sequence = SEQ_INDEX_DECODE;446}447448// Fall through449450case SEQ_INDEX_DECODE: {451// Decode (a part of) the Index. If the whole Index is already452// in coder->temp, read it from there. Otherwise read from453// in[*in_pos] onwards. Note that index_decode() updates454// coder->index_remaining and optionally coder->file_cur_pos.455lzma_ret ret;456if (coder->temp_size != 0) {457assert(coder->temp_size - coder->temp_pos458== coder->index_remaining);459ret = decode_index(coder, allocator, coder->temp,460&coder->temp_pos, coder->temp_size,461false);462} else {463// Don't give the decoder more input than the known464// remaining size of the Index field.465size_t in_stop = in_size;466if (in_size - *in_pos > coder->index_remaining)467in_stop = *in_pos468+ (size_t)(coder->index_remaining);469470ret = decode_index(coder, allocator,471in, in_pos, in_stop, true);472}473474switch (ret) {475case LZMA_OK:476// If the Index docoder asks for more input when we477// have already given it as much input as Backward Size478// indicated, the file is invalid.479if (coder->index_remaining == 0)480return LZMA_DATA_ERROR;481482// We cannot get here if we were reading Index from483// coder->temp because when reading from coder->temp484// we give the Index decoder exactly485// coder->index_remaining bytes of input.486assert(coder->temp_size == 0);487488return LZMA_OK;489490case LZMA_STREAM_END:491// If the decoding seems to be successful, check also492// that the Index decoder consumed as much input as493// indicated by the Backward Size field.494if (coder->index_remaining != 0)495return LZMA_DATA_ERROR;496497break;498499default:500return ret;501}502503// Calculate how much the Index tells us to seek backwards504// (relative to the beginning of the Index): Total size of505// all Blocks plus the size of the Stream Header field.506// No integer overflow here because lzma_index_total_size()507// cannot return a value greater than LZMA_VLI_MAX.508const uint64_t seek_amount509= lzma_index_total_size(coder->this_index)510+ LZMA_STREAM_HEADER_SIZE;511512// Check that Index is sane in sense that seek_amount won't513// make us seek past the beginning of the file when locating514// the Stream Header.515//516// coder->file_target_pos still points to the beginning of517// the Index field.518if (coder->file_target_pos < seek_amount)519return LZMA_DATA_ERROR;520521// Set the target to the beginning of Stream Header.522coder->file_target_pos -= seek_amount;523524if (coder->file_target_pos == 0) {525// We would seek to the beginning of the file, but526// since we already decoded that Stream Header in527// SEQ_MAGIC_BYTES, we can use the cached value from528// coder->first_header_flags to avoid the seek.529coder->header_flags = coder->first_header_flags;530coder->sequence = SEQ_HEADER_COMPARE;531break;532}533534coder->sequence = SEQ_HEADER_DECODE;535536// Make coder->file_target_pos point to the end of537// the Stream Header field.538coder->file_target_pos += LZMA_STREAM_HEADER_SIZE;539540// If coder->temp_size is non-zero, it points to the end541// of the Index field. Then the beginning of the Index542// field is at coder->temp[coder->temp_size543// - coder->footer_flags.backward_size].544assert(coder->temp_size == 0 || coder->temp_size545>= coder->footer_flags.backward_size);546547// If coder->temp contained the whole Index, see if it has548// enough data to contain also the Stream Header. If so,549// we avoid an external seek.550//551// NOTE: This can happen only with small .xz files and only552// for the non-first Stream as the Stream Flags of the first553// Stream are cached and already handled a few lines above.554// So this isn't as useful as the other seek-avoidance cases.555if (coder->temp_size != 0 && coder->temp_size556- coder->footer_flags.backward_size557>= seek_amount) {558// Make temp_pos and temp_size point to the *end* of559// Stream Header so that SEQ_HEADER_DECODE will find560// the start of Stream Header from coder->temp[561// coder->temp_size - LZMA_STREAM_HEADER_SIZE].562coder->temp_pos = coder->temp_size563- coder->footer_flags.backward_size564- seek_amount565+ LZMA_STREAM_HEADER_SIZE;566coder->temp_size = coder->temp_pos;567} else {568// Seek so that Stream Header will be at the end of569// coder->temp. With typical multi-Stream files we570// will usually also get the Stream Footer and Index571// of the *previous* Stream in coder->temp and thus572// won't need a separate seek for them.573return_if_error(reverse_seek(coder,574in_start, in_pos, in_size));575}576}577578// Fall through579580case SEQ_HEADER_DECODE:581// Copy the Stream Header field into coder->temp.582// If Stream Header was already available in coder->temp583// in SEQ_INDEX_DECODE, then this does nothing.584if (fill_temp(coder, in, in_pos, in_size))585return LZMA_OK;586587// Make all these point to the beginning of Stream Header.588coder->file_target_pos -= LZMA_STREAM_HEADER_SIZE;589coder->temp_size -= LZMA_STREAM_HEADER_SIZE;590coder->temp_pos = coder->temp_size;591592// Decode the Stream Header.593return_if_error(hide_format_error(lzma_stream_header_decode(594&coder->header_flags,595coder->temp + coder->temp_size)));596597coder->sequence = SEQ_HEADER_COMPARE;598599// Fall through600601case SEQ_HEADER_COMPARE:602// Compare Stream Header against Stream Footer. They must603// match.604return_if_error(lzma_stream_flags_compare(605&coder->header_flags, &coder->footer_flags));606607// Store the decoded Stream Flags into the Index. Use the608// Footer Flags because it contains Backward Size, although609// it shouldn't matter in practice.610if (lzma_index_stream_flags(coder->this_index,611&coder->footer_flags) != LZMA_OK)612return LZMA_PROG_ERROR;613614// Store also the size of the Stream Padding field. It is615// needed to calculate the offsets of the Streams correctly.616if (lzma_index_stream_padding(coder->this_index,617coder->stream_padding) != LZMA_OK)618return LZMA_PROG_ERROR;619620// Reset it so that it's ready for the next Stream.621coder->stream_padding = 0;622623// Append the earlier decoded Indexes after this_index.624if (coder->combined_index != NULL)625return_if_error(lzma_index_cat(coder->this_index,626coder->combined_index, allocator));627628coder->combined_index = coder->this_index;629coder->this_index = NULL;630631// If the whole file was decoded, tell the caller that we632// are finished.633if (coder->file_target_pos == 0) {634// The combined index must indicate the same file635// size as was told to us at initialization.636assert(lzma_index_file_size(coder->combined_index)637== coder->file_size);638639// Make the combined index available to640// the application.641*coder->dest_index = coder->combined_index;642coder->combined_index = NULL;643644// Mark the input buffer as used since we may have645// done internal seeking and thus don't know how646// many input bytes were actually used. This way647// lzma_stream.total_in gets a slightly better648// estimate of the amount of input used.649*in_pos = in_size;650return LZMA_STREAM_END;651}652653// We didn't hit the beginning of the file yet, so continue654// reading backwards in the file. If we have unprocessed655// data in coder->temp, use it before requesting more data656// from the application.657//658// coder->file_target_pos, coder->temp_size, and659// coder->temp_pos all point to the beginning of Stream Header660// and thus the end of the previous Stream in the file.661coder->sequence = coder->temp_size > 0662? SEQ_PADDING_DECODE : SEQ_PADDING_SEEK;663break;664665default:666assert(0);667return LZMA_PROG_ERROR;668}669}670671672static lzma_ret673file_info_decoder_memconfig(void *coder_ptr, uint64_t *memusage,674uint64_t *old_memlimit, uint64_t new_memlimit)675{676lzma_file_info_coder *coder = coder_ptr;677678// The memory usage calculation comes from three things:679//680// (1) The Indexes that have already been decoded and processed into681// coder->combined_index.682//683// (2) The latest Index in coder->this_index that has been decoded but684// not yet put into coder->combined_index.685//686// (3) The latest Index that we have started decoding but haven't687// finished and thus isn't available in coder->this_index yet.688// Memory usage and limit information needs to be communicated689// from/to coder->index_decoder.690//691// Care has to be taken to not do both (2) and (3) when calculating692// the memory usage.693uint64_t combined_index_memusage = 0;694uint64_t this_index_memusage = 0;695696// (1) If we have already successfully decoded one or more Indexes,697// get their memory usage.698if (coder->combined_index != NULL)699combined_index_memusage = lzma_index_memused(700coder->combined_index);701702// Choose between (2), (3), or neither.703if (coder->this_index != NULL) {704// (2) The latest Index is available. Use its memory usage.705this_index_memusage = lzma_index_memused(coder->this_index);706707} else if (coder->sequence == SEQ_INDEX_DECODE) {708// (3) The Index decoder is activate and hasn't yet stored709// the new index in coder->this_index. Get the memory usage710// information from the Index decoder.711//712// NOTE: If the Index decoder doesn't yet know how much memory713// it will eventually need, it will return a tiny value here.714uint64_t dummy;715if (coder->index_decoder.memconfig(coder->index_decoder.coder,716&this_index_memusage, &dummy, 0)717!= LZMA_OK) {718assert(0);719return LZMA_PROG_ERROR;720}721}722723// Now we know the total memory usage/requirement. If we had neither724// old Indexes nor a new Index, this will be zero which isn't725// acceptable as lzma_memusage() has to return non-zero on success726// and even with an empty .xz file we will end up with a lzma_index727// that takes some memory.728*memusage = combined_index_memusage + this_index_memusage;729if (*memusage == 0)730*memusage = lzma_index_memusage(1, 0);731732*old_memlimit = coder->memlimit;733734// If requested, set a new memory usage limit.735if (new_memlimit != 0) {736if (new_memlimit < *memusage)737return LZMA_MEMLIMIT_ERROR;738739// In the condition (3) we need to tell the Index decoder740// its new memory usage limit.741if (coder->this_index == NULL742&& coder->sequence == SEQ_INDEX_DECODE) {743const uint64_t idec_new_memlimit = new_memlimit744- combined_index_memusage;745746assert(this_index_memusage > 0);747assert(idec_new_memlimit > 0);748749uint64_t dummy1;750uint64_t dummy2;751752if (coder->index_decoder.memconfig(753coder->index_decoder.coder,754&dummy1, &dummy2, idec_new_memlimit)755!= LZMA_OK) {756assert(0);757return LZMA_PROG_ERROR;758}759}760761coder->memlimit = new_memlimit;762}763764return LZMA_OK;765}766767768static void769file_info_decoder_end(void *coder_ptr, const lzma_allocator *allocator)770{771lzma_file_info_coder *coder = coder_ptr;772773lzma_next_end(&coder->index_decoder, allocator);774lzma_index_end(coder->this_index, allocator);775lzma_index_end(coder->combined_index, allocator);776777lzma_free(coder, allocator);778return;779}780781782static lzma_ret783lzma_file_info_decoder_init(lzma_next_coder *next,784const lzma_allocator *allocator, uint64_t *seek_pos,785lzma_index **dest_index,786uint64_t memlimit, uint64_t file_size)787{788lzma_next_coder_init(&lzma_file_info_decoder_init, next, allocator);789790if (dest_index == NULL)791return LZMA_PROG_ERROR;792793lzma_file_info_coder *coder = next->coder;794if (coder == NULL) {795coder = lzma_alloc(sizeof(lzma_file_info_coder), allocator);796if (coder == NULL)797return LZMA_MEM_ERROR;798799next->coder = coder;800next->code = &file_info_decode;801next->end = &file_info_decoder_end;802next->memconfig = &file_info_decoder_memconfig;803804coder->index_decoder = LZMA_NEXT_CODER_INIT;805coder->this_index = NULL;806coder->combined_index = NULL;807}808809coder->sequence = SEQ_MAGIC_BYTES;810coder->file_cur_pos = 0;811coder->file_target_pos = 0;812coder->file_size = file_size;813814lzma_index_end(coder->this_index, allocator);815coder->this_index = NULL;816817lzma_index_end(coder->combined_index, allocator);818coder->combined_index = NULL;819820coder->stream_padding = 0;821822coder->dest_index = dest_index;823coder->external_seek_pos = seek_pos;824825// If memlimit is 0, make it 1 to ensure that lzma_memlimit_get()826// won't return 0 (which would indicate an error).827coder->memlimit = my_max(1, memlimit);828829// Prepare these for reading the first Stream Header into coder->temp.830coder->temp_pos = 0;831coder->temp_size = LZMA_STREAM_HEADER_SIZE;832833return LZMA_OK;834}835836837extern LZMA_API(lzma_ret)838lzma_file_info_decoder(lzma_stream *strm, lzma_index **dest_index,839uint64_t memlimit, uint64_t file_size)840{841lzma_next_strm_init(lzma_file_info_decoder_init, strm, &strm->seek_pos,842dest_index, memlimit, file_size);843844// We allow LZMA_FINISH in addition to LZMA_RUN for convenience.845// lzma_code() is able to handle the LZMA_FINISH + LZMA_SEEK_NEEDED846// combination in a sane way. Applications still need to be careful847// if they use LZMA_FINISH so that they remember to reset it back848// to LZMA_RUN after seeking if needed.849strm->internal->supported_actions[LZMA_RUN] = true;850strm->internal->supported_actions[LZMA_FINISH] = true;851852return LZMA_OK;853}854855856