5 #include "../../../C/7zCrc.h"
\r
6 #include "../../../C/CpuArch.h"
\r
7 #include "../../../C/Alloc.h"
\r
9 #include "Common/ComTry.h"
\r
10 #include "Common/StringConvert.h"
\r
12 #include "Windows/PropVariantUtils.h"
\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
20 #include "../Compress/CopyCoder.h"
\r
21 #include "../Compress/ZlibDecoder.h"
\r
23 namespace NArchive {
\r
26 #define SIGNATURE { 'C','o','m','p','r','e','s','s','e','d',' ','R','O','M','F','S' }
\r
28 static const UInt32 kSignatureSize = 16;
\r
29 static const char kSignature[kSignatureSize] = SIGNATURE;
\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
35 static const UInt32 kHeaderSize = 0x40;
\r
36 static const unsigned kHeaderNameSize = 16;
\r
37 static const UInt32 kNodeSize = 12;
\r
39 static const UInt32 kFlag_FsVer2 = (1 << 0);
\r
41 static const CUInt32PCharPair k_Flags[] =
\r
44 { 1, "SortedDirs" },
\r
46 { 9, "WrongSignature" },
\r
47 { 10, "ShiftedRootOffset" }
\r
50 static const unsigned kBlockSizeLog = 12;
\r
51 static const UInt32 kBlockSize = 1 << kBlockSizeLog;
\r
63 void Parse(const Byte *p)
\r
66 Uid = GetUi16(p + 2);
\r
67 Size = Get32(p + 4) & 0xFFFFFF;
\r
69 NameLen = p[8] & 0x3F;
\r
70 Offset = Get32(p + 8) >> 6;
\r
75 #define Get32(p) (be ? GetBe32(p) : GetUi32(p))
\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
80 static UInt32 GetSize(const Byte *p, bool be)
\r
83 return GetBe32(p + 4) >> 8;
\r
85 return GetUi32(p + 4) & 0xFFFFFF;
\r
88 static UInt32 GetNameLen(const Byte *p, bool be)
\r
91 return (p[8] & 0xFC);
\r
93 return (p[8] & 0x3F) << 2;
\r
96 static UInt32 GetOffset(const Byte *p, bool be)
\r
99 return (GetBe32(p + 8) & 0x03FFFFFF) << 2;
\r
101 return GetUi32(p + 8) >> 6 << 2;
\r
120 char Name[kHeaderNameSize];
\r
122 bool Parse(const Byte *p)
\r
124 if (memcmp(p + 16, kSignature, kSignatureSize) != 0)
\r
128 case 0x28CD3D45: be = false; break;
\r
129 case 0x453DCD28: be = true; break;
\r
130 default: return false;
\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
143 bool IsVer2() const { return (Flags & kFlag_FsVer2) != 0; }
\r
148 public IInArchiveGetStream,
\r
149 public CMyUnknownImp
\r
151 CRecordVector<CItem> _items;
\r
152 CMyComPtr<IInStream> _stream;
\r
155 UInt32 _headersSize;
\r
156 AString _errorMessage;
\r
161 NCompress::NZlib::CDecoder *_zlibDecoderSpec;
\r
162 CMyComPtr<ICompressCoder> _zlibDecoder;
\r
164 CBufInStream *_inStreamSpec;
\r
165 CMyComPtr<ISequentialInStream> _inStream;
\r
167 CBufPtrSeqOutStream *_outStreamSpec;
\r
168 CMyComPtr<ISequentialOutStream> _outStream;
\r
170 UInt32 _curBlocksOffset;
\r
171 UInt32 _curNumBlocks;
\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
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
187 static const STATPROPSTG kProps[] =
\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
197 static const STATPROPSTG kArcProps[] =
\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
208 IMP_IInArchive_Props
\r
209 IMP_IInArchive_ArcProps
\r
211 HRESULT CHandler::OpenDir(int parent, UInt32 baseOffset, unsigned level)
\r
213 const Byte *p = _data + baseOffset;
\r
217 UInt32 offset = GetOffset(p, be);
\r
218 UInt32 size = GetSize(p, be);
\r
219 if (offset == 0 && size == 0)
\r
221 UInt32 end = offset + size;
\r
222 if (offset < kHeaderSize || end > _size || level > kNumDirLevelsMax)
\r
224 if (end > _headersSize)
\r
225 _headersSize = end;
\r
227 int startIndex = _items.Size();
\r
231 if (size < kNodeSize || (UInt32)_items.Size() >= kNumFilesMax)
\r
234 item.Parent = parent;
\r
235 item.Offset = offset;
\r
237 UInt32 nodeLen = kNodeSize + GetNameLen(_data + offset, be);
\r
238 if (size < nodeLen)
\r
244 int endIndex = _items.Size();
\r
245 for (int i = startIndex; i < endIndex; i++)
\r
247 RINOK(OpenDir(i, _items[i].Offset, level + 1));
\r
252 HRESULT CHandler::Open2(IInStream *inStream)
\r
254 Byte buf[kHeaderSize];
\r
255 RINOK(ReadStream_FALSE(inStream, buf, kHeaderSize));
\r
256 if (!_h.Parse(buf))
\r
260 if (_h.Size < kHeaderSize || _h.Size > kArcSizeMax || _h.NumFiles > kNumFilesMax)
\r
266 RINOK(inStream->Seek(0, STREAM_SEEK_END, &size));
\r
267 if (size > kArcSizeMax)
\r
269 _h.Size = (UInt32)size;
\r
270 RINOK(inStream->Seek(kHeaderSize, STREAM_SEEK_SET, NULL));
\r
272 _data = (Byte *)MidAlloc(_h.Size);
\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
280 _size = kHeaderSize + (UInt32)processed;
\r
281 if (_size != _h.Size)
\r
282 _errorMessage = "Unexpected end of archive";
\r
285 SetUi32(_data + 0x20, 0);
\r
287 if (CrcCalc(_data, _h.Size) != _h.Crc)
\r
288 _errorMessage = "CRC error";
\r
291 _items.Reserve(_h.NumFiles - 1);
\r
292 return OpenDir(-1, kHeaderSize, 0);
\r
295 AString CHandler::GetPath(int index) const
\r
298 int indexMem = index;
\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
307 for (i = 0; i < size && p[i]; i++);
\r
310 while (index >= 0);
\r
314 char *dest = path.GetBuffer(len) + len;
\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
324 for (i = 0; i < size && p[i]; i++);
\r
326 memcpy(dest, p, i);
\r
329 *(--dest) = CHAR_PATH_SEPARATOR;
\r
331 path.ReleaseBuffer(len);
\r
335 bool CHandler::GetPackSize(int index, UInt32 &res) const
\r
337 const CItem &item = _items[index];
\r
338 const Byte *p = _data + item.Offset;
\r
340 UInt32 offset = GetOffset(p, be);
\r
341 if (offset < kHeaderSize)
\r
343 UInt32 numBlocks = (GetSize(p, be) + kBlockSize - 1) >> kBlockSizeLog;
\r
344 UInt32 start = offset + numBlocks * 4;
\r
347 UInt32 end = Get32(_data + start - 4);
\r
354 STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback * /* callback */)
\r
359 RINOK(Open2(stream));
\r
366 void CHandler::Free()
\r
372 STDMETHODIMP CHandler::Close()
\r
377 _errorMessage.Empty();
\r
382 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
\r
384 *numItems = _items.Size();
\r
388 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
\r
391 NWindows::NCOM::CPropVariant prop;
\r
396 char dest[kHeaderNameSize + 4];
\r
397 memcpy(dest, _h.Name, kHeaderNameSize);
\r
398 dest[kHeaderNameSize] = 0;
\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
410 prop.Detach(value);
\r
415 STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
\r
418 NWindows::NCOM::CPropVariant prop;
\r
419 const CItem &item = _items[index];
\r
420 const Byte *p = _data + item.Offset;
\r
422 bool isDir = IsDir(p, be);
\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
433 if (GetPackSize(index, size))
\r
437 case kpidPosixAttrib: prop = (UInt32)GetMode(p, be); break;
\r
439 prop.Detach(value);
\r
444 class CCramfsInStream: public CCachedInStream
\r
446 HRESULT ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize);
\r
451 HRESULT CCramfsInStream::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize)
\r
453 return Handler->ReadBlock(blockIndex, dest, blockSize);
\r
456 HRESULT CHandler::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize)
\r
460 _zlibDecoderSpec = new NCompress::NZlib::CDecoder();
\r
461 _zlibDecoder = _zlibDecoderSpec;
\r
465 _inStreamSpec = new CBufInStream();
\r
466 _inStream = _inStreamSpec;
\r
470 _outStreamSpec = new CBufPtrSeqOutStream();
\r
471 _outStream = _outStreamSpec;
\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
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
487 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
\r
488 Int32 testMode, IArchiveExtractCallback *extractCallback)
\r
491 bool allFilesMode = (numItems == (UInt32)-1);
\r
493 numItems = _items.Size();
\r
497 UInt64 totalSize = 0;
\r
499 for (i = 0; i < numItems; i++)
\r
501 const Byte *p = _data + _items[allFilesMode ? i : indices[i]].Offset;
\r
503 totalSize += GetSize(p, be);
\r
505 extractCallback->SetTotal(totalSize);
\r
507 UInt64 totalPackSize;
\r
508 totalSize = totalPackSize = 0;
\r
510 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
\r
511 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
\r
513 CLocalProgress *lps = new CLocalProgress;
\r
514 CMyComPtr<ICompressProgressInfo> progress = lps;
\r
515 lps->Init(extractCallback, false);
\r
517 CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
\r
518 CMyComPtr<ISequentialInStream> inStream(streamSpec);
\r
519 streamSpec->SetStream(_stream);
\r
521 for (i = 0; i < numItems; i++)
\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
537 RINOK(extractCallback->PrepareOperation(askMode));
\r
538 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK));
\r
541 UInt32 curSize = GetSize(p, be);
\r
542 totalSize += curSize;
\r
544 if (GetPackSize(index, packSize))
\r
545 totalPackSize += packSize;
\r
547 if (!testMode && !outStream)
\r
549 RINOK(extractCallback->PrepareOperation(askMode));
\r
551 UInt32 offset = GetOffset(p, be);
\r
552 if (offset < kHeaderSize)
\r
555 int res = NExtract::NOperationResult::kDataError;
\r
557 CMyComPtr<ISequentialInStream> inSeqStream;
\r
558 CMyComPtr<IInStream> inStream;
\r
559 HRESULT hres = GetStream(index, &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
571 HRESULT hres = copyCoder->Code(inStream, outStream, NULL, NULL, progress);
\r
572 if (hres != S_OK && hres != S_FALSE)
\r
576 if (copyCoderSpec->TotalSize == curSize && hres == S_OK)
\r
577 res = NExtract::NOperationResult::kOK;
\r
581 RINOK(extractCallback->SetOperationResult(res));
\r
587 STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
\r
591 const CItem &item = _items[index];
\r
592 const Byte *p = _data + item.Offset;
\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
605 CBufInStream *streamSpec = new CBufInStream;
\r
606 CMyComPtr<IInStream> streamTemp = streamSpec;
\r
607 streamSpec->Init(NULL, 0);
\r
608 *stream = streamTemp.Detach();
\r
612 if (offset + numBlocks * 4 > _size)
\r
614 UInt32 prev = offset;
\r
615 for (UInt32 i = 0; i < numBlocks; i++)
\r
617 UInt32 next = Get32(_data + offset + i * 4);
\r
618 if (next < prev || next > _size)
\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
637 static IInArchive *CreateArc() { return new NArchive::NCramfs::CHandler; }
\r
639 static CArcInfo g_ArcInfo =
\r
640 { L"CramFS", L"cramfs", 0, 0xD3, SIGNATURE, kSignatureSize, false, CreateArc, 0 };
\r
642 REGISTER_ARC(Cramfs)
\r