Skip to content

Commit

Permalink
COMMON: Use lower-level liblzma functions to decompress LZMA
Browse files Browse the repository at this point in the history
The single-call lzma_raw_buffer_decode() function mucks about with the
error code and the stream positions, so we use a raw lzma decoder
stream ourselves. That we, we correctly identify when a compressed
data stream has been truncated.
  • Loading branch information
DrMcCoy committed Nov 6, 2016
1 parent a736193 commit 5fef7ba
Showing 1 changed file with 25 additions and 18 deletions.
43 changes: 25 additions & 18 deletions src/common/lzma.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include "src/common/types.h"
#include <lzma.h>

#include <boost/scope_exit.hpp>

#include "src/common/lzma.h"
#include "src/common/scopedptr.h"
#include "src/common/error.h"
Expand All @@ -34,11 +36,10 @@
namespace Common {

byte *decompressLZMA1(const byte *data, size_t inputSize, size_t outputSize) {
lzma_filter filters[2];
filters[0].id = LZMA_FILTER_LZMA1;
filters[0].options = 0;
filters[1].id = LZMA_VLI_UNKNOWN;
filters[1].options = 0;
lzma_filter filters[2] = {
{ LZMA_FILTER_LZMA1, 0 },
{ LZMA_VLI_UNKNOWN , 0 }
};

if (!lzma_filter_decoder_is_supported(filters[0].id))
throw Exception("LZMA1 compression not supported");
Expand All @@ -51,26 +52,32 @@ byte *decompressLZMA1(const byte *data, size_t inputSize, size_t outputSize) {
throw Exception("LZMA1 properties size larger than input data");

if (lzma_properties_decode(&filters[0], 0, data, propsSize) != LZMA_OK)
throw Exception("Failed to decode LZMA properties");
throw Exception("Failed to decode LZMA1 properties");

data += propsSize;
inputSize -= propsSize;

ScopedArray<byte> outputData(new byte[outputSize]);
lzma_stream strm = LZMA_STREAM_INIT;
BOOST_SCOPE_EXIT( (&strm) ) {
lzma_end(&strm);
} BOOST_SCOPE_EXIT_END

lzma_ret lzmaRet = LZMA_OK;

size_t posIn = 0, posOut = 0;
lzma_ret decodeRet = lzma_raw_buffer_decode(filters, 0,
data , &posIn , inputSize,
outputData.get(), &posOut, outputSize);
if ((lzmaRet = lzma_raw_decoder(&strm, filters)) != LZMA_OK)
throw Exception("Failed to create raw LZMA1 decoder: %d", (int) lzmaRet);

ScopedArray<byte> outputData(new byte[outputSize]);

/* Ignore LZMA_DATA_ERROR and LZMA_BUF_ERROR thrown from the uncompressor.
* These are also thrown when there is no end marker (which happens in data
* found in BZF files). Unfortunately, LZMA provides no way to differentiate
* between "real" buffer/data errors and missing end markers. So we have
* no way to detect actual truncated input data, for example. */
strm.next_in = data;
strm.avail_in = inputSize;
strm.next_out = outputData.get();
strm.avail_out = outputSize;

if ((decodeRet != LZMA_OK) && (decodeRet != LZMA_DATA_ERROR) && (decodeRet != LZMA_BUF_ERROR))
throw Exception("Failed to uncompress LZMA data: %d", (int) decodeRet);
if ((lzmaRet = lzma_code(&strm, LZMA_FINISH)) != LZMA_STREAM_END)
if ((strm.avail_in != 0) || (strm.avail_out != 0))
throw Exception("Failed to uncompress LZMA1 data: %d (%u, %u, %u, %u)", (int) lzmaRet,
(uint)inputSize, (uint)outputSize, (uint)strm.avail_in, (uint)strm.avail_out);

return outputData.release();
}
Expand Down

0 comments on commit 5fef7ba

Please sign in to comment.