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