"Fossies" - the Fresh Open Source Software Archive

Member "p7zip_16.02/CPP/7zip/Archive/CramfsHandler.cpp" (20 May 2016, 19457 Bytes) of package /linux/misc/p7zip_16.02_src_all.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "CramfsHandler.cpp": 15.14.1_src_all_vs_16.02_src_all.

    1 // CramfsHandler.cpp
    2 
    3 #include "StdAfx.h"
    4 
    5 #include "../../../C/7zCrc.h"
    6 #include "../../../C/Alloc.h"
    7 #include "../../../C/CpuArch.h"
    8 #include "../../../C/LzmaDec.h"
    9 
   10 #include "../../Common/ComTry.h"
   11 #include "../../Common/MyLinux.h"
   12 #include "../../Common/StringConvert.h"
   13 
   14 #include "../../Windows/PropVariantUtils.h"
   15 
   16 #include "../Common/LimitedStreams.h"
   17 #include "../Common/ProgressUtils.h"
   18 #include "../Common/RegisterArc.h"
   19 #include "../Common/StreamObjects.h"
   20 #include "../Common/StreamUtils.h"
   21 
   22 #include "../Compress/CopyCoder.h"
   23 #include "../Compress/ZlibDecoder.h"
   24 
   25 namespace NArchive {
   26 namespace NCramfs {
   27 
   28 #define SIGNATURE { 'C','o','m','p','r','e','s','s','e','d',' ','R','O','M','F','S' }
   29 
   30 static const Byte kSignature[] = SIGNATURE;
   31 
   32 static const UInt32 kArcSizeMax = (256 + 16) << 20;
   33 static const UInt32 kNumFilesMax = (1 << 19);
   34 static const unsigned kNumDirLevelsMax = (1 << 8);
   35 
   36 static const UInt32 kHeaderSize = 0x40;
   37 static const unsigned kHeaderNameSize = 16;
   38 static const UInt32 kNodeSize = 12;
   39 
   40 static const UInt32 kFlag_FsVer2 = (1 << 0);
   41 
   42 static const unsigned k_Flags_BlockSize_Shift = 11;
   43 static const unsigned k_Flags_BlockSize_Mask = 7;
   44 static const unsigned k_Flags_Method_Shift = 14;
   45 static const unsigned k_Flags_Method_Mask = 3;
   46 
   47 /*
   48   There is possible collision in flags:
   49     - Original CramFS writes 0 in method field. But it uses ZLIB.
   50     - Modified CramFS writes 0 in method field for "NONE" compression?
   51   How to solve that collision?
   52 */
   53 
   54 #define k_Flags_Method_NONE 0
   55 #define k_Flags_Method_ZLIB 1
   56 #define k_Flags_Method_LZMA 2
   57 
   58 static const char * const k_Methods[] =
   59 {
   60     "Copy"
   61   , "ZLIB"
   62   , "LZMA"
   63   , "Unknown"
   64 };
   65 
   66 static const CUInt32PCharPair k_Flags[] =
   67 {
   68   { 0, "Ver2" },
   69   { 1, "SortedDirs" },
   70   { 8, "Holes" },
   71   { 9, "WrongSignature" },
   72   { 10, "ShiftedRootOffset" }
   73 };
   74 
   75 static const unsigned kBlockSizeLog = 12;
   76 
   77 /*
   78 struct CNode
   79 {
   80   UInt16 Mode;
   81   UInt16 Uid;
   82   UInt32 Size;
   83   Byte Gid;
   84   UInt32 NameLen;
   85   UInt32 Offset;
   86 
   87   void Parse(const Byte *p)
   88   {
   89     Mode = GetUi16(p);
   90     Uid = GetUi16(p + 2);
   91     Size = Get32(p + 4) & 0xFFFFFF;
   92     Gid = p[7];
   93     NameLen = p[8] & 0x3F;
   94     Offset = Get32(p + 8) >> 6;
   95   }
   96 };
   97 */
   98 
   99 #define Get32(p) (be ? GetBe32(p) : GetUi32(p))
  100 
  101 static UInt32 GetMode(const Byte *p, bool be) { return be ? GetBe16(p) : GetUi16(p); }
  102 static bool IsDir(const Byte *p, bool be) { return MY_LIN_S_ISDIR(GetMode(p, be)); }
  103 
  104 static UInt32 GetSize(const Byte *p, bool be)
  105 {
  106   if (be)
  107     return GetBe32(p + 4) >> 8;
  108   else
  109     return GetUi32(p + 4) & 0xFFFFFF;
  110 }
  111 
  112 static UInt32 GetNameLen(const Byte *p, bool be)
  113 {
  114   if (be)
  115     return (p[8] & 0xFC);
  116   else
  117     return (p[8] & 0x3F) << 2;
  118 }
  119 
  120 static UInt32 GetOffset(const Byte *p, bool be)
  121 {
  122   if (be)
  123     return (GetBe32(p + 8) & 0x03FFFFFF) << 2;
  124   else
  125     return GetUi32(p + 8) >> 6 << 2;
  126 }
  127 
  128 struct CItem
  129 {
  130   UInt32 Offset;
  131   int Parent;
  132 };
  133 
  134 struct CHeader
  135 {
  136   bool be;
  137   UInt32 Size;
  138   UInt32 Flags;
  139   // UInt32 Future;
  140   UInt32 Crc;
  141   // UInt32 Edition;
  142   UInt32 NumBlocks;
  143   UInt32 NumFiles;
  144   char Name[kHeaderNameSize];
  145 
  146   bool Parse(const Byte *p)
  147   {
  148     if (memcmp(p + 16, kSignature, ARRAY_SIZE(kSignature)) != 0)
  149       return false;
  150     switch (GetUi32(p))
  151     {
  152       case 0x28CD3D45: be = false; break;
  153       case 0x453DCD28: be = true; break;
  154       default: return false;
  155     }
  156     Size = Get32(p + 4);
  157     Flags = Get32(p + 8);
  158     // Future = Get32(p + 0xC);
  159     Crc = Get32(p + 0x20);
  160     // Edition = Get32(p + 0x24);
  161     NumBlocks = Get32(p + 0x28);
  162     NumFiles = Get32(p + 0x2C);
  163     memcpy(Name, p + 0x30, kHeaderNameSize);
  164     return true;
  165   }
  166 
  167   bool IsVer2() const { return (Flags & kFlag_FsVer2) != 0; }
  168   unsigned GetBlockSizeShift() const { return (unsigned)(Flags >> k_Flags_BlockSize_Shift) & k_Flags_BlockSize_Mask; }
  169   unsigned GetMethod() const { return (unsigned)(Flags >> k_Flags_Method_Shift) & k_Flags_Method_Mask; }
  170 };
  171 
  172 class CHandler:
  173   public IInArchive,
  174   public IInArchiveGetStream,
  175   public CMyUnknownImp
  176 {
  177   CRecordVector<CItem> _items;
  178   CMyComPtr<IInStream> _stream;
  179   Byte *_data;
  180   UInt32 _size;
  181   UInt32 _headersSize;
  182 
  183   UInt32 _errorFlags;
  184   bool _isArc;
  185 
  186   CHeader _h;
  187   UInt32 _phySize;
  188 
  189   unsigned _method;
  190   unsigned _blockSizeLog;
  191 
  192   // Current file
  193 
  194   NCompress::NZlib::CDecoder *_zlibDecoderSpec;
  195   CMyComPtr<ICompressCoder> _zlibDecoder;
  196 
  197   CBufInStream *_inStreamSpec;
  198   CMyComPtr<ISequentialInStream> _inStream;
  199 
  200   CBufPtrSeqOutStream *_outStreamSpec;
  201   CMyComPtr<ISequentialOutStream> _outStream;
  202 
  203   UInt32 _curBlocksOffset;
  204   UInt32 _curNumBlocks;
  205 
  206   HRESULT OpenDir(int parent, UInt32 baseOffsetBase, unsigned level);
  207   HRESULT Open2(IInStream *inStream);
  208   AString GetPath(int index) const;
  209   bool GetPackSize(int index, UInt32 &res) const;
  210   void Free();
  211 
  212   UInt32 GetNumBlocks(UInt32 size) const
  213   {
  214     return (size + ((UInt32)1 << _blockSizeLog) - 1) >> _blockSizeLog;
  215   }
  216 
  217   void UpdatePhySize(UInt32 s)
  218   {
  219     if (_phySize < s)
  220       _phySize = s;
  221   }
  222 
  223 public:
  224   CHandler(): _data(0) {}
  225   ~CHandler() { Free(); }
  226   MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
  227   INTERFACE_IInArchive(;)
  228   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
  229   HRESULT ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize);
  230 };
  231 
  232 static const Byte kProps[] =
  233 {
  234   kpidPath,
  235   kpidIsDir,
  236   kpidSize,
  237   kpidPackSize,
  238   kpidPosixAttrib
  239   // kpidOffset
  240 };
  241 
  242 static const Byte kArcProps[] =
  243 {
  244   kpidVolumeName,
  245   kpidBigEndian,
  246   kpidCharacts,
  247   kpidClusterSize,
  248   kpidMethod,
  249   kpidHeadersSize,
  250   kpidNumSubFiles,
  251   kpidNumBlocks
  252 };
  253 
  254 IMP_IInArchive_Props
  255 IMP_IInArchive_ArcProps
  256 
  257 HRESULT CHandler::OpenDir(int parent, UInt32 baseOffset, unsigned level)
  258 {
  259   const Byte *p = _data + baseOffset;
  260   bool be = _h.be;
  261   if (!IsDir(p, be))
  262     return S_OK;
  263   UInt32 offset = GetOffset(p, be);
  264   UInt32 size = GetSize(p, be);
  265   if (offset == 0 && size == 0)
  266     return S_OK;
  267   UInt32 end = offset + size;
  268   if (offset < kHeaderSize || end > _size || level > kNumDirLevelsMax)
  269     return S_FALSE;
  270   UpdatePhySize(end);
  271   if (end > _headersSize)
  272     _headersSize = end;
  273 
  274   unsigned startIndex = _items.Size();
  275   
  276   while (size != 0)
  277   {
  278     if (size < kNodeSize || (UInt32)_items.Size() >= kNumFilesMax)
  279       return S_FALSE;
  280     CItem item;
  281     item.Parent = parent;
  282     item.Offset = offset;
  283     _items.Add(item);
  284     UInt32 nodeLen = kNodeSize + GetNameLen(_data + offset, be);
  285     if (size < nodeLen)
  286       return S_FALSE;
  287     offset += nodeLen;
  288     size -= nodeLen;
  289   }
  290 
  291   unsigned endIndex = _items.Size();
  292   for (unsigned i = startIndex; i < endIndex; i++)
  293   {
  294     RINOK(OpenDir(i, _items[i].Offset, level + 1));
  295   }
  296   return S_OK;
  297 }
  298 
  299 HRESULT CHandler::Open2(IInStream *inStream)
  300 {
  301   Byte buf[kHeaderSize];
  302   RINOK(ReadStream_FALSE(inStream, buf, kHeaderSize));
  303   if (!_h.Parse(buf))
  304     return S_FALSE;
  305   _method = k_Flags_Method_ZLIB;
  306   _blockSizeLog = kBlockSizeLog;
  307   _phySize = kHeaderSize;
  308   if (_h.IsVer2())
  309   {
  310     _method = _h.GetMethod();
  311     // FIT IT. Now we don't know correct way to work with collision in method field.
  312     if (_method == k_Flags_Method_NONE)
  313       _method = k_Flags_Method_ZLIB;
  314     _blockSizeLog = kBlockSizeLog + _h.GetBlockSizeShift();
  315     if (_h.Size < kHeaderSize || _h.Size > kArcSizeMax || _h.NumFiles > kNumFilesMax)
  316       return S_FALSE;
  317     _phySize = _h.Size;
  318   }
  319   else
  320   {
  321     UInt64 size;
  322     RINOK(inStream->Seek(0, STREAM_SEEK_END, &size));
  323     if (size > kArcSizeMax)
  324       size = kArcSizeMax;
  325     _h.Size = (UInt32)size;
  326     RINOK(inStream->Seek(kHeaderSize, STREAM_SEEK_SET, NULL));
  327   }
  328   _data = (Byte *)MidAlloc(_h.Size);
  329   if (_data == 0)
  330     return E_OUTOFMEMORY;
  331   memcpy(_data, buf, kHeaderSize);
  332   size_t processed = _h.Size - kHeaderSize;
  333   RINOK(ReadStream(inStream, _data + kHeaderSize, &processed));
  334   if (processed < kNodeSize)
  335     return S_FALSE;
  336   _size = kHeaderSize + (UInt32)processed;
  337   if (_h.IsVer2())
  338   {
  339     if (_size != _h.Size)
  340       _errorFlags = kpv_ErrorFlags_UnexpectedEnd;
  341     else
  342     {
  343       SetUi32(_data + 0x20, 0);
  344       if (CrcCalc(_data, _h.Size) != _h.Crc)
  345       {
  346         _errorFlags = kpv_ErrorFlags_HeadersError;
  347         // _errorMessage = "CRC error";
  348       }
  349     }
  350     if (_h.NumFiles >= 1)
  351       _items.ClearAndReserve(_h.NumFiles - 1);
  352   }
  353   
  354   RINOK(OpenDir(-1, kHeaderSize, 0));
  355   
  356   if (!_h.IsVer2())
  357   {
  358     FOR_VECTOR (i, _items)
  359     {
  360       const CItem &item = _items[i];
  361       const Byte *p = _data + item.Offset;
  362       bool be = _h.be;
  363       if (IsDir(p, be))
  364         continue;
  365       UInt32 offset = GetOffset(p, be);
  366       if (offset < kHeaderSize)
  367         continue;
  368       UInt32 numBlocks = GetNumBlocks(GetSize(p, be));
  369       if (numBlocks == 0)
  370         continue;
  371       UInt32 start = offset + numBlocks * 4;
  372       if (start > _size)
  373         continue;
  374       UInt32 end = Get32(_data + start - 4);
  375       if (end >= start)
  376         UpdatePhySize(end);
  377     }
  378 
  379     // Read tailing zeros. Most cramfs archives use 4096-bytes aligned zeros
  380     const UInt32 kTailSize_MAX = 1 << 12;
  381     UInt32 endPos = (_phySize + kTailSize_MAX - 1) & ~(kTailSize_MAX - 1);
  382     if (endPos > _size)
  383       endPos = _size;
  384     UInt32 pos;
  385     for (pos = _phySize; pos < endPos && _data[pos] == 0; pos++);
  386     if (pos == endPos)
  387       _phySize = endPos;
  388   }
  389   return S_OK;
  390 }
  391 
  392 AString CHandler::GetPath(int index) const
  393 {
  394   unsigned len = 0;
  395   int indexMem = index;
  396   do
  397   {
  398     const CItem &item = _items[index];
  399     index = item.Parent;
  400     const Byte *p = _data + item.Offset;
  401     unsigned size = GetNameLen(p, _h.be);
  402     p += kNodeSize;
  403     unsigned i;
  404     for (i = 0; i < size && p[i]; i++);
  405     len += i + 1;
  406   }
  407   while (index >= 0);
  408   len--;
  409 
  410   AString path;
  411   char *dest = path.GetBuf_SetEnd(len) + len;
  412   index = indexMem;
  413   for (;;)
  414   {
  415     const CItem &item = _items[index];
  416     index = item.Parent;
  417     const Byte *p = _data + item.Offset;
  418     unsigned size = GetNameLen(p, _h.be);
  419     p += kNodeSize;
  420     unsigned i;
  421     for (i = 0; i < size && p[i]; i++);
  422     dest -= i;
  423     memcpy(dest, p, i);
  424     if (index < 0)
  425       break;
  426     *(--dest) = CHAR_PATH_SEPARATOR;
  427   }
  428   return path;
  429 }
  430 
  431 bool CHandler::GetPackSize(int index, UInt32 &res) const
  432 {
  433   res = 0;
  434   const CItem &item = _items[index];
  435   const Byte *p = _data + item.Offset;
  436   bool be = _h.be;
  437   UInt32 offset = GetOffset(p, be);
  438   if (offset < kHeaderSize)
  439     return false;
  440   UInt32 numBlocks = GetNumBlocks(GetSize(p, be));
  441   if (numBlocks == 0)
  442     return true;
  443   UInt32 start = offset + numBlocks * 4;
  444   if (start > _size)
  445     return false;
  446   UInt32 end = Get32(_data + start - 4);
  447   if (end < start)
  448     return false;
  449   res = end - start;
  450   return true;
  451 }
  452 
  453 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * /* callback */)
  454 {
  455   COM_TRY_BEGIN
  456   {
  457     Close();
  458     RINOK(Open2(stream));
  459     _isArc = true;
  460     _stream = stream;
  461   }
  462   return S_OK;
  463   COM_TRY_END
  464 }
  465 
  466 void CHandler::Free()
  467 {
  468   MidFree(_data);
  469   _data = 0;
  470 }
  471 
  472 STDMETHODIMP CHandler::Close()
  473 {
  474   _isArc = false;
  475   _phySize = 0;
  476   _errorFlags = 0;
  477   _headersSize = 0;
  478   _items.Clear();
  479   _stream.Release();
  480   Free();
  481   return S_OK;
  482 }
  483 
  484 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
  485 {
  486   *numItems = _items.Size();
  487   return S_OK;
  488 }
  489 
  490 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
  491 {
  492   COM_TRY_BEGIN
  493   NWindows::NCOM::CPropVariant prop;
  494   switch (propID)
  495   {
  496     case kpidVolumeName:
  497     {
  498       char dest[kHeaderNameSize + 4];
  499       memcpy(dest, _h.Name, kHeaderNameSize);
  500       dest[kHeaderNameSize] = 0;
  501       prop = dest;
  502       break;
  503     }
  504     case kpidBigEndian: prop = _h.be; break;
  505     case kpidCharacts: FLAGS_TO_PROP(k_Flags, _h.Flags, prop); break;
  506     case kpidMethod: prop = k_Methods[_method]; break;
  507     case kpidClusterSize: prop = (UInt32)1 << _blockSizeLog; break;
  508     case kpidNumBlocks: if (_h.IsVer2()) prop = _h.NumBlocks; break;
  509     case kpidNumSubFiles: if (_h.IsVer2()) prop = _h.NumFiles; break;
  510     case kpidPhySize: prop = _phySize; break;
  511     case kpidHeadersSize: prop = _headersSize; break;
  512     case kpidErrorFlags:
  513     {
  514       UInt32 v = _errorFlags;
  515       if (!_isArc)
  516         v |= kpv_ErrorFlags_IsNotArc;
  517       prop = v;
  518       break;
  519     }
  520   }
  521   prop.Detach(value);
  522   return S_OK;
  523   COM_TRY_END
  524 }
  525 
  526 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
  527 {
  528   COM_TRY_BEGIN
  529   NWindows::NCOM::CPropVariant prop;
  530   const CItem &item = _items[index];
  531   const Byte *p = _data + item.Offset;
  532   bool be = _h.be;
  533   bool isDir = IsDir(p, be);
  534   switch (propID)
  535   {
  536     case kpidPath: prop = MultiByteToUnicodeString(GetPath(index), CP_OEMCP); break;
  537     case kpidIsDir: prop = isDir; break;
  538     // case kpidOffset: prop = (UInt32)GetOffset(p, be); break;
  539     case kpidSize: if (!isDir) prop = GetSize(p, be); break;
  540     case kpidPackSize:
  541       if (!isDir)
  542       {
  543         UInt32 size;
  544         if (GetPackSize(index, size))
  545           prop = size;
  546       }
  547       break;
  548     case kpidPosixAttrib: prop = (UInt32)GetMode(p, be); break;
  549   }
  550   prop.Detach(value);
  551   return S_OK;
  552   COM_TRY_END
  553 }
  554 
  555 class CCramfsInStream: public CCachedInStream
  556 {
  557   HRESULT ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize);
  558 public:
  559   CHandler *Handler;
  560 };
  561 
  562 HRESULT CCramfsInStream::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize)
  563 {
  564   return Handler->ReadBlock(blockIndex, dest, blockSize);
  565 }
  566 
  567 HRESULT CHandler::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize)
  568 {
  569   if (_method == k_Flags_Method_ZLIB)
  570   {
  571     if (!_zlibDecoder)
  572     {
  573       _zlibDecoderSpec = new NCompress::NZlib::CDecoder();
  574       _zlibDecoder = _zlibDecoderSpec;
  575     }
  576   }
  577   else
  578   {
  579     if (_method != k_Flags_Method_LZMA)
  580     {
  581       // probably we must support no-compression archives here.
  582       return E_NOTIMPL;
  583     }
  584   }
  585 
  586   const bool be = _h.be;
  587   const Byte *p2 = _data + (_curBlocksOffset + (UInt32)blockIndex * 4);
  588   const UInt32 start = (blockIndex == 0 ? _curBlocksOffset + _curNumBlocks * 4: Get32(p2 - 4));
  589   const UInt32 end = Get32(p2);
  590   if (end < start || end > _size)
  591     return S_FALSE;
  592   const UInt32 inSize = end - start;
  593 
  594   if (_method == k_Flags_Method_LZMA)
  595   {
  596     const unsigned kLzmaHeaderSize = LZMA_PROPS_SIZE + 4;
  597     if (inSize < kLzmaHeaderSize)
  598       return S_FALSE;
  599     const Byte *p = _data + start;
  600     UInt32 destSize32 = GetUi32(p + LZMA_PROPS_SIZE);
  601     if (destSize32 > blockSize)
  602       return S_FALSE;
  603     SizeT destLen = destSize32;
  604     SizeT srcLen = inSize - kLzmaHeaderSize;
  605     ELzmaStatus status;
  606     SRes res = LzmaDecode(dest, &destLen, p + kLzmaHeaderSize, &srcLen,
  607         p, LZMA_PROPS_SIZE, LZMA_FINISH_END, &status, &g_Alloc);
  608     if (res != SZ_OK
  609         || (status != LZMA_STATUS_FINISHED_WITH_MARK &&
  610             status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)
  611         || destLen != destSize32
  612         || srcLen != inSize - kLzmaHeaderSize)
  613       return S_FALSE;
  614     return S_OK;
  615   }
  616 
  617   if (!_inStream)
  618   {
  619     _inStreamSpec = new CBufInStream();
  620     _inStream = _inStreamSpec;
  621   }
  622   if (!_outStream)
  623   {
  624     _outStreamSpec = new CBufPtrSeqOutStream();
  625     _outStream = _outStreamSpec;
  626   }
  627   _inStreamSpec->Init(_data + start, inSize);
  628   _outStreamSpec->Init(dest, blockSize);
  629   RINOK(_zlibDecoder->Code(_inStream, _outStream, NULL, NULL, NULL));
  630   return (inSize == _zlibDecoderSpec->GetInputProcessedSize() &&
  631       _outStreamSpec->GetPos() == blockSize) ? S_OK : S_FALSE;
  632 }
  633 
  634 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
  635     Int32 testMode, IArchiveExtractCallback *extractCallback)
  636 {
  637   COM_TRY_BEGIN
  638   bool allFilesMode = (numItems == (UInt32)(Int32)-1);
  639   if (allFilesMode)
  640     numItems = _items.Size();
  641   if (numItems == 0)
  642     return S_OK;
  643   bool be = _h.be;
  644   UInt64 totalSize = 0;
  645   UInt32 i;
  646   for (i = 0; i < numItems; i++)
  647   {
  648     const Byte *p = _data + _items[allFilesMode ? i : indices[i]].Offset;
  649     if (!IsDir(p, be))
  650       totalSize += GetSize(p, be);
  651   }
  652   extractCallback->SetTotal(totalSize);
  653 
  654   UInt64 totalPackSize;
  655   totalSize = totalPackSize = 0;
  656   
  657   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
  658   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
  659 
  660   CLocalProgress *lps = new CLocalProgress;
  661   CMyComPtr<ICompressProgressInfo> progress = lps;
  662   lps->Init(extractCallback, false);
  663 
  664   for (i = 0; i < numItems; i++)
  665   {
  666     lps->InSize = totalPackSize;
  667     lps->OutSize = totalSize;
  668     RINOK(lps->SetCur());
  669     CMyComPtr<ISequentialOutStream> outStream;
  670     Int32 askMode = testMode ?
  671         NExtract::NAskMode::kTest :
  672         NExtract::NAskMode::kExtract;
  673     UInt32 index = allFilesMode ? i : indices[i];
  674     const CItem &item = _items[index];
  675     RINOK(extractCallback->GetStream(index, &outStream, askMode));
  676     const Byte *p = _data + item.Offset;
  677 
  678     if (IsDir(p, be))
  679     {
  680       RINOK(extractCallback->PrepareOperation(askMode));
  681       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
  682       continue;
  683     }
  684     UInt32 curSize = GetSize(p, be);
  685     totalSize += curSize;
  686     UInt32 packSize;
  687     if (GetPackSize(index, packSize))
  688       totalPackSize += packSize;
  689 
  690     if (!testMode && !outStream)
  691       continue;
  692     RINOK(extractCallback->PrepareOperation(askMode));
  693 
  694     UInt32 offset = GetOffset(p, be);
  695     if (offset < kHeaderSize)
  696       curSize = 0;
  697 
  698     int res = NExtract::NOperationResult::kDataError;
  699     {
  700       CMyComPtr<ISequentialInStream> inSeqStream;
  701       HRESULT hres = GetStream(index, &inSeqStream);
  702       if (hres == E_OUTOFMEMORY)
  703         return E_OUTOFMEMORY;
  704       if (hres == S_FALSE || !inSeqStream)
  705         res = NExtract::NOperationResult::kUnsupportedMethod;
  706       else
  707       {
  708         RINOK(hres);
  709         {
  710           hres = copyCoder->Code(inSeqStream, outStream, NULL, NULL, progress);
  711           if (hres == S_OK)
  712           {
  713             if (copyCoderSpec->TotalSize == curSize)
  714               res = NExtract::NOperationResult::kOK;
  715           }
  716           else if (hres == E_NOTIMPL)
  717             res = NExtract::NOperationResult::kUnsupportedMethod;
  718           else if (hres != S_FALSE)
  719             return hres;
  720         }
  721       }
  722     }
  723     RINOK(extractCallback->SetOperationResult(res));
  724   }
  725 
  726   return S_OK;
  727   COM_TRY_END
  728 }
  729 
  730 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
  731 {
  732   COM_TRY_BEGIN
  733 
  734   const CItem &item = _items[index];
  735   const Byte *p = _data + item.Offset;
  736 
  737   bool be = _h.be;
  738   if (IsDir(p, be))
  739     return E_FAIL;
  740 
  741   UInt32 size = GetSize(p, be);
  742   UInt32 numBlocks = GetNumBlocks(size);
  743   UInt32 offset = GetOffset(p, be);
  744   if (offset < kHeaderSize)
  745   {
  746     if (offset != 0)
  747       return S_FALSE;
  748     CBufInStream *streamSpec = new CBufInStream;
  749     CMyComPtr<IInStream> streamTemp = streamSpec;
  750     streamSpec->Init(NULL, 0);
  751     *stream = streamTemp.Detach();
  752     return S_OK;
  753   }
  754 
  755   if (offset + numBlocks * 4 > _size)
  756     return S_FALSE;
  757   UInt32 prev = offset;
  758   for (UInt32 i = 0; i < numBlocks; i++)
  759   {
  760     UInt32 next = Get32(_data + offset + i * 4);
  761     if (next < prev || next > _size)
  762       return S_FALSE;
  763     prev = next;
  764   }
  765 
  766   CCramfsInStream *streamSpec = new CCramfsInStream;
  767   CMyComPtr<IInStream> streamTemp = streamSpec;
  768   _curNumBlocks = numBlocks;
  769   _curBlocksOffset = offset;
  770   streamSpec->Handler = this;
  771   if (!streamSpec->Alloc(_blockSizeLog, 21 - _blockSizeLog))
  772     return E_OUTOFMEMORY;
  773   streamSpec->Init(size);
  774   *stream = streamTemp.Detach();
  775 
  776   return S_OK;
  777   COM_TRY_END
  778 }
  779 
  780 REGISTER_ARC_I(
  781   "CramFS", "cramfs", 0, 0xD3,
  782   kSignature,
  783   16,
  784   0,
  785   NULL)
  786 
  787 }}