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