Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Archive / CramfsHandler.cpp
1 // CramfsHandler.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "../../../C/7zCrc.h"\r
6 #include "../../../C/CpuArch.h"\r
7 #include "../../../C/Alloc.h"\r
8 \r
9 #include "Common/ComTry.h"\r
10 #include "Common/StringConvert.h"\r
11 \r
12 #include "Windows/PropVariantUtils.h"\r
13 \r
14 #include "../Common/LimitedStreams.h"\r
15 #include "../Common/ProgressUtils.h"\r
16 #include "../Common/RegisterArc.h"\r
17 #include "../Common/StreamObjects.h"\r
18 #include "../Common/StreamUtils.h"\r
19 \r
20 #include "../Compress/CopyCoder.h"\r
21 #include "../Compress/ZlibDecoder.h"\r
22 \r
23 namespace NArchive {\r
24 namespace NCramfs {\r
25 \r
26 #define SIGNATURE { 'C','o','m','p','r','e','s','s','e','d',' ','R','O','M','F','S' }\r
27 \r
28 static const UInt32 kSignatureSize = 16;\r
29 static const char kSignature[kSignatureSize] = SIGNATURE;\r
30 \r
31 static const UInt32 kArcSizeMax = (256 + 16) << 20;\r
32 static const UInt32 kNumFilesMax = (1 << 19);\r
33 static const unsigned kNumDirLevelsMax = (1 << 8);\r
34 \r
35 static const UInt32 kHeaderSize = 0x40;\r
36 static const unsigned kHeaderNameSize = 16;\r
37 static const UInt32 kNodeSize = 12;\r
38 \r
39 static const UInt32 kFlag_FsVer2 = (1 << 0);\r
40 \r
41 static const CUInt32PCharPair k_Flags[] =\r
42 {\r
43   { 0, "Ver2" },\r
44   { 1, "SortedDirs" },\r
45   { 8, "Holes" },\r
46   { 9, "WrongSignature" },\r
47   { 10, "ShiftedRootOffset" }\r
48 };\r
49 \r
50 static const unsigned kBlockSizeLog = 12;\r
51 static const UInt32 kBlockSize = 1 << kBlockSizeLog;\r
52 \r
53 /*\r
54 struct CNode\r
55 {\r
56   UInt16 Mode;\r
57   UInt16 Uid;\r
58   UInt32 Size;\r
59   Byte Gid;\r
60   UInt32 NameLen;\r
61   UInt32 Offset;\r
62 \r
63   void Parse(const Byte *p)\r
64   {\r
65     Mode = GetUi16(p);\r
66     Uid = GetUi16(p + 2);\r
67     Size = Get32(p + 4) & 0xFFFFFF;\r
68     Gid = p[7];\r
69     NameLen = p[8] & 0x3F;\r
70     Offset = Get32(p + 8) >> 6;\r
71   }\r
72 };\r
73 */\r
74 \r
75 #define Get32(p) (be ? GetBe32(p) : GetUi32(p))\r
76 \r
77 static UInt32 GetMode(const Byte *p, bool be) { return be ? GetBe16(p) : GetUi16(p); }\r
78 static bool IsDir(const Byte *p, bool be) { return (GetMode(p, be) & 0xF000) == 0x4000; }\r
79 \r
80 static UInt32 GetSize(const Byte *p, bool be)\r
81 {\r
82   if (be)\r
83     return GetBe32(p + 4) >> 8;\r
84   else\r
85     return GetUi32(p + 4) & 0xFFFFFF;\r
86 }\r
87 \r
88 static UInt32 GetNameLen(const Byte *p, bool be)\r
89 {\r
90   if (be)\r
91     return (p[8] & 0xFC);\r
92   else\r
93     return (p[8] & 0x3F) << 2;\r
94 }\r
95 \r
96 static UInt32 GetOffset(const Byte *p, bool be)\r
97 {\r
98   if (be)\r
99     return (GetBe32(p + 8) & 0x03FFFFFF) << 2;\r
100   else\r
101     return GetUi32(p + 8) >> 6 << 2;\r
102 }\r
103 \r
104 struct CItem\r
105 {\r
106   UInt32 Offset;\r
107   int Parent;\r
108 };\r
109 \r
110 struct CHeader\r
111 {\r
112   bool be;\r
113   UInt32 Size;\r
114   UInt32 Flags;\r
115   // UInt32 Future;\r
116   UInt32 Crc;\r
117   // UInt32 Edition;\r
118   UInt32 NumBlocks;\r
119   UInt32 NumFiles;\r
120   char Name[kHeaderNameSize];\r
121 \r
122   bool Parse(const Byte *p)\r
123   {\r
124     if (memcmp(p + 16, kSignature, kSignatureSize) != 0)\r
125       return false;\r
126     switch(GetUi32(p))\r
127     {\r
128       case 0x28CD3D45: be = false; break;\r
129       case 0x453DCD28: be = true; break;\r
130       default: return false;\r
131     }\r
132     Size = Get32(p + 4);\r
133     Flags = Get32(p + 8);\r
134     // Future = Get32(p + 0xC);\r
135     Crc = Get32(p + 0x20);\r
136     // Edition = Get32(p + 0x24);\r
137     NumBlocks = Get32(p + 0x28);\r
138     NumFiles = Get32(p + 0x2C);\r
139     memcpy(Name, p + 0x30, kHeaderNameSize);\r
140     return true;\r
141   }\r
142 \r
143   bool IsVer2() const { return (Flags & kFlag_FsVer2) != 0; }\r
144 };\r
145 \r
146 class CHandler:\r
147   public IInArchive,\r
148   public IInArchiveGetStream,\r
149   public CMyUnknownImp\r
150 {\r
151   CRecordVector<CItem> _items;\r
152   CMyComPtr<IInStream> _stream;\r
153   Byte *_data;\r
154   UInt32 _size;\r
155   UInt32 _headersSize;\r
156   AString _errorMessage;\r
157   CHeader _h;\r
158 \r
159   // Current file\r
160 \r
161   NCompress::NZlib::CDecoder *_zlibDecoderSpec;\r
162   CMyComPtr<ICompressCoder> _zlibDecoder;\r
163   \r
164   CBufInStream *_inStreamSpec;\r
165   CMyComPtr<ISequentialInStream> _inStream;\r
166 \r
167   CBufPtrSeqOutStream *_outStreamSpec;\r
168   CMyComPtr<ISequentialOutStream> _outStream;\r
169 \r
170   UInt32 _curBlocksOffset;\r
171   UInt32 _curNumBlocks;\r
172 \r
173   HRESULT OpenDir(int parent, UInt32 baseOffsetBase, unsigned level);\r
174   HRESULT Open2(IInStream *inStream);\r
175   AString GetPath(int index) const;\r
176   bool GetPackSize(int index, UInt32 &res) const;\r
177   void Free();\r
178 public:\r
179   CHandler(): _data(0) {}\r
180   ~CHandler() { Free(); }\r
181   MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)\r
182   INTERFACE_IInArchive(;)\r
183   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);\r
184   HRESULT ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize);\r
185 };\r
186 \r
187 static const STATPROPSTG kProps[] =\r
188 {\r
189   { NULL, kpidPath, VT_BSTR},\r
190   { NULL, kpidIsDir, VT_BOOL},\r
191   { NULL, kpidSize, VT_UI4},\r
192   { NULL, kpidPackSize, VT_UI4},\r
193   { NULL, kpidPosixAttrib, VT_UI4}\r
194   // { NULL, kpidOffset, VT_UI4}\r
195 };\r
196 \r
197 static const STATPROPSTG kArcProps[] =\r
198 {\r
199   { NULL, kpidName, VT_BSTR},\r
200   { NULL, kpidBigEndian, VT_BOOL},\r
201   { NULL, kpidCharacts, VT_BSTR},\r
202   { NULL, kpidPhySize, VT_UI4},\r
203   { NULL, kpidHeadersSize, VT_UI4},\r
204   { NULL, kpidNumSubFiles, VT_UI4},\r
205   { NULL, kpidNumBlocks, VT_UI4}\r
206 };\r
207 \r
208 IMP_IInArchive_Props\r
209 IMP_IInArchive_ArcProps\r
210 \r
211 HRESULT CHandler::OpenDir(int parent, UInt32 baseOffset, unsigned level)\r
212 {\r
213   const Byte *p = _data + baseOffset;\r
214   bool be = _h.be;\r
215   if (!IsDir(p, be))\r
216     return S_OK;\r
217   UInt32 offset = GetOffset(p, be);\r
218   UInt32 size = GetSize(p, be);\r
219   if (offset == 0 && size == 0)\r
220     return S_OK;\r
221   UInt32 end = offset + size;\r
222   if (offset < kHeaderSize || end > _size || level > kNumDirLevelsMax)\r
223     return S_FALSE;\r
224   if (end > _headersSize)\r
225     _headersSize = end;\r
226 \r
227   int startIndex = _items.Size();\r
228   \r
229   while (size != 0)\r
230   {\r
231     if (size < kNodeSize || (UInt32)_items.Size() >= kNumFilesMax)\r
232       return S_FALSE;\r
233     CItem item;\r
234     item.Parent = parent;\r
235     item.Offset = offset;\r
236     _items.Add(item);\r
237     UInt32 nodeLen = kNodeSize + GetNameLen(_data + offset, be);\r
238     if (size < nodeLen)\r
239       return S_FALSE;\r
240     offset += nodeLen;\r
241     size -= nodeLen;\r
242   }\r
243 \r
244   int endIndex = _items.Size();\r
245   for (int i = startIndex; i < endIndex; i++)\r
246   {\r
247     RINOK(OpenDir(i, _items[i].Offset, level + 1));\r
248   }\r
249   return S_OK;\r
250 }\r
251 \r
252 HRESULT CHandler::Open2(IInStream *inStream)\r
253 {\r
254   Byte buf[kHeaderSize];\r
255   RINOK(ReadStream_FALSE(inStream, buf, kHeaderSize));\r
256   if (!_h.Parse(buf))\r
257     return S_FALSE;\r
258   if (_h.IsVer2())\r
259   {\r
260     if (_h.Size < kHeaderSize || _h.Size > kArcSizeMax || _h.NumFiles > kNumFilesMax)\r
261       return S_FALSE;\r
262   }\r
263   else\r
264   {\r
265     UInt64 size;\r
266     RINOK(inStream->Seek(0, STREAM_SEEK_END, &size));\r
267     if (size > kArcSizeMax)\r
268       return S_FALSE;\r
269     _h.Size = (UInt32)size;\r
270     RINOK(inStream->Seek(kHeaderSize, STREAM_SEEK_SET, NULL));\r
271   }\r
272   _data = (Byte *)MidAlloc(_h.Size);\r
273   if (_data == 0)\r
274     return E_OUTOFMEMORY;\r
275   memcpy(_data, buf, kHeaderSize);\r
276   size_t processed = _h.Size - kHeaderSize;\r
277   RINOK(ReadStream(inStream, _data + kHeaderSize, &processed));\r
278   if (processed < kNodeSize)\r
279     return S_FALSE;\r
280   _size = kHeaderSize + (UInt32)processed;\r
281   if (_size != _h.Size)\r
282     _errorMessage = "Unexpected end of archive";\r
283   else\r
284   {\r
285     SetUi32(_data + 0x20, 0);\r
286     if (_h.IsVer2())\r
287       if (CrcCalc(_data, _h.Size) != _h.Crc)\r
288         _errorMessage = "CRC error";\r
289   }\r
290   if (_h.IsVer2())\r
291     _items.Reserve(_h.NumFiles - 1);\r
292   return OpenDir(-1, kHeaderSize, 0);\r
293 }\r
294 \r
295 AString CHandler::GetPath(int index) const\r
296 {\r
297   unsigned len = 0;\r
298   int indexMem = index;\r
299   do\r
300   {\r
301     const CItem &item = _items[index];\r
302     index = item.Parent;\r
303     const Byte *p = _data + item.Offset;\r
304     unsigned size = GetNameLen(p, _h.be);\r
305     p += kNodeSize;\r
306     unsigned i;\r
307     for (i = 0; i < size && p[i]; i++);\r
308     len += i + 1;\r
309   }\r
310   while (index >= 0);\r
311   len--;\r
312 \r
313   AString path;\r
314   char *dest = path.GetBuffer(len) + len;\r
315   index = indexMem;\r
316   for (;;)\r
317   {\r
318     const CItem &item = _items[index];\r
319     index = item.Parent;\r
320     const Byte *p = _data + item.Offset;\r
321     unsigned size = GetNameLen(p, _h.be);\r
322     p += kNodeSize;\r
323     unsigned i;\r
324     for (i = 0; i < size && p[i]; i++);\r
325     dest -= i;\r
326     memcpy(dest, p, i);\r
327     if (index < 0)\r
328       break;\r
329     *(--dest) = CHAR_PATH_SEPARATOR;\r
330   }\r
331   path.ReleaseBuffer(len);\r
332   return path;\r
333 }\r
334 \r
335 bool CHandler::GetPackSize(int index, UInt32 &res) const\r
336 {\r
337   const CItem &item = _items[index];\r
338   const Byte *p = _data + item.Offset;\r
339   bool be = _h.be;\r
340   UInt32 offset = GetOffset(p, be);\r
341   if (offset < kHeaderSize)\r
342     return false;\r
343   UInt32 numBlocks = (GetSize(p, be) + kBlockSize - 1) >> kBlockSizeLog;\r
344   UInt32 start = offset + numBlocks * 4;\r
345   if (start > _size)\r
346     return false;\r
347   UInt32 end = Get32(_data + start - 4);\r
348   if (end < start)\r
349     return false;\r
350   res = end - start;\r
351   return true;\r
352 }\r
353 \r
354 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * /* callback */)\r
355 {\r
356   COM_TRY_BEGIN\r
357   {\r
358     Close();\r
359     RINOK(Open2(stream));\r
360     _stream = stream;\r
361   }\r
362   return S_OK;\r
363   COM_TRY_END\r
364 }\r
365 \r
366 void CHandler::Free()\r
367 {\r
368   MidFree(_data);\r
369   _data = 0;\r
370 }\r
371 \r
372 STDMETHODIMP CHandler::Close()\r
373 {\r
374   _headersSize = 0;\r
375   _items.Clear();\r
376   _stream.Release();\r
377   _errorMessage.Empty();\r
378   Free();\r
379   return S_OK;\r
380 }\r
381 \r
382 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)\r
383 {\r
384   *numItems = _items.Size();\r
385   return S_OK;\r
386 }\r
387 \r
388 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)\r
389 {\r
390   COM_TRY_BEGIN\r
391   NWindows::NCOM::CPropVariant prop;\r
392   switch(propID)\r
393   {\r
394     case kpidName:\r
395     {\r
396       char dest[kHeaderNameSize + 4];\r
397       memcpy(dest, _h.Name, kHeaderNameSize);\r
398       dest[kHeaderNameSize] = 0;\r
399       prop = dest;\r
400       break;\r
401     }\r
402     case kpidBigEndian: prop = _h.be; break;\r
403     case kpidCharacts: FLAGS_TO_PROP(k_Flags, _h.Flags, prop); break;\r
404     case kpidNumBlocks: if (_h.IsVer2()) prop = _h.NumBlocks; break;\r
405     case kpidNumSubFiles: if (_h.IsVer2()) prop = _h.NumFiles; break;\r
406     case kpidPhySize: if (_h.IsVer2()) prop = _h.Size; break;\r
407     case kpidHeadersSize: prop = _headersSize; break;\r
408     case kpidError: if (!_errorMessage.IsEmpty()) prop = _errorMessage; break;\r
409   }\r
410   prop.Detach(value);\r
411   return S_OK;\r
412   COM_TRY_END\r
413 }\r
414 \r
415 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)\r
416 {\r
417   COM_TRY_BEGIN\r
418   NWindows::NCOM::CPropVariant prop;\r
419   const CItem &item = _items[index];\r
420   const Byte *p = _data + item.Offset;\r
421   bool be = _h.be;\r
422   bool isDir = IsDir(p, be);\r
423   switch(propID)\r
424   {\r
425     case kpidPath: prop = MultiByteToUnicodeString(GetPath(index), CP_OEMCP); break;\r
426     case kpidIsDir: prop = isDir; break;\r
427     // case kpidOffset: prop = (UInt32)GetOffset(p, be); break;\r
428     case kpidSize: if (!isDir) prop = GetSize(p, be); break;\r
429     case kpidPackSize:\r
430       if (!isDir)\r
431       {\r
432         UInt32 size;\r
433         if (GetPackSize(index, size))\r
434           prop = size;\r
435       }\r
436       break;\r
437     case kpidPosixAttrib: prop = (UInt32)GetMode(p, be); break;\r
438   }\r
439   prop.Detach(value);\r
440   return S_OK;\r
441   COM_TRY_END\r
442 }\r
443 \r
444 class CCramfsInStream: public CCachedInStream\r
445 {\r
446   HRESULT ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize);\r
447 public:\r
448   CHandler *Handler;\r
449 };\r
450 \r
451 HRESULT CCramfsInStream::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize)\r
452 {\r
453   return Handler->ReadBlock(blockIndex, dest, blockSize);\r
454 }\r
455 \r
456 HRESULT CHandler::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize)\r
457 {\r
458   if (!_zlibDecoder)\r
459   {\r
460     _zlibDecoderSpec = new NCompress::NZlib::CDecoder();\r
461     _zlibDecoder = _zlibDecoderSpec;\r
462   }\r
463   if (!_inStream)\r
464   {\r
465     _inStreamSpec = new CBufInStream();\r
466     _inStream = _inStreamSpec;\r
467   }\r
468   if (!_outStream)\r
469   {\r
470     _outStreamSpec = new CBufPtrSeqOutStream();\r
471     _outStream = _outStreamSpec;\r
472   }\r
473   bool be = _h.be;\r
474   const Byte *p = _data + (_curBlocksOffset + (UInt32)blockIndex * 4);\r
475   UInt32 start = (blockIndex == 0 ? _curBlocksOffset + _curNumBlocks * 4: Get32(p - 4));\r
476   UInt32 end = Get32(p);\r
477   if (end < start || end > _size)\r
478     return S_FALSE;\r
479   UInt32 inSize = end - start;\r
480   _inStreamSpec->Init(_data + start, inSize);\r
481   _outStreamSpec->Init(dest, blockSize);\r
482   RINOK(_zlibDecoder->Code(_inStream, _outStream, NULL, NULL, NULL));\r
483   return (_zlibDecoderSpec->GetInputProcessedSize() == inSize &&\r
484       _outStreamSpec->GetPos() == blockSize) ? S_OK : S_FALSE;\r
485 }\r
486 \r
487 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,\r
488     Int32 testMode, IArchiveExtractCallback *extractCallback)\r
489 {\r
490   COM_TRY_BEGIN\r
491   bool allFilesMode = (numItems == (UInt32)-1);\r
492   if (allFilesMode)\r
493     numItems = _items.Size();\r
494   if (numItems == 0)\r
495     return S_OK;\r
496   bool be = _h.be;\r
497   UInt64 totalSize = 0;\r
498   UInt32 i;\r
499   for (i = 0; i < numItems; i++)\r
500   {\r
501     const Byte *p = _data + _items[allFilesMode ? i : indices[i]].Offset;\r
502     if (!IsDir(p, be))\r
503       totalSize += GetSize(p, be);\r
504   }\r
505   extractCallback->SetTotal(totalSize);\r
506 \r
507   UInt64 totalPackSize;\r
508   totalSize = totalPackSize = 0;\r
509   \r
510   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();\r
511   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;\r
512 \r
513   CLocalProgress *lps = new CLocalProgress;\r
514   CMyComPtr<ICompressProgressInfo> progress = lps;\r
515   lps->Init(extractCallback, false);\r
516 \r
517   CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;\r
518   CMyComPtr<ISequentialInStream> inStream(streamSpec);\r
519   streamSpec->SetStream(_stream);\r
520 \r
521   for (i = 0; i < numItems; i++)\r
522   {\r
523     lps->InSize = totalPackSize;\r
524     lps->OutSize = totalSize;\r
525     RINOK(lps->SetCur());\r
526     CMyComPtr<ISequentialOutStream> outStream;\r
527     Int32 askMode = testMode ?\r
528         NExtract::NAskMode::kTest :\r
529         NExtract::NAskMode::kExtract;\r
530     UInt32 index = allFilesMode ? i : indices[i];\r
531     const CItem &item = _items[index];\r
532     RINOK(extractCallback->GetStream(index, &outStream, askMode));\r
533     const Byte *p = _data + item.Offset;\r
534 \r
535     if (IsDir(p, be))\r
536     {\r
537       RINOK(extractCallback->PrepareOperation(askMode));\r
538       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));\r
539       continue;\r
540     }\r
541     UInt32 curSize = GetSize(p, be);\r
542     totalSize += curSize;\r
543     UInt32 packSize;\r
544     if (GetPackSize(index, packSize))\r
545       totalPackSize += packSize;\r
546 \r
547     if (!testMode && !outStream)\r
548       continue;\r
549     RINOK(extractCallback->PrepareOperation(askMode));\r
550 \r
551     UInt32 offset = GetOffset(p, be);\r
552     if (offset < kHeaderSize)\r
553       curSize = 0;\r
554 \r
555     int res = NExtract::NOperationResult::kDataError;\r
556     {\r
557       CMyComPtr<ISequentialInStream> inSeqStream;\r
558       CMyComPtr<IInStream> inStream;\r
559       HRESULT hres = GetStream(index, &inSeqStream);\r
560       if (inSeqStream)\r
561         inSeqStream.QueryInterface(IID_IInStream, &inStream);\r
562       if (hres == E_OUTOFMEMORY)\r
563         return E_OUTOFMEMORY;\r
564       if (hres == S_FALSE || !inStream)\r
565         res = NExtract::NOperationResult::kUnSupportedMethod;\r
566       else\r
567       {\r
568         RINOK(hres);\r
569         if (inStream)\r
570         {\r
571           HRESULT hres = copyCoder->Code(inStream, outStream, NULL, NULL, progress);\r
572           if (hres != S_OK && hres != S_FALSE)\r
573           {\r
574             RINOK(hres);\r
575           }\r
576           if (copyCoderSpec->TotalSize == curSize && hres == S_OK)\r
577             res = NExtract::NOperationResult::kOK;\r
578         }\r
579       }\r
580     }\r
581     RINOK(extractCallback->SetOperationResult(res));\r
582   }\r
583   return S_OK;\r
584   COM_TRY_END\r
585 }\r
586 \r
587 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)\r
588 {\r
589   COM_TRY_BEGIN\r
590 \r
591   const CItem &item = _items[index];\r
592   const Byte *p = _data + item.Offset;\r
593 \r
594   bool be = _h.be;\r
595   if (IsDir(p, be))\r
596     return E_FAIL;\r
597 \r
598   UInt32 size = GetSize(p, be);\r
599   UInt32 numBlocks = (size + kBlockSize - 1) >> kBlockSizeLog;\r
600   UInt32 offset = GetOffset(p, be);\r
601   if (offset < kHeaderSize)\r
602   {\r
603     if (offset != 0)\r
604       return S_FALSE;\r
605     CBufInStream *streamSpec = new CBufInStream;\r
606     CMyComPtr<IInStream> streamTemp = streamSpec;\r
607     streamSpec->Init(NULL, 0);\r
608     *stream = streamTemp.Detach();\r
609     return S_OK;\r
610   }\r
611 \r
612   if (offset + numBlocks * 4 > _size)\r
613     return S_FALSE;\r
614   UInt32 prev = offset;\r
615   for (UInt32 i = 0; i < numBlocks; i++)\r
616   {\r
617     UInt32 next = Get32(_data + offset + i * 4);\r
618     if (next < prev || next > _size)\r
619       return S_FALSE;\r
620     prev = next;\r
621   }\r
622 \r
623   CCramfsInStream *streamSpec = new CCramfsInStream;\r
624   CMyComPtr<IInStream> streamTemp = streamSpec;\r
625   _curNumBlocks = numBlocks;\r
626   _curBlocksOffset = offset;\r
627   streamSpec->Handler = this;\r
628   if (!streamSpec->Alloc(kBlockSizeLog, 21 - kBlockSizeLog))\r
629     return E_OUTOFMEMORY;\r
630   streamSpec->Init(size);\r
631   *stream = streamTemp.Detach();\r
632 \r
633   return S_OK;\r
634   COM_TRY_END\r
635 }\r
636 \r
637 static IInArchive *CreateArc() { return new NArchive::NCramfs::CHandler; }\r
638 \r
639 static CArcInfo g_ArcInfo =\r
640   { L"CramFS", L"cramfs", 0, 0xD3, SIGNATURE, kSignatureSize, false, CreateArc, 0 };\r
641 \r
642 REGISTER_ARC(Cramfs)\r
643 \r
644 }}\r