tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

LzmaHandler.cpp (14573B)


      1 // LzmaHandler.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "../../../C/CpuArch.h"
      6 
      7 #include "../../Common/ComTry.h"
      8 #include "../../Common/IntToString.h"
      9 
     10 #include "../../Windows/PropVariant.h"
     11 
     12 #include "../Common/FilterCoder.h"
     13 #include "../Common/ProgressUtils.h"
     14 #include "../Common/RegisterArc.h"
     15 #include "../Common/StreamUtils.h"
     16 
     17 #include "../Compress/BcjCoder.h"
     18 #include "../Compress/LzmaDecoder.h"
     19 
     20 #include "Common/DummyOutStream.h"
     21 
     22 using namespace NWindows;
     23 
     24 namespace NArchive {
     25 namespace NLzma {
     26 
     27 static bool CheckDicSize(const Byte *p)
     28 {
     29  UInt32 dicSize = GetUi32(p);
     30  if (dicSize == 1)
     31    return true;
     32  for (unsigned i = 0; i <= 30; i++)
     33    if (dicSize == ((UInt32)2 << i) || dicSize == ((UInt32)3 << i))
     34      return true;
     35  return (dicSize == 0xFFFFFFFF);
     36 }
     37 
     38 static const Byte kProps[] =
     39 {
     40  kpidSize,
     41  kpidPackSize,
     42  kpidMethod
     43 };
     44 
     45 static const Byte kArcProps[] =
     46 {
     47  kpidNumStreams,
     48  kpidMethod
     49 };
     50 
     51 struct CHeader
     52 {
     53  UInt64 Size;
     54  Byte FilterID;
     55  Byte LzmaProps[5];
     56 
     57  Byte GetProp() const { return LzmaProps[0]; }
     58  UInt32 GetDicSize() const { return GetUi32(LzmaProps + 1); }
     59  bool HasSize() const { return (Size != (UInt64)(Int64)-1); }
     60  bool Parse(const Byte *buf, bool isThereFilter);
     61 };
     62 
     63 bool CHeader::Parse(const Byte *buf, bool isThereFilter)
     64 {
     65  FilterID = 0;
     66  if (isThereFilter)
     67    FilterID = buf[0];
     68  const Byte *sig = buf + (isThereFilter ? 1 : 0);
     69  for (int i = 0; i < 5; i++)
     70    LzmaProps[i] = sig[i];
     71  Size = GetUi64(sig + 5);
     72  return
     73    LzmaProps[0] < 5 * 5 * 9 &&
     74    FilterID < 2 &&
     75    (!HasSize() || Size < ((UInt64)1 << 56))
     76    && CheckDicSize(LzmaProps + 1);
     77 }
     78 
     79 class CDecoder
     80 {
     81  CMyComPtr<ISequentialOutStream> _bcjStream;
     82  CFilterCoder *_filterCoder;
     83  CMyComPtr<ICompressCoder> _lzmaDecoder;
     84 public:
     85  NCompress::NLzma::CDecoder *_lzmaDecoderSpec;
     86 
     87  ~CDecoder();
     88  HRESULT Create(bool filtered, ISequentialInStream *inStream);
     89 
     90  HRESULT Code(const CHeader &header, ISequentialOutStream *outStream, ICompressProgressInfo *progress);
     91 
     92  UInt64 GetInputProcessedSize() const { return _lzmaDecoderSpec->GetInputProcessedSize(); }
     93 
     94  void ReleaseInStream() { if (_lzmaDecoder) _lzmaDecoderSpec->ReleaseInStream(); }
     95 
     96  HRESULT ReadInput(Byte *data, UInt32 size, UInt32 *processedSize)
     97    { return _lzmaDecoderSpec->ReadFromInputStream(data, size, processedSize); }
     98 };
     99 
    100 HRESULT CDecoder::Create(bool filteredMode, ISequentialInStream *inStream)
    101 {
    102  if (!_lzmaDecoder)
    103  {
    104    _lzmaDecoderSpec = new NCompress::NLzma::CDecoder;
    105    _lzmaDecoderSpec->FinishStream = true;
    106    _lzmaDecoder = _lzmaDecoderSpec;
    107  }
    108 
    109  if (filteredMode)
    110  {
    111    if (!_bcjStream)
    112    {
    113      _filterCoder = new CFilterCoder(false);
    114      CMyComPtr<ICompressCoder> coder = _filterCoder;
    115      _filterCoder->Filter = new NCompress::NBcj::CCoder(false);
    116      _bcjStream = _filterCoder;
    117    }
    118  }
    119 
    120  return _lzmaDecoderSpec->SetInStream(inStream);
    121 }
    122 
    123 CDecoder::~CDecoder()
    124 {
    125  ReleaseInStream();
    126 }
    127 
    128 HRESULT CDecoder::Code(const CHeader &header, ISequentialOutStream *outStream,
    129    ICompressProgressInfo *progress)
    130 {
    131  if (header.FilterID > 1)
    132    return E_NOTIMPL;
    133 
    134  {
    135    CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties;
    136    _lzmaDecoder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecoderProperties);
    137    if (!setDecoderProperties)
    138      return E_NOTIMPL;
    139    RINOK(setDecoderProperties->SetDecoderProperties2(header.LzmaProps, 5));
    140  }
    141 
    142  bool filteredMode = (header.FilterID == 1);
    143 
    144  if (filteredMode)
    145  {
    146    RINOK(_filterCoder->SetOutStream(outStream));
    147    outStream = _bcjStream;
    148    RINOK(_filterCoder->SetOutStreamSize(NULL));
    149  }
    150 
    151  const UInt64 *Size = header.HasSize() ? &header.Size : NULL;
    152  HRESULT res = _lzmaDecoderSpec->CodeResume(outStream, Size, progress);
    153 
    154  if (filteredMode)
    155  {
    156    {
    157      HRESULT res2 = _filterCoder->OutStreamFinish();
    158      if (res == S_OK)
    159        res = res2;
    160    }
    161    HRESULT res2 = _filterCoder->ReleaseOutStream();
    162    if (res == S_OK)
    163      res = res2;
    164  }
    165  
    166  RINOK(res);
    167 
    168  if (header.HasSize())
    169    if (_lzmaDecoderSpec->GetOutputProcessedSize() != header.Size)
    170      return S_FALSE;
    171 
    172  return S_OK;
    173 }
    174 
    175 
    176 class CHandler:
    177  public IInArchive,
    178  public IArchiveOpenSeq,
    179  public CMyUnknownImp
    180 {
    181  CHeader _header;
    182  bool _lzma86;
    183  CMyComPtr<IInStream> _stream;
    184  CMyComPtr<ISequentialInStream> _seqStream;
    185  
    186  bool _isArc;
    187  bool _needSeekToStart;
    188  bool _dataAfterEnd;
    189  bool _needMoreInput;
    190 
    191  bool _packSize_Defined;
    192  bool _unpackSize_Defined;
    193  bool _numStreams_Defined;
    194 
    195  bool _unsupported;
    196  bool _dataError;
    197 
    198  UInt64 _packSize;
    199  UInt64 _unpackSize;
    200  UInt64 _numStreams;
    201 
    202  void GetMethod(NCOM::CPropVariant &prop);
    203 
    204 public:
    205  MY_UNKNOWN_IMP2(IInArchive, IArchiveOpenSeq)
    206 
    207  INTERFACE_IInArchive(;)
    208  STDMETHOD(OpenSeq)(ISequentialInStream *stream);
    209 
    210  CHandler(bool lzma86) { _lzma86 = lzma86; }
    211 
    212  unsigned GetHeaderSize() const { return 5 + 8 + (_lzma86 ? 1 : 0); }
    213 
    214 };
    215 
    216 IMP_IInArchive_Props
    217 IMP_IInArchive_ArcProps
    218 
    219 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
    220 {
    221  NCOM::CPropVariant prop;
    222  switch (propID)
    223  {
    224    case kpidPhySize: if (_packSize_Defined) prop = _packSize; break;
    225    case kpidNumStreams: if (_numStreams_Defined) prop = _numStreams; break;
    226    case kpidUnpackSize: if (_unpackSize_Defined) prop = _unpackSize; break;
    227    case kpidMethod: GetMethod(prop); break;
    228    case kpidErrorFlags:
    229    {
    230      UInt32 v = 0;
    231      if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;;
    232      if (_needMoreInput) v |= kpv_ErrorFlags_UnexpectedEnd;
    233      if (_dataAfterEnd) v |= kpv_ErrorFlags_DataAfterEnd;
    234      if (_unsupported) v |= kpv_ErrorFlags_UnsupportedMethod;
    235      if (_dataError) v |= kpv_ErrorFlags_DataError;
    236      prop = v;
    237      break;
    238    }
    239  }
    240  prop.Detach(value);
    241  return S_OK;
    242 }
    243 
    244 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
    245 {
    246  *numItems = 1;
    247  return S_OK;
    248 }
    249 
    250 
    251 static void DictSizeToString(UInt32 val, char *s)
    252 {
    253  for (unsigned i = 0; i <= 31; i++)
    254    if (((UInt32)1 << i) == val)
    255    {
    256      ::ConvertUInt32ToString(i, s);
    257      return;
    258    }
    259  char c = 'b';
    260       if ((val & ((1 << 20) - 1)) == 0) { val >>= 20; c = 'm'; }
    261  else if ((val & ((1 << 10) - 1)) == 0) { val >>= 10; c = 'k'; }
    262  ::ConvertUInt32ToString(val, s);
    263  s += MyStringLen(s);
    264  *s++ = c;
    265  *s = 0;
    266 }
    267 
    268 static char *AddProp32(char *s, const char *name, UInt32 v)
    269 {
    270  *s++ = ':';
    271  s = MyStpCpy(s, name);
    272  ::ConvertUInt32ToString(v, s);
    273  return s + MyStringLen(s);
    274 }
    275 
    276 void CHandler::GetMethod(NCOM::CPropVariant &prop)
    277 {
    278  if (!_stream)
    279    return;
    280 
    281  char sz[64];
    282  char *s = sz;
    283  if (_header.FilterID != 0)
    284    s = MyStpCpy(s, "BCJ ");
    285  s = MyStpCpy(s, "LZMA:");
    286  DictSizeToString(_header.GetDicSize(), s);
    287  s += strlen(s);
    288  
    289  UInt32 d = _header.GetProp();
    290  // if (d != 0x5D)
    291  {
    292    UInt32 lc = d % 9;
    293    d /= 9;
    294    UInt32 pb = d / 5;
    295    UInt32 lp = d % 5;
    296    if (lc != 3) s = AddProp32(s, "lc", lc);
    297    if (lp != 0) s = AddProp32(s, "lp", lp);
    298    if (pb != 2) s = AddProp32(s, "pb", pb);
    299  }
    300  prop = sz;
    301 }
    302 
    303 
    304 STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
    305 {
    306  NCOM::CPropVariant prop;
    307  switch (propID)
    308  {
    309    case kpidSize: if (_stream && _header.HasSize()) prop = _header.Size; break;
    310    case kpidPackSize: if (_packSize_Defined) prop = _packSize; break;
    311    case kpidMethod: GetMethod(prop); break;
    312  }
    313  prop.Detach(value);
    314  return S_OK;
    315 }
    316 
    317 API_FUNC_static_IsArc IsArc_Lzma(const Byte *p, size_t size)
    318 {
    319  const UInt32 kHeaderSize = 1 + 4 + 8;
    320  if (size < kHeaderSize)
    321    return k_IsArc_Res_NEED_MORE;
    322  if (p[0] >= 5 * 5 * 9)
    323    return k_IsArc_Res_NO;
    324  UInt64 unpackSize = GetUi64(p + 1 + 4);
    325  if (unpackSize != (UInt64)(Int64)-1)
    326  {
    327    if (size >= ((UInt64)1 << 56))
    328      return k_IsArc_Res_NO;
    329  }
    330  if (unpackSize != 0)
    331  {
    332    if (size < kHeaderSize + 2)
    333      return k_IsArc_Res_NEED_MORE;
    334    if (p[kHeaderSize] != 0)
    335      return k_IsArc_Res_NO;
    336    if (unpackSize != (UInt64)(Int64)-1)
    337    {
    338      if ((p[kHeaderSize + 1] & 0x80) != 0)
    339        return k_IsArc_Res_NO;
    340    }
    341  }
    342  if (!CheckDicSize(p + 1))
    343    // return k_IsArc_Res_YES_LOW_PROB;
    344    return k_IsArc_Res_NO;
    345  return k_IsArc_Res_YES;
    346 }
    347 }
    348 
    349 API_FUNC_static_IsArc IsArc_Lzma86(const Byte *p, size_t size)
    350 {
    351  if (size < 1)
    352    return k_IsArc_Res_NEED_MORE;
    353  Byte filterID = p[0];
    354  if (filterID != 0 && filterID != 1)
    355    return k_IsArc_Res_NO;
    356  return IsArc_Lzma(p + 1, size - 1);
    357 }
    358 }
    359 
    360 STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *)
    361 {
    362  Close();
    363  
    364  const UInt32 kBufSize = 1 + 5 + 8 + 2;
    365  Byte buf[kBufSize];
    366  
    367  RINOK(ReadStream_FALSE(inStream, buf, kBufSize));
    368  
    369  if (!_header.Parse(buf, _lzma86))
    370    return S_FALSE;
    371  const Byte *start = buf + GetHeaderSize();
    372  if (start[0] != 0 /* || (start[1] & 0x80) != 0 */ ) // empty stream with EOS is not 0x80
    373    return S_FALSE;
    374  
    375  RINOK(inStream->Seek(0, STREAM_SEEK_END, &_packSize));
    376  if (_packSize >= 24 && _header.Size == 0 && _header.FilterID == 0 && _header.LzmaProps[0] == 0)
    377    return S_FALSE;
    378  _isArc = true;
    379  _stream = inStream;
    380  _seqStream = inStream;
    381  _needSeekToStart = true;
    382  return S_OK;
    383 }
    384 
    385 STDMETHODIMP CHandler::OpenSeq(ISequentialInStream *stream)
    386 {
    387  Close();
    388  _isArc = true;
    389  _seqStream = stream;
    390  return S_OK;
    391 }
    392 
    393 STDMETHODIMP CHandler::Close()
    394 {
    395  _isArc = false;
    396  _packSize_Defined = false;
    397  _unpackSize_Defined = false;
    398  _numStreams_Defined = false;
    399 
    400  _dataAfterEnd = false;
    401  _needMoreInput = false;
    402  _unsupported = false;
    403  _dataError = false;
    404 
    405  _packSize = 0;
    406 
    407  _needSeekToStart = false;
    408 
    409  _stream.Release();
    410  _seqStream.Release();
    411   return S_OK;
    412 }
    413 
    414 class CCompressProgressInfoImp:
    415  public ICompressProgressInfo,
    416  public CMyUnknownImp
    417 {
    418  CMyComPtr<IArchiveOpenCallback> Callback;
    419 public:
    420  UInt64 Offset;
    421 
    422  MY_UNKNOWN_IMP1(ICompressProgressInfo)
    423  STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
    424  void Init(IArchiveOpenCallback *callback) { Callback = callback; }
    425 };
    426 
    427 STDMETHODIMP CCompressProgressInfoImp::SetRatioInfo(const UInt64 *inSize, const UInt64 * /* outSize */)
    428 {
    429  if (Callback)
    430  {
    431    const UInt64 files = 0;
    432    const UInt64 val = Offset + *inSize;
    433    return Callback->SetCompleted(&files, &val);
    434  }
    435  return S_OK;
    436 }
    437 
    438 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
    439    Int32 testMode, IArchiveExtractCallback *extractCallback)
    440 {
    441  COM_TRY_BEGIN
    442 
    443  if (numItems == 0)
    444    return S_OK;
    445  if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0))
    446    return E_INVALIDARG;
    447 
    448  if (_packSize_Defined)
    449    extractCallback->SetTotal(_packSize);
    450    
    451  
    452  CMyComPtr<ISequentialOutStream> realOutStream;
    453  Int32 askMode = testMode ?
    454      NExtract::NAskMode::kTest :
    455      NExtract::NAskMode::kExtract;
    456  RINOK(extractCallback->GetStream(0, &realOutStream, askMode));
    457  if (!testMode && !realOutStream)
    458    return S_OK;
    459  
    460  extractCallback->PrepareOperation(askMode);
    461 
    462  CDummyOutStream *outStreamSpec = new CDummyOutStream;
    463  CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
    464  outStreamSpec->SetStream(realOutStream);
    465  outStreamSpec->Init();
    466  realOutStream.Release();
    467 
    468  CLocalProgress *lps = new CLocalProgress;
    469  CMyComPtr<ICompressProgressInfo> progress = lps;
    470  lps->Init(extractCallback, true);
    471 
    472  if (_needSeekToStart)
    473  {
    474    if (!_stream)
    475      return E_FAIL;
    476    RINOK(_stream->Seek(0, STREAM_SEEK_SET, NULL));
    477  }
    478  else
    479    _needSeekToStart = true;
    480 
    481  CDecoder decoder;
    482  HRESULT result = decoder.Create(_lzma86, _seqStream);
    483  RINOK(result);
    484 
    485  bool firstItem = true;
    486 
    487  UInt64 packSize = 0;
    488  UInt64 unpackSize = 0;
    489  UInt64 numStreams = 0;
    490 
    491  bool dataAfterEnd = false;
    492  
    493  for (;;)
    494  {
    495    lps->InSize = packSize;
    496    lps->OutSize = unpackSize;
    497    RINOK(lps->SetCur());
    498 
    499    const UInt32 kBufSize = 1 + 5 + 8;
    500    Byte buf[kBufSize];
    501    const UInt32 headerSize = GetHeaderSize();
    502    UInt32 processed;
    503    RINOK(decoder.ReadInput(buf, headerSize, &processed));
    504    if (processed != headerSize)
    505    {
    506      if (processed != 0)
    507        dataAfterEnd = true;
    508      break;
    509    }
    510  
    511    CHeader st;
    512    if (!st.Parse(buf, _lzma86))
    513    {
    514      dataAfterEnd = true;
    515      break;
    516    }
    517    numStreams++;
    518    firstItem = false;
    519 
    520    result = decoder.Code(st, outStream, progress);
    521 
    522    packSize = decoder.GetInputProcessedSize();
    523    unpackSize = outStreamSpec->GetSize();
    524    
    525    if (result == E_NOTIMPL)
    526    {
    527      _unsupported = true;
    528      result = S_FALSE;
    529      break;
    530    }
    531    if (result == S_FALSE)
    532      break;
    533    RINOK(result);
    534  }
    535 
    536  if (firstItem)
    537  {
    538    _isArc = false;
    539    result = S_FALSE;
    540  }
    541  else if (result == S_OK || result == S_FALSE)
    542  {
    543    if (dataAfterEnd)
    544      _dataAfterEnd = true;
    545    else if (decoder._lzmaDecoderSpec->NeedsMoreInput())
    546      _needMoreInput = true;
    547 
    548    _packSize = packSize;
    549    _unpackSize = unpackSize;
    550    _numStreams = numStreams;
    551  
    552    _packSize_Defined = true;
    553    _unpackSize_Defined = true;
    554    _numStreams_Defined = true;
    555  }
    556  
    557  Int32 opResult = NExtract::NOperationResult::kOK;
    558 
    559  if (!_isArc)
    560    opResult = NExtract::NOperationResult::kIsNotArc;
    561  else if (_needMoreInput)
    562    opResult = NExtract::NOperationResult::kUnexpectedEnd;
    563  else if (_unsupported)
    564    opResult = NExtract::NOperationResult::kUnsupportedMethod;
    565  else if (_dataAfterEnd)
    566    opResult = NExtract::NOperationResult::kDataAfterEnd;
    567  else if (result == S_FALSE)
    568    opResult = NExtract::NOperationResult::kDataError;
    569  else if (result == S_OK)
    570    opResult = NExtract::NOperationResult::kOK;
    571  else
    572    return result;
    573 
    574  outStream.Release();
    575  return extractCallback->SetOperationResult(opResult);
    576 
    577  COM_TRY_END
    578 }
    579 
    580 namespace NLzmaAr {
    581 
    582 // 2, { 0x5D, 0x00 },
    583 
    584 REGISTER_ARC_I_CLS_NO_SIG(
    585  CHandler(false),
    586  "lzma", "lzma", 0, 0xA,
    587  0,
    588  NArcInfoFlags::kStartOpen |
    589  NArcInfoFlags::kKeepName,
    590  IsArc_Lzma)
    591 
    592 }
    593 
    594 namespace NLzma86Ar {
    595 
    596 REGISTER_ARC_I_CLS_NO_SIG(
    597  CHandler(true),
    598  "lzma86", "lzma86", 0, 0xB,
    599  0,
    600  NArcInfoFlags::kKeepName,
    601  IsArc_Lzma86)
    602 
    603 }
    604 
    605 }}