/* XzDec.c -- Xz Decode 2010-04-16 : Igor Pavlov : Public domain */ /* #define XZ_DUMP */ #ifdef XZ_DUMP #include <stdio.h> #endif #include <stdlib.h> #include <string.h> #include "7zCrc.h" #include "Alloc.h" #include "Bra.h" #include "CpuArch.h" #include "Delta.h" #include "Lzma2Dec.h" #ifdef USE_SUBBLOCK #include "SbDec.h" #endif #include "Xz.h" #define XZ_CHECK_SIZE_MAX 64 #define CODER_BUF_SIZE (1 << 17) unsigned Xz_ReadVarInt(const Byte *p, size_t maxSize, UInt64 *value) { int i, limit; *value = 0; limit = (maxSize > 9) ? 9 : (int)maxSize; for (i = 0; i < limit;) { Byte b = p[i]; *value |= (UInt64)(b & 0x7F) << (7 * i++); if ((b & 0x80) == 0) return (b == 0 && i != 1) ? 0 : i; } return 0; } /* ---------- BraState ---------- */ #define BRA_BUF_SIZE (1 << 14) typedef struct { size_t bufPos; size_t bufConv; size_t bufTotal; UInt32 methodId; int encodeMode; UInt32 delta; UInt32 ip; UInt32 x86State; Byte deltaState[DELTA_STATE_SIZE]; Byte buf[BRA_BUF_SIZE]; } CBraState; void BraState_Free(void *pp, ISzAlloc *alloc) { alloc->Free(alloc, pp); } SRes BraState_SetProps(void *pp, const Byte *props, size_t propSize, ISzAlloc *alloc) { CBraState *p = ((CBraState *)pp); alloc = alloc; p->encodeMode = 0; p->ip = 0; if (p->methodId == XZ_ID_Delta) { if (propSize != 1) return SZ_ERROR_UNSUPPORTED; p->delta = (unsigned)props[0] + 1; } else { if (propSize == 4) { UInt32 v = GetUi32(props); switch(p->methodId) { case XZ_ID_PPC: case XZ_ID_ARM: case XZ_ID_SPARC: if ((v & 3) != 0) return SZ_ERROR_UNSUPPORTED; break; case XZ_ID_ARMT: if ((v & 1) != 0) return SZ_ERROR_UNSUPPORTED; break; case XZ_ID_IA64: if ((v & 0xF) != 0) return SZ_ERROR_UNSUPPORTED; break; } p->ip = v; } else if (propSize != 0) return SZ_ERROR_UNSUPPORTED; } return SZ_OK; } void BraState_Init(void *pp) { CBraState *p = ((CBraState *)pp); p->bufPos = p->bufConv = p->bufTotal = 0; x86_Convert_Init(p->x86State); if (p->methodId == XZ_ID_Delta) Delta_Init(p->deltaState); } #define CASE_BRA_CONV(isa) case XZ_ID_ ## isa: p->bufConv = isa ## _Convert(p->buf, p->bufTotal, p->ip, p->encodeMode); break; static SRes BraState_Code(void *pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, int *wasFinished) { CBraState *p = ((CBraState *)pp); SizeT destLenOrig = *destLen; SizeT srcLenOrig = *srcLen; *destLen = 0; *srcLen = 0; finishMode = finishMode; *wasFinished = 0; while (destLenOrig > 0) { if (p->bufPos != p->bufConv) { size_t curSize = p->bufConv - p->bufPos; if (curSize > destLenOrig) curSize = destLenOrig; memcpy(dest, p->buf + p->bufPos, curSize); p->bufPos += curSize; *destLen += curSize; dest += curSize; destLenOrig -= curSize; continue; } p->bufTotal -= p->bufPos; memmove(p->buf, p->buf + p->bufPos, p->bufTotal); p->bufPos = 0; p->bufConv = 0; { size_t curSize = BRA_BUF_SIZE - p->bufTotal; if (curSize > srcLenOrig) curSize = srcLenOrig; memcpy(p->buf + p->bufTotal, src, curSize); *srcLen += curSize; src += curSize; srcLenOrig -= curSize; p->bufTotal += curSize; } if (p->bufTotal == 0) break; switch(p->methodId) { case XZ_ID_Delta: if (p->encodeMode) Delta_Encode(p->deltaState, p->delta, p->buf, p->bufTotal); else Delta_Decode(p->deltaState, p->delta, p->buf, p->bufTotal); p->bufConv = p->bufTotal; break; case XZ_ID_X86: p->bufConv = x86_Convert(p->buf, p->bufTotal, p->ip, &p->x86State, p->encodeMode); break; CASE_BRA_CONV(PPC) CASE_BRA_CONV(IA64) CASE_BRA_CONV(ARM) CASE_BRA_CONV(ARMT) CASE_BRA_CONV(SPARC) default: return SZ_ERROR_UNSUPPORTED; } p->ip += (UInt32)p->bufConv; if (p->bufConv == 0) { if (!srcWasFinished) break; p->bufConv = p->bufTotal; } } if (p->bufTotal == p->bufPos && srcLenOrig == 0 && srcWasFinished) *wasFinished = 1; return SZ_OK; } SRes BraState_SetFromMethod(IStateCoder *p, UInt64 id, ISzAlloc *alloc) { CBraState *decoder; if (id != XZ_ID_Delta && id != XZ_ID_X86 && id != XZ_ID_PPC && id != XZ_ID_IA64 && id != XZ_ID_ARM && id != XZ_ID_ARMT && id != XZ_ID_SPARC) return SZ_ERROR_UNSUPPORTED; p->p = 0; decoder = alloc->Alloc(alloc, sizeof(CBraState)); if (decoder == 0) return SZ_ERROR_MEM; decoder->methodId = (UInt32)id; p->p = decoder; p->Free = BraState_Free; p->SetProps = BraState_SetProps; p->Init = BraState_Init; p->Code = BraState_Code; return SZ_OK; } /* ---------- SbState ---------- */ #ifdef USE_SUBBLOCK static void SbState_Free(void *pp, ISzAlloc *alloc) { CSubblockDec *p = (CSubblockDec *)pp; SubblockDec_Free(p, alloc); alloc->Free(alloc, pp); } static SRes SbState_SetProps(void *pp, const Byte *props, size_t propSize, ISzAlloc *alloc) { pp = pp; props = props; alloc = alloc; return (propSize == 0) ? SZ_OK : SZ_ERROR_UNSUPPORTED; } static void SbState_Init(void *pp) { SubblockDec_Init((CSubblockDec *)pp); } static SRes SbState_Code(void *pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, int *wasFinished) { ECoderStatus status; SRes res = SubblockDec_Decode((CSubblockDec *)pp, dest, destLen, src, srcLen, finishMode, &status); srcWasFinished = srcWasFinished; *wasFinished = (status == LZMA_STATUS_FINISHED_WITH_MARK); return res; } SRes SbState_SetFromMethod(IStateCoder *p, ISzAlloc *alloc) { CSubblockDec *decoder; p->p = 0; decoder = alloc->Alloc(alloc, sizeof(CSubblockDec)); if (decoder == 0) return SZ_ERROR_MEM; p->p = decoder; p->Free = SbState_Free; p->SetProps = SbState_SetProps; p->Init = SbState_Init; p->Code = SbState_Code; SubblockDec_Construct(decoder); return SZ_OK; } #endif /* ---------- Lzma2State ---------- */ static void Lzma2State_Free(void *pp, ISzAlloc *alloc) { Lzma2Dec_Free((CLzma2Dec *)pp, alloc); alloc->Free(alloc, pp); } static SRes Lzma2State_SetProps(void *pp, const Byte *props, size_t propSize, ISzAlloc *alloc) { if (propSize != 1) return SZ_ERROR_UNSUPPORTED; return Lzma2Dec_Allocate((CLzma2Dec *)pp, props[0], alloc); } static void Lzma2State_Init(void *pp) { Lzma2Dec_Init((CLzma2Dec *)pp); } static SRes Lzma2State_Code(void *pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, int *wasFinished) { ELzmaStatus status; /* ELzmaFinishMode fm = (finishMode == LZMA_FINISH_ANY) ? LZMA_FINISH_ANY : LZMA_FINISH_END; */ SRes res = Lzma2Dec_DecodeToBuf((CLzma2Dec *)pp, dest, destLen, src, srcLen, finishMode, &status); srcWasFinished = srcWasFinished; *wasFinished = (status == LZMA_STATUS_FINISHED_WITH_MARK); return res; } static SRes Lzma2State_SetFromMethod(IStateCoder *p, ISzAlloc *alloc) { CLzma2Dec *decoder = alloc->Alloc(alloc, sizeof(CLzma2Dec)); p->p = decoder; if (decoder == 0) return SZ_ERROR_MEM; p->Free = Lzma2State_Free; p->SetProps = Lzma2State_SetProps; p->Init = Lzma2State_Init; p->Code = Lzma2State_Code; Lzma2Dec_Construct(decoder); return SZ_OK; } void MixCoder_Construct(CMixCoder *p, ISzAlloc *alloc) { int i; p->alloc = alloc; p->buf = 0; p->numCoders = 0; for (i = 0; i < MIXCODER_NUM_FILTERS_MAX; i++) p->coders[i].p = NULL; } void MixCoder_Free(CMixCoder *p) { int i; for (i = 0; i < p->numCoders; i++) { IStateCoder *sc = &p->coders[i]; if (p->alloc && sc->p) sc->Free(sc->p, p->alloc); } p->numCoders = 0; if (p->buf) p->alloc->Free(p->alloc, p->buf); } void MixCoder_Init(CMixCoder *p) { int i; for (i = 0; i < p->numCoders - 1; i++) { p->size[i] = 0; p->pos[i] = 0; p->finished[i] = 0; } for (i = 0; i < p->numCoders; i++) { IStateCoder *coder = &p->coders[i]; coder->Init(coder->p); } } SRes MixCoder_SetFromMethod(CMixCoder *p, int coderIndex, UInt64 methodId) { IStateCoder *sc = &p->coders[coderIndex]; p->ids[coderIndex] = methodId; switch(methodId) { case XZ_ID_LZMA2: return Lzma2State_SetFromMethod(sc, p->alloc); #ifdef USE_SUBBLOCK case XZ_ID_Subblock: return SbState_SetFromMethod(sc, p->alloc); #endif } if (coderIndex == 0) return SZ_ERROR_UNSUPPORTED; return BraState_SetFromMethod(sc, methodId, p->alloc); } SRes MixCoder_Code(CMixCoder *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int srcWasFinished, ECoderFinishMode finishMode, ECoderStatus *status) { SizeT destLenOrig = *destLen; SizeT srcLenOrig = *srcLen; Bool allFinished = True; *destLen = 0; *srcLen = 0; *status = CODER_STATUS_NOT_FINISHED; if (p->buf == 0) { p->buf = p->alloc->Alloc(p->alloc, CODER_BUF_SIZE * (MIXCODER_NUM_FILTERS_MAX - 1)); if (p->buf == 0) return SZ_ERROR_MEM; } if (p->numCoders != 1) finishMode = CODER_FINISH_ANY; for (;;) { Bool processed = False; int i; /* if (p->numCoders == 1 && *destLen == destLenOrig && finishMode == LZMA_FINISH_ANY) break; */ for (i = 0; i < p->numCoders; i++) { SRes res; IStateCoder *coder = &p->coders[i]; Byte *destCur; SizeT destLenCur, srcLenCur; const Byte *srcCur; int srcFinishedCur; int encodingWasFinished; if (i == 0) { srcCur = src; srcLenCur = srcLenOrig - *srcLen; srcFinishedCur = srcWasFinished; } else { srcCur = p->buf + (CODER_BUF_SIZE * (i - 1)) + p->pos[i - 1]; srcLenCur = p->size[i - 1] - p->pos[i - 1]; srcFinishedCur = p->finished[i - 1]; } if (i == p->numCoders - 1) { destCur = dest; destLenCur = destLenOrig - *destLen; } else { if (p->pos[i] != p->size[i]) continue; destCur = p->buf + (CODER_BUF_SIZE * i); destLenCur = CODER_BUF_SIZE; } res = coder->Code(coder->p, destCur, &destLenCur, srcCur, &srcLenCur, srcFinishedCur, finishMode, &encodingWasFinished); if (!encodingWasFinished) allFinished = False; if (i == 0) { *srcLen += srcLenCur; src += srcLenCur; } else { p->pos[i - 1] += srcLenCur; } if (i == p->numCoders - 1) { *destLen += destLenCur; dest += destLenCur; } else { p->size[i] = destLenCur; p->pos[i] = 0; p->finished[i] = encodingWasFinished; } if (res != SZ_OK) return res; if (destLenCur != 0 || srcLenCur != 0) processed = True; } if (!processed) break; } if (allFinished) *status = CODER_STATUS_FINISHED_WITH_MARK; return SZ_OK; } SRes Xz_ParseHeader(CXzStreamFlags *p, const Byte *buf) { *p = (CXzStreamFlags)GetBe16(buf + XZ_SIG_SIZE); if (CrcCalc(buf + XZ_SIG_SIZE, XZ_STREAM_FLAGS_SIZE) != GetUi32(buf + XZ_SIG_SIZE + XZ_STREAM_FLAGS_SIZE)) return SZ_ERROR_NO_ARCHIVE; return XzFlags_IsSupported(*p) ? SZ_OK : SZ_ERROR_UNSUPPORTED; } static Bool Xz_CheckFooter(CXzStreamFlags flags, UInt64 indexSize, const Byte *buf) { return indexSize == (((UInt64)GetUi32(buf + 4) + 1) << 2) && (GetUi32(buf) == CrcCalc(buf + 4, 6) && flags == GetBe16(buf + 8) && memcmp(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) == 0); } #define READ_VARINT_AND_CHECK(buf, pos, size, res) \ { unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \ if (s == 0) return SZ_ERROR_ARCHIVE; pos += s; } SRes XzBlock_Parse(CXzBlock *p, const Byte *header) { unsigned pos; int numFilters, i; UInt32 headerSize = (UInt32)header[0] << 2; if (CrcCalc(header, headerSize) != GetUi32(header + headerSize)) return SZ_ERROR_ARCHIVE; pos = 1; if (pos == headerSize) return SZ_ERROR_ARCHIVE; p->flags = header[pos++]; if (XzBlock_HasPackSize(p)) { READ_VARINT_AND_CHECK(header, pos, headerSize, &p->packSize); if (p->packSize == 0 || p->packSize + headerSize >= (UInt64)1 << 63) return SZ_ERROR_ARCHIVE; } if (XzBlock_HasUnpackSize(p)) READ_VARINT_AND_CHECK(header, pos, headerSize, &p->unpackSize); numFilters = XzBlock_GetNumFilters(p); for (i = 0; i < numFilters; i++) { CXzFilter *filter = p->filters + i; UInt64 size; READ_VARINT_AND_CHECK(header, pos, headerSize, &filter->id); READ_VARINT_AND_CHECK(header, pos, headerSize, &size); if (size > headerSize - pos || size > XZ_FILTER_PROPS_SIZE_MAX) return SZ_ERROR_ARCHIVE; filter->propsSize = (UInt32)size; memcpy(filter->props, header + pos, (size_t)size); pos += (unsigned)size; #ifdef XZ_DUMP printf("\nf[%d] = %2X: ", i, filter->id); { int i; for (i = 0; i < size; i++) printf(" %2X", filter->props[i]); } #endif } while (pos < headerSize) if (header[pos++] != 0) return SZ_ERROR_ARCHIVE; return SZ_OK; } SRes XzDec_Init(CMixCoder *p, const CXzBlock *block) { int i; Bool needReInit = True; int numFilters = XzBlock_GetNumFilters(block); if (numFilters == p->numCoders) { for (i = 0; i < numFilters; i++) if (p->ids[i] != block->filters[numFilters - 1 - i].id) break; needReInit = (i != numFilters); } if (needReInit) { MixCoder_Free(p); p->numCoders = numFilters; for (i = 0; i < numFilters; i++) { const CXzFilter *f = &block->filters[numFilters - 1 - i]; RINOK(MixCoder_SetFromMethod(p, i, f->id)); } } for (i = 0; i < numFilters; i++) { const CXzFilter *f = &block->filters[numFilters - 1 - i]; IStateCoder *sc = &p->coders[i]; RINOK(sc->SetProps(sc->p, f->props, f->propsSize, p->alloc)); } MixCoder_Init(p); return SZ_OK; } SRes XzUnpacker_Create(CXzUnpacker *p, ISzAlloc *alloc) { MixCoder_Construct(&p->decoder, alloc); p->state = XZ_STATE_STREAM_HEADER; p->pos = 0; p->numStreams = 0; return SZ_OK; } void XzUnpacker_Free(CXzUnpacker *p) { MixCoder_Free(&p->decoder); } SRes XzUnpacker_Code(CXzUnpacker *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, int finishMode, ECoderStatus *status) { SizeT destLenOrig = *destLen; SizeT srcLenOrig = *srcLen; *destLen = 0; *srcLen = 0; *status = CODER_STATUS_NOT_SPECIFIED; for (;;) { SizeT srcRem = srcLenOrig - *srcLen; if (p->state == XZ_STATE_BLOCK) { SizeT destLen2 = destLenOrig - *destLen; SizeT srcLen2 = srcLenOrig - *srcLen; SRes res; if (srcLen2 == 0 && destLen2 == 0) { *status = CODER_STATUS_NOT_FINISHED; return SZ_OK; } res = MixCoder_Code(&p->decoder, dest, &destLen2, src, &srcLen2, False, finishMode, status); XzCheck_Update(&p->check, dest, destLen2); (*srcLen) += srcLen2; src += srcLen2; p->packSize += srcLen2; (*destLen) += destLen2; dest += destLen2; p->unpackSize += destLen2; RINOK(res); if (*status == CODER_STATUS_FINISHED_WITH_MARK) { Byte temp[32]; unsigned num = Xz_WriteVarInt(temp, p->packSize + p->blockHeaderSize + XzFlags_GetCheckSize(p->streamFlags)); num += Xz_WriteVarInt(temp + num, p->unpackSize); Sha256_Update(&p->sha, temp, num); p->indexSize += num; p->numBlocks++; p->state = XZ_STATE_BLOCK_FOOTER; p->pos = 0; p->alignPos = 0; } else if (srcLen2 == 0 && destLen2 == 0) return SZ_OK; continue; } if (srcRem == 0) { *status = CODER_STATUS_NEEDS_MORE_INPUT; return SZ_OK; } switch(p->state) { case XZ_STATE_STREAM_HEADER: { if (p->pos < XZ_STREAM_HEADER_SIZE) { if (p->pos < XZ_SIG_SIZE && *src != XZ_SIG[p->pos]) return SZ_ERROR_NO_ARCHIVE; p->buf[p->pos++] = *src++; (*srcLen)++; } else { RINOK(Xz_ParseHeader(&p->streamFlags, p->buf)); p->state = XZ_STATE_BLOCK_HEADER; Sha256_Init(&p->sha); p->indexSize = 0; p->numBlocks = 0; p->pos = 0; } break; } case XZ_STATE_BLOCK_HEADER: { if (p->pos == 0) { p->buf[p->pos++] = *src++; (*srcLen)++; if (p->buf[0] == 0) { p->indexPreSize = 1 + Xz_WriteVarInt(p->buf + 1, p->numBlocks); p->indexPos = p->indexPreSize; p->indexSize += p->indexPreSize; Sha256_Final(&p->sha, p->shaDigest); Sha256_Init(&p->sha); p->crc = CrcUpdate(CRC_INIT_VAL, p->buf, p->indexPreSize); p->state = XZ_STATE_STREAM_INDEX; } p->blockHeaderSize = ((UInt32)p->buf[0] << 2) + 4; } else if (p->pos != p->blockHeaderSize) { UInt32 cur = p->blockHeaderSize - p->pos; if (cur > srcRem) cur = (UInt32)srcRem; memcpy(p->buf + p->pos, src, cur); p->pos += cur; (*srcLen) += cur; src += cur; } else { RINOK(XzBlock_Parse(&p->block, p->buf)); p->state = XZ_STATE_BLOCK; p->packSize = 0; p->unpackSize = 0; XzCheck_Init(&p->check, XzFlags_GetCheckType(p->streamFlags)); RINOK(XzDec_Init(&p->decoder, &p->block)); } break; } case XZ_STATE_BLOCK_FOOTER: { if (((p->packSize + p->alignPos) & 3) != 0) { (*srcLen)++; p->alignPos++; if (*src++ != 0) return SZ_ERROR_CRC; } else { UInt32 checkSize = XzFlags_GetCheckSize(p->streamFlags); UInt32 cur = checkSize - p->pos; if (cur != 0) { if (cur > srcRem) cur = (UInt32)srcRem; memcpy(p->buf + p->pos, src, cur); p->pos += cur; (*srcLen) += cur; src += cur; } else { Byte digest[XZ_CHECK_SIZE_MAX]; p->state = XZ_STATE_BLOCK_HEADER; p->pos = 0; if (XzCheck_Final(&p->check, digest) && memcmp(digest, p->buf, checkSize) != 0) return SZ_ERROR_CRC; } } break; } case XZ_STATE_STREAM_INDEX: { if (p->pos < p->indexPreSize) { (*srcLen)++; if (*src++ != p->buf[p->pos++]) return SZ_ERROR_CRC; } else { if (p->indexPos < p->indexSize) { UInt64 cur = p->indexSize - p->indexPos; if (srcRem > cur) srcRem = (SizeT)cur; p->crc = CrcUpdate(p->crc, src, srcRem); Sha256_Update(&p->sha, src, srcRem); (*srcLen) += srcRem; src += srcRem; p->indexPos += srcRem; } else if ((p->indexPos & 3) != 0) { Byte b = *src++; p->crc = CRC_UPDATE_BYTE(p->crc, b); (*srcLen)++; p->indexPos++; p->indexSize++; if (b != 0) return SZ_ERROR_CRC; } else { Byte digest[SHA256_DIGEST_SIZE]; p->state = XZ_STATE_STREAM_INDEX_CRC; p->indexSize += 4; p->pos = 0; Sha256_Final(&p->sha, digest); if (memcmp(digest, p->shaDigest, SHA256_DIGEST_SIZE) != 0) return SZ_ERROR_CRC; } } break; } case XZ_STATE_STREAM_INDEX_CRC: { if (p->pos < 4) { (*srcLen)++; p->buf[p->pos++] = *src++; } else { p->state = XZ_STATE_STREAM_FOOTER; p->pos = 0; if (CRC_GET_DIGEST(p->crc) != GetUi32(p->buf)) return SZ_ERROR_CRC; } break; } case XZ_STATE_STREAM_FOOTER: { UInt32 cur = XZ_STREAM_FOOTER_SIZE - p->pos; if (cur > srcRem) cur = (UInt32)srcRem; memcpy(p->buf + p->pos, src, cur); p->pos += cur; (*srcLen) += cur; src += cur; if (p->pos == XZ_STREAM_FOOTER_SIZE) { p->state = XZ_STATE_STREAM_PADDING; p->numStreams++; p->padSize = 0; if (!Xz_CheckFooter(p->streamFlags, p->indexSize, p->buf)) return SZ_ERROR_CRC; } break; } case XZ_STATE_STREAM_PADDING: { if (*src != 0) { if (((UInt32)p->padSize & 3) != 0) return SZ_ERROR_NO_ARCHIVE; p->pos = 0; p->state = XZ_STATE_STREAM_HEADER; } else { (*srcLen)++; src++; p->padSize++; } break; } case XZ_STATE_BLOCK: break; /* to disable GCC warning */ } } /* if (p->state == XZ_STATE_FINISHED) *status = CODER_STATUS_FINISHED_WITH_MARK; return SZ_OK; */ } Bool XzUnpacker_IsStreamWasFinished(CXzUnpacker *p) { return (p->state == XZ_STATE_STREAM_PADDING) && (((UInt32)p->padSize & 3) == 0); }