Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/kornelski/7z.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'CPP/7zip/Archive/CramfsHandler.cpp')
-rw-r--r--[-rwxr-xr-x]CPP/7zip/Archive/CramfsHandler.cpp283
1 files changed, 221 insertions, 62 deletions
diff --git a/CPP/7zip/Archive/CramfsHandler.cpp b/CPP/7zip/Archive/CramfsHandler.cpp
index a55e3743..3764f1af 100755..100644
--- a/CPP/7zip/Archive/CramfsHandler.cpp
+++ b/CPP/7zip/Archive/CramfsHandler.cpp
@@ -3,13 +3,14 @@
#include "StdAfx.h"
#include "../../../C/7zCrc.h"
-#include "../../../C/CpuArch.h"
#include "../../../C/Alloc.h"
+#include "../../../C/CpuArch.h"
+#include "../../../C/LzmaDec.h"
-#include "Common/ComTry.h"
-#include "Common/StringConvert.h"
+#include "../../Common/ComTry.h"
+#include "../../Common/StringConvert.h"
-#include "Windows/PropVariantUtils.h"
+#include "../../Windows/PropVariantUtils.h"
#include "../Common/LimitedStreams.h"
#include "../Common/ProgressUtils.h"
@@ -38,6 +39,30 @@ static const UInt32 kNodeSize = 12;
static const UInt32 kFlag_FsVer2 = (1 << 0);
+static const unsigned k_Flags_BlockSize_Shift = 11;
+static const unsigned k_Flags_BlockSize_Mask = 7;
+static const unsigned k_Flags_Method_Shift = 14;
+static const unsigned k_Flags_Method_Mask = 3;
+
+/*
+ There is possible collision in flags:
+ - Original CramFS writes 0 in method field. But it uses ZLIB.
+ - Modified CramFS writes 0 in method field for "NONE" compression?
+ How to solve that collision?
+*/
+
+#define k_Flags_Method_NONE 0
+#define k_Flags_Method_ZLIB 1
+#define k_Flags_Method_LZMA 2
+
+static const char *k_Methods[] =
+{
+ "Copy"
+ , "ZLIB"
+ , "LZMA"
+ , "Unknown"
+};
+
static const CUInt32PCharPair k_Flags[] =
{
{ 0, "Ver2" },
@@ -48,7 +73,6 @@ static const CUInt32PCharPair k_Flags[] =
};
static const unsigned kBlockSizeLog = 12;
-static const UInt32 kBlockSize = 1 << kBlockSizeLog;
/*
struct CNode
@@ -141,6 +165,8 @@ struct CHeader
}
bool IsVer2() const { return (Flags & kFlag_FsVer2) != 0; }
+ unsigned GetBlockSizeShift() const { return (unsigned)(Flags >> k_Flags_BlockSize_Shift) & k_Flags_BlockSize_Mask; }
+ unsigned GetMethod() const { return (unsigned)(Flags >> k_Flags_Method_Shift) & k_Flags_Method_Mask; }
};
class CHandler:
@@ -153,14 +179,21 @@ class CHandler:
Byte *_data;
UInt32 _size;
UInt32 _headersSize;
- AString _errorMessage;
+
+ UInt32 _errorFlags;
+ bool _isArc;
+
CHeader _h;
+ UInt32 _phySize;
+
+ unsigned _method;
+ unsigned _blockSizeLog;
// Current file
NCompress::NZlib::CDecoder *_zlibDecoderSpec;
CMyComPtr<ICompressCoder> _zlibDecoder;
-
+
CBufInStream *_inStreamSpec;
CMyComPtr<ISequentialInStream> _inStream;
@@ -175,6 +208,18 @@ class CHandler:
AString GetPath(int index) const;
bool GetPackSize(int index, UInt32 &res) const;
void Free();
+
+ UInt32 GetNumBlocks(UInt32 size) const
+ {
+ return (size + ((UInt32)1 << _blockSizeLog) - 1) >> _blockSizeLog;
+ }
+
+ void UpdatePhySize(UInt32 s)
+ {
+ if (_phySize < s)
+ _phySize = s;
+ }
+
public:
CHandler(): _data(0) {}
~CHandler() { Free(); }
@@ -184,25 +229,26 @@ public:
HRESULT ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize);
};
-static const STATPROPSTG kProps[] =
+static const Byte kProps[] =
{
- { NULL, kpidPath, VT_BSTR},
- { NULL, kpidIsDir, VT_BOOL},
- { NULL, kpidSize, VT_UI4},
- { NULL, kpidPackSize, VT_UI4},
- { NULL, kpidPosixAttrib, VT_UI4}
- // { NULL, kpidOffset, VT_UI4}
+ kpidPath,
+ kpidIsDir,
+ kpidSize,
+ kpidPackSize,
+ kpidPosixAttrib
+ // kpidOffset
};
-static const STATPROPSTG kArcProps[] =
+static const Byte kArcProps[] =
{
- { NULL, kpidName, VT_BSTR},
- { NULL, kpidBigEndian, VT_BOOL},
- { NULL, kpidCharacts, VT_BSTR},
- { NULL, kpidPhySize, VT_UI4},
- { NULL, kpidHeadersSize, VT_UI4},
- { NULL, kpidNumSubFiles, VT_UI4},
- { NULL, kpidNumBlocks, VT_UI4}
+ kpidVolumeName,
+ kpidBigEndian,
+ kpidCharacts,
+ kpidClusterSize,
+ kpidMethod,
+ kpidHeadersSize,
+ kpidNumSubFiles,
+ kpidNumBlocks
};
IMP_IInArchive_Props
@@ -221,10 +267,11 @@ HRESULT CHandler::OpenDir(int parent, UInt32 baseOffset, unsigned level)
UInt32 end = offset + size;
if (offset < kHeaderSize || end > _size || level > kNumDirLevelsMax)
return S_FALSE;
+ UpdatePhySize(end);
if (end > _headersSize)
_headersSize = end;
- int startIndex = _items.Size();
+ unsigned startIndex = _items.Size();
while (size != 0)
{
@@ -241,8 +288,8 @@ HRESULT CHandler::OpenDir(int parent, UInt32 baseOffset, unsigned level)
size -= nodeLen;
}
- int endIndex = _items.Size();
- for (int i = startIndex; i < endIndex; i++)
+ unsigned endIndex = _items.Size();
+ for (unsigned i = startIndex; i < endIndex; i++)
{
RINOK(OpenDir(i, _items[i].Offset, level + 1));
}
@@ -255,17 +302,26 @@ HRESULT CHandler::Open2(IInStream *inStream)
RINOK(ReadStream_FALSE(inStream, buf, kHeaderSize));
if (!_h.Parse(buf))
return S_FALSE;
+ _method = k_Flags_Method_ZLIB;
+ _blockSizeLog = kBlockSizeLog;
+ _phySize = kHeaderSize;
if (_h.IsVer2())
{
+ _method = _h.GetMethod();
+ // FIT IT. Now we don't know correct way to work with collision in method field.
+ if (_method == k_Flags_Method_NONE)
+ _method = k_Flags_Method_ZLIB;
+ _blockSizeLog = kBlockSizeLog + _h.GetBlockSizeShift();
if (_h.Size < kHeaderSize || _h.Size > kArcSizeMax || _h.NumFiles > kNumFilesMax)
return S_FALSE;
+ _phySize = _h.Size;
}
else
{
UInt64 size;
RINOK(inStream->Seek(0, STREAM_SEEK_END, &size));
if (size > kArcSizeMax)
- return S_FALSE;
+ size = kArcSizeMax;
_h.Size = (UInt32)size;
RINOK(inStream->Seek(kHeaderSize, STREAM_SEEK_SET, NULL));
}
@@ -278,18 +334,59 @@ HRESULT CHandler::Open2(IInStream *inStream)
if (processed < kNodeSize)
return S_FALSE;
_size = kHeaderSize + (UInt32)processed;
- if (_size != _h.Size)
- _errorMessage = "Unexpected end of archive";
- else
+ if (_h.IsVer2())
{
- SetUi32(_data + 0x20, 0);
- if (_h.IsVer2())
+ if (_size != _h.Size)
+ _errorFlags = kpv_ErrorFlags_UnexpectedEnd;
+ else
+ {
+ SetUi32(_data + 0x20, 0);
if (CrcCalc(_data, _h.Size) != _h.Crc)
- _errorMessage = "CRC error";
+ {
+ _errorFlags = kpv_ErrorFlags_HeadersError;
+ // _errorMessage = "CRC error";
+ }
+ }
+ if (_h.NumFiles >= 1)
+ _items.ClearAndReserve(_h.NumFiles - 1);
}
- if (_h.IsVer2())
- _items.Reserve(_h.NumFiles - 1);
- return OpenDir(-1, kHeaderSize, 0);
+
+ RINOK(OpenDir(-1, kHeaderSize, 0));
+
+ if (!_h.IsVer2())
+ {
+ FOR_VECTOR(i, _items)
+ {
+ const CItem &item = _items[i];
+ const Byte *p = _data + item.Offset;
+ bool be = _h.be;
+ if (IsDir(p, be))
+ continue;
+ UInt32 offset = GetOffset(p, be);
+ if (offset < kHeaderSize)
+ continue;
+ UInt32 numBlocks = GetNumBlocks(GetSize(p, be));
+ if (numBlocks == 0)
+ continue;
+ UInt32 start = offset + numBlocks * 4;
+ if (start > _size)
+ continue;
+ UInt32 end = Get32(_data + start - 4);
+ if (end >= start)
+ UpdatePhySize(end);
+ }
+
+ // Read tailing zeros. Most cramfs archives use 4096-bytes aligned zeros
+ const UInt32 kTailSize_MAX = 1 << 12;
+ UInt32 endPos = (_phySize + kTailSize_MAX - 1) & ~(kTailSize_MAX - 1);
+ if (endPos > _size)
+ endPos = _size;
+ UInt32 pos;
+ for (pos = _phySize; pos < endPos && _data[pos] == 0; pos++);
+ if (pos == endPos)
+ _phySize = endPos;
+ }
+ return S_OK;
}
AString CHandler::GetPath(int index) const
@@ -334,13 +431,16 @@ AString CHandler::GetPath(int index) const
bool CHandler::GetPackSize(int index, UInt32 &res) const
{
+ res = 0;
const CItem &item = _items[index];
const Byte *p = _data + item.Offset;
bool be = _h.be;
UInt32 offset = GetOffset(p, be);
if (offset < kHeaderSize)
return false;
- UInt32 numBlocks = (GetSize(p, be) + kBlockSize - 1) >> kBlockSizeLog;
+ UInt32 numBlocks = GetNumBlocks(GetSize(p, be));
+ if (numBlocks == 0)
+ return true;
UInt32 start = offset + numBlocks * 4;
if (start > _size)
return false;
@@ -357,6 +457,7 @@ STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallb
{
Close();
RINOK(Open2(stream));
+ _isArc = true;
_stream = stream;
}
return S_OK;
@@ -371,10 +472,12 @@ void CHandler::Free()
STDMETHODIMP CHandler::Close()
{
+ _isArc = false;
+ _phySize = 0;
+ _errorFlags = 0;
_headersSize = 0;
_items.Clear();
_stream.Release();
- _errorMessage.Empty();
Free();
return S_OK;
}
@@ -389,9 +492,9 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
{
COM_TRY_BEGIN
NWindows::NCOM::CPropVariant prop;
- switch(propID)
+ switch (propID)
{
- case kpidName:
+ case kpidVolumeName:
{
char dest[kHeaderNameSize + 4];
memcpy(dest, _h.Name, kHeaderNameSize);
@@ -401,11 +504,20 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
}
case kpidBigEndian: prop = _h.be; break;
case kpidCharacts: FLAGS_TO_PROP(k_Flags, _h.Flags, prop); break;
+ case kpidMethod: prop = k_Methods[_method]; break;
+ case kpidClusterSize: prop = (UInt32)1 << _blockSizeLog; break;
case kpidNumBlocks: if (_h.IsVer2()) prop = _h.NumBlocks; break;
case kpidNumSubFiles: if (_h.IsVer2()) prop = _h.NumFiles; break;
- case kpidPhySize: if (_h.IsVer2()) prop = _h.Size; break;
+ case kpidPhySize: prop = _phySize; break;
case kpidHeadersSize: prop = _headersSize; break;
- case kpidError: if (!_errorMessage.IsEmpty()) prop = _errorMessage; break;
+ case kpidErrorFlags:
+ {
+ UInt32 v = _errorFlags;
+ if (!_isArc)
+ v |= kpv_ErrorFlags_IsNotArc;
+ prop = v;
+ break;
+ }
}
prop.Detach(value);
return S_OK;
@@ -453,13 +565,60 @@ HRESULT CCramfsInStream::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSi
return Handler->ReadBlock(blockIndex, dest, blockSize);
}
+static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
+static void SzFree(void *p, void *address) { p = p; MyFree(address); }
+static ISzAlloc g_Alloc = { SzAlloc, SzFree };
+
HRESULT CHandler::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize)
{
- if (!_zlibDecoder)
+ if (_method == k_Flags_Method_ZLIB)
{
- _zlibDecoderSpec = new NCompress::NZlib::CDecoder();
- _zlibDecoder = _zlibDecoderSpec;
+ if (!_zlibDecoder)
+ {
+ _zlibDecoderSpec = new NCompress::NZlib::CDecoder();
+ _zlibDecoder = _zlibDecoderSpec;
+ }
}
+ else
+ {
+ if (_method != k_Flags_Method_LZMA)
+ {
+ // probably we must support no-compression archives here.
+ return E_NOTIMPL;
+ }
+ }
+
+ bool be = _h.be;
+ const Byte *p = _data + (_curBlocksOffset + (UInt32)blockIndex * 4);
+ UInt32 start = (blockIndex == 0 ? _curBlocksOffset + _curNumBlocks * 4: Get32(p - 4));
+ UInt32 end = Get32(p);
+ if (end < start || end > _size)
+ return S_FALSE;
+ UInt32 inSize = end - start;
+
+ if (_method == k_Flags_Method_LZMA)
+ {
+ const unsigned kLzmaHeaderSize = LZMA_PROPS_SIZE + 4;
+ if (inSize < kLzmaHeaderSize)
+ return S_FALSE;
+ const Byte *p = _data + start;
+ UInt32 destSize32 = GetUi32(p + LZMA_PROPS_SIZE);
+ if (destSize32 > blockSize)
+ return S_FALSE;
+ SizeT destLen = destSize32;
+ SizeT srcLen = inSize - kLzmaHeaderSize;
+ ELzmaStatus status;
+ SRes res = LzmaDecode(dest, &destLen, p + kLzmaHeaderSize, &srcLen,
+ p, LZMA_PROPS_SIZE, LZMA_FINISH_END, &status, &g_Alloc);
+ if (res != SZ_OK
+ || (status != LZMA_STATUS_FINISHED_WITH_MARK &&
+ status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK)
+ || destLen != destSize32
+ || srcLen != inSize - kLzmaHeaderSize)
+ return S_FALSE;
+ return S_OK;
+ }
+
if (!_inStream)
{
_inStreamSpec = new CBufInStream();
@@ -470,17 +629,10 @@ HRESULT CHandler::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize)
_outStreamSpec = new CBufPtrSeqOutStream();
_outStream = _outStreamSpec;
}
- bool be = _h.be;
- const Byte *p = _data + (_curBlocksOffset + (UInt32)blockIndex * 4);
- UInt32 start = (blockIndex == 0 ? _curBlocksOffset + _curNumBlocks * 4: Get32(p - 4));
- UInt32 end = Get32(p);
- if (end < start || end > _size)
- return S_FALSE;
- UInt32 inSize = end - start;
_inStreamSpec->Init(_data + start, inSize);
_outStreamSpec->Init(dest, blockSize);
RINOK(_zlibDecoder->Code(_inStream, _outStream, NULL, NULL, NULL));
- return (_zlibDecoderSpec->GetInputProcessedSize() == inSize &&
+ return (inSize == _zlibDecoderSpec->GetInputProcessedSize() &&
_outStreamSpec->GetPos() == blockSize) ? S_OK : S_FALSE;
}
@@ -488,7 +640,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
Int32 testMode, IArchiveExtractCallback *extractCallback)
{
COM_TRY_BEGIN
- bool allFilesMode = (numItems == (UInt32)-1);
+ bool allFilesMode = (numItems == (UInt32)(Int32)-1);
if (allFilesMode)
numItems = _items.Size();
if (numItems == 0)
@@ -562,19 +714,22 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
if (hres == E_OUTOFMEMORY)
return E_OUTOFMEMORY;
if (hres == S_FALSE || !inStream)
- res = NExtract::NOperationResult::kUnSupportedMethod;
+ res = NExtract::NOperationResult::kUnsupportedMethod;
else
{
RINOK(hres);
if (inStream)
{
HRESULT hres = copyCoder->Code(inStream, outStream, NULL, NULL, progress);
- if (hres != S_OK && hres != S_FALSE)
+ if (hres == S_OK)
{
- RINOK(hres);
+ if (copyCoderSpec->TotalSize == curSize)
+ res = NExtract::NOperationResult::kOK;
}
- if (copyCoderSpec->TotalSize == curSize && hres == S_OK)
- res = NExtract::NOperationResult::kOK;
+ else if (hres == E_NOTIMPL)
+ res = NExtract::NOperationResult::kUnsupportedMethod;
+ else if (hres != S_FALSE)
+ return hres;
}
}
}
@@ -596,7 +751,7 @@ STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
return E_FAIL;
UInt32 size = GetSize(p, be);
- UInt32 numBlocks = (size + kBlockSize - 1) >> kBlockSizeLog;
+ UInt32 numBlocks = GetNumBlocks(size);
UInt32 offset = GetOffset(p, be);
if (offset < kHeaderSize)
{
@@ -625,7 +780,7 @@ STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
_curNumBlocks = numBlocks;
_curBlocksOffset = offset;
streamSpec->Handler = this;
- if (!streamSpec->Alloc(kBlockSizeLog, 21 - kBlockSizeLog))
+ if (!streamSpec->Alloc(_blockSizeLog, 21 - _blockSizeLog))
return E_OUTOFMEMORY;
streamSpec->Init(size);
*stream = streamTemp.Detach();
@@ -634,10 +789,14 @@ STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
COM_TRY_END
}
-static IInArchive *CreateArc() { return new NArchive::NCramfs::CHandler; }
+IMP_CreateArcIn
static CArcInfo g_ArcInfo =
- { L"CramFS", L"cramfs", 0, 0xD3, SIGNATURE, kSignatureSize, false, CreateArc, 0 };
+ { "CramFS", "cramfs", 0, 0xD3,
+ kSignatureSize, SIGNATURE,
+ 16,
+ 0,
+ CreateArc };
REGISTER_ARC(Cramfs)