Path: blob/master/Utilities/cmliblzma/liblzma/common/lzip_decoder.c
3153 views
// SPDX-License-Identifier: 0BSD12///////////////////////////////////////////////////////////////////////////////3//4/// \file lzip_decoder.c5/// \brief Decodes .lz (lzip) files6//7// Author: Michał Górny8// Lasse Collin9//10///////////////////////////////////////////////////////////////////////////////1112#include "lzip_decoder.h"13#include "lzma_decoder.h"14#include "check.h"151617// .lz format version 0 lacks the 64-bit Member size field in the footer.18#define LZIP_V0_FOOTER_SIZE 1219#define LZIP_V1_FOOTER_SIZE 2020#define LZIP_FOOTER_SIZE_MAX LZIP_V1_FOOTER_SIZE2122// lc/lp/pb are hardcoded in the .lz format.23#define LZIP_LC 324#define LZIP_LP 025#define LZIP_PB 2262728typedef struct {29enum {30SEQ_ID_STRING,31SEQ_VERSION,32SEQ_DICT_SIZE,33SEQ_CODER_INIT,34SEQ_LZMA_STREAM,35SEQ_MEMBER_FOOTER,36} sequence;3738/// .lz member format version39uint32_t version;4041/// CRC32 of the uncompressed data in the .lz member42uint32_t crc32;4344/// Uncompressed size of the .lz member45uint64_t uncompressed_size;4647/// Compressed size of the .lz member48uint64_t member_size;4950/// Memory usage limit51uint64_t memlimit;5253/// Amount of memory actually needed54uint64_t memusage;5556/// If true, LZMA_GET_CHECK is returned after decoding the header57/// fields. As all files use CRC32 this is redundant but it's58/// implemented anyway since the initialization functions supports59/// all other flags in addition to LZMA_TELL_ANY_CHECK.60bool tell_any_check;6162/// If true, we won't calculate or verify the CRC32 of63/// the uncompressed data.64bool ignore_check;6566/// If true, we will decode concatenated .lz members and stop if67/// non-.lz data is seen after at least one member has been68/// successfully decoded.69bool concatenated;7071/// When decoding concatenated .lz members, this is true as long as72/// we are decoding the first .lz member. This is needed to avoid73/// incorrect LZMA_FORMAT_ERROR in case there is non-.lz data at74/// the end of the file.75bool first_member;7677/// Reading position in the header and footer fields78size_t pos;7980/// Buffer to hold the .lz footer fields81uint8_t buffer[LZIP_FOOTER_SIZE_MAX];8283/// Options decoded from the .lz header that needed to initialize84/// the LZMA1 decoder.85lzma_options_lzma options;8687/// LZMA1 decoder88lzma_next_coder lzma_decoder;8990} lzma_lzip_coder;919293static lzma_ret94lzip_decode(void *coder_ptr, const lzma_allocator *allocator,95const uint8_t *restrict in, size_t *restrict in_pos,96size_t in_size, uint8_t *restrict out,97size_t *restrict out_pos, size_t out_size, lzma_action action)98{99lzma_lzip_coder *coder = coder_ptr;100101while (true)102switch (coder->sequence) {103case SEQ_ID_STRING: {104// The "ID string" or magic bytes are "LZIP" in US-ASCII.105const uint8_t lzip_id_string[4] = { 0x4C, 0x5A, 0x49, 0x50 };106107while (coder->pos < sizeof(lzip_id_string)) {108if (*in_pos >= in_size) {109// If we are on the 2nd+ concatenated member110// and the input ends before we can read111// the magic bytes, we discard the bytes that112// were already read (up to 3) and finish.113// See the reasoning below.114return !coder->first_member115&& action == LZMA_FINISH116? LZMA_STREAM_END : LZMA_OK;117}118119if (in[*in_pos] != lzip_id_string[coder->pos]) {120// The .lz format allows putting non-.lz data121// at the end of the file. If we have seen122// at least one valid .lz member already,123// then we won't consume the byte at *in_pos124// and will return LZMA_STREAM_END. This way125// apps can easily locate and read the non-.lz126// data after the .lz member(s).127//128// NOTE: If the first 1-3 bytes of the non-.lz129// data match the .lz ID string then the first130// 1-3 bytes of the junk will get ignored by131// us. If apps want to properly locate the132// trailing data they must ensure that the133// first byte of their custom data isn't the134// same as the first byte of .lz ID string.135// With the liblzma API we cannot rewind the136// input position across calls to lzma_code().137return !coder->first_member138? LZMA_STREAM_END : LZMA_FORMAT_ERROR;139}140141++*in_pos;142++coder->pos;143}144145coder->pos = 0;146147coder->crc32 = 0;148coder->uncompressed_size = 0;149coder->member_size = sizeof(lzip_id_string);150151coder->sequence = SEQ_VERSION;152}153154// Fall through155156case SEQ_VERSION:157if (*in_pos >= in_size)158return LZMA_OK;159160coder->version = in[(*in_pos)++];161162// We support version 0 and unextended version 1.163if (coder->version > 1)164return LZMA_OPTIONS_ERROR;165166++coder->member_size;167coder->sequence = SEQ_DICT_SIZE;168169// .lz versions 0 and 1 use CRC32 as the integrity check170// so if the application wanted to know that171// (LZMA_TELL_ANY_CHECK) we can tell it now.172if (coder->tell_any_check)173return LZMA_GET_CHECK;174175// Fall through176177case SEQ_DICT_SIZE: {178if (*in_pos >= in_size)179return LZMA_OK;180181const uint32_t ds = in[(*in_pos)++];182++coder->member_size;183184// The five lowest bits are for the base-2 logarithm of185// the dictionary size and the highest three bits are186// the fractional part (0/16 to 7/16) that will be187// subtracted to get the final value.188//189// For example, with 0xB5:190// b2log = 21191// fracnum = 5192// dict_size = 2^21 - 2^21 * 5 / 16 = 1408 KiB193const uint32_t b2log = ds & 0x1F;194const uint32_t fracnum = ds >> 5;195196// The format versions 0 and 1 allow dictionary size in the197// range [4 KiB, 512 MiB].198if (b2log < 12 || b2log > 29 || (b2log == 12 && fracnum > 0))199return LZMA_DATA_ERROR;200201// 2^[b2log] - 2^[b2log] * [fracnum] / 16202// = 2^[b2log] - [fracnum] * 2^([b2log] - 4)203coder->options.dict_size = (UINT32_C(1) << b2log)204- (fracnum << (b2log - 4));205206assert(coder->options.dict_size >= 4096);207assert(coder->options.dict_size <= (UINT32_C(512) << 20));208209coder->options.preset_dict = NULL;210coder->options.lc = LZIP_LC;211coder->options.lp = LZIP_LP;212coder->options.pb = LZIP_PB;213214// Calculate the memory usage.215coder->memusage = lzma_lzma_decoder_memusage(&coder->options)216+ LZMA_MEMUSAGE_BASE;217218// Initialization is a separate step because if we return219// LZMA_MEMLIMIT_ERROR we need to be able to restart after220// the memlimit has been increased.221coder->sequence = SEQ_CODER_INIT;222}223224// Fall through225226case SEQ_CODER_INIT: {227if (coder->memusage > coder->memlimit)228return LZMA_MEMLIMIT_ERROR;229230const lzma_filter_info filters[2] = {231{232.id = LZMA_FILTER_LZMA1,233.init = &lzma_lzma_decoder_init,234.options = &coder->options,235}, {236.init = NULL,237}238};239240return_if_error(lzma_next_filter_init(&coder->lzma_decoder,241allocator, filters));242243coder->crc32 = 0;244coder->sequence = SEQ_LZMA_STREAM;245}246247// Fall through248249case SEQ_LZMA_STREAM: {250const size_t in_start = *in_pos;251const size_t out_start = *out_pos;252253const lzma_ret ret = coder->lzma_decoder.code(254coder->lzma_decoder.coder, allocator,255in, in_pos, in_size, out, out_pos, out_size,256action);257258const size_t out_used = *out_pos - out_start;259260coder->member_size += *in_pos - in_start;261coder->uncompressed_size += out_used;262263// Don't update the CRC32 if the integrity check will be264// ignored or if there was no new output. The latter is265// important in case out == NULL to avoid null pointer + 0266// which is undefined behavior.267if (!coder->ignore_check && out_used > 0)268coder->crc32 = lzma_crc32(out + out_start, out_used,269coder->crc32);270271if (ret != LZMA_STREAM_END)272return ret;273274coder->sequence = SEQ_MEMBER_FOOTER;275}276277// Fall through278279case SEQ_MEMBER_FOOTER: {280// The footer of .lz version 0 lacks the Member size field.281// This is the only difference between version 0 and282// unextended version 1 formats.283const size_t footer_size = coder->version == 0284? LZIP_V0_FOOTER_SIZE285: LZIP_V1_FOOTER_SIZE;286287// Copy the CRC32, Data size, and Member size fields to288// the internal buffer.289lzma_bufcpy(in, in_pos, in_size, coder->buffer, &coder->pos,290footer_size);291292// Return if we didn't get the whole footer yet.293if (coder->pos < footer_size)294return LZMA_OK;295296coder->pos = 0;297coder->member_size += footer_size;298299// Check that the footer fields match the observed data.300if (!coder->ignore_check301&& coder->crc32 != read32le(&coder->buffer[0]))302return LZMA_DATA_ERROR;303304if (coder->uncompressed_size != read64le(&coder->buffer[4]))305return LZMA_DATA_ERROR;306307if (coder->version > 0) {308// .lz version 0 has no Member size field.309if (coder->member_size != read64le(&coder->buffer[12]))310return LZMA_DATA_ERROR;311}312313// Decoding is finished if we weren't requested to decode314// more than one .lz member.315if (!coder->concatenated)316return LZMA_STREAM_END;317318coder->first_member = false;319coder->sequence = SEQ_ID_STRING;320break;321}322323default:324assert(0);325return LZMA_PROG_ERROR;326}327328// Never reached329}330331332static void333lzip_decoder_end(void *coder_ptr, const lzma_allocator *allocator)334{335lzma_lzip_coder *coder = coder_ptr;336lzma_next_end(&coder->lzma_decoder, allocator);337lzma_free(coder, allocator);338return;339}340341342static lzma_check343lzip_decoder_get_check(const void *coder_ptr lzma_attribute((__unused__)))344{345return LZMA_CHECK_CRC32;346}347348349static lzma_ret350lzip_decoder_memconfig(void *coder_ptr, uint64_t *memusage,351uint64_t *old_memlimit, uint64_t new_memlimit)352{353lzma_lzip_coder *coder = coder_ptr;354355*memusage = coder->memusage;356*old_memlimit = coder->memlimit;357358if (new_memlimit != 0) {359if (new_memlimit < coder->memusage)360return LZMA_MEMLIMIT_ERROR;361362coder->memlimit = new_memlimit;363}364365return LZMA_OK;366}367368369extern lzma_ret370lzma_lzip_decoder_init(371lzma_next_coder *next, const lzma_allocator *allocator,372uint64_t memlimit, uint32_t flags)373{374lzma_next_coder_init(&lzma_lzip_decoder_init, next, allocator);375376if (flags & ~LZMA_SUPPORTED_FLAGS)377return LZMA_OPTIONS_ERROR;378379lzma_lzip_coder *coder = next->coder;380if (coder == NULL) {381coder = lzma_alloc(sizeof(lzma_lzip_coder), allocator);382if (coder == NULL)383return LZMA_MEM_ERROR;384385next->coder = coder;386next->code = &lzip_decode;387next->end = &lzip_decoder_end;388next->get_check = &lzip_decoder_get_check;389next->memconfig = &lzip_decoder_memconfig;390391coder->lzma_decoder = LZMA_NEXT_CODER_INIT;392}393394coder->sequence = SEQ_ID_STRING;395coder->memlimit = my_max(1, memlimit);396coder->memusage = LZMA_MEMUSAGE_BASE;397coder->tell_any_check = (flags & LZMA_TELL_ANY_CHECK) != 0;398coder->ignore_check = (flags & LZMA_IGNORE_CHECK) != 0;399coder->concatenated = (flags & LZMA_CONCATENATED) != 0;400coder->first_member = true;401coder->pos = 0;402403return LZMA_OK;404}405406407extern LZMA_API(lzma_ret)408lzma_lzip_decoder(lzma_stream *strm, uint64_t memlimit, uint32_t flags)409{410lzma_next_strm_init(lzma_lzip_decoder_init, strm, memlimit, flags);411412strm->internal->supported_actions[LZMA_RUN] = true;413strm->internal->supported_actions[LZMA_FINISH] = true;414415return LZMA_OK;416}417418419