Compress-Deflate7
view release on metacpan or search on metacpan
7zip/CPP/7zip/Archive/SquashfsHandler.cpp view on Meta::CPAN
case kType_BLK: case kType_BLK + 7:
case kType_CHR: case kType_CHR + 7:
if (size < 24)
return 0;
// LE_32 (20, RDev);
offset = 24;
break;
default:
return 0;
}
if (Type >= 8)
{
if (size < offset + 4)
return 0;
// LE_32 (offset, Xattr);
offset += 4;
}
return offset;
}
struct CItem
{
int Node;
int Parent;
UInt32 Ptr;
};
struct CData
{
CByteBuffer Data;
CRecordVector<UInt32> PackPos;
CRecordVector<UInt32> UnpackPos; // additional item at the end contains TotalUnpackSize
UInt32 GetNumBlocks() const { return PackPos.Size(); }
void Clear()
{
Data.Free();
PackPos.Clear();
UnpackPos.Clear();
}
};
struct CFrag
{
UInt64 StartBlock;
UInt32 Size;
};
class CHandler:
public IInArchive,
public IInArchiveGetStream,
public CMyUnknownImp
{
CRecordVector<CItem> _items;
CRecordVector<CNode> _nodes;
CRecordVector<UInt32> _nodesPos;
CRecordVector<UInt32> _blockToNode;
CData _inodesData;
CData _dirs;
CRecordVector<CFrag> _frags;
// CByteBuffer _uids;
// CByteBuffer _gids;
CHeader _h;
CMyComPtr<IInStream> _stream;
UInt64 _sizeCalculated;
IArchiveOpenCallback *_openCallback;
int _nodeIndex;
CRecordVector<bool> _blockCompressed;
CRecordVector<UInt64> _blockOffsets;
CByteBuffer _cachedBlock;
UInt64 _cachedBlockStartPos;
UInt32 _cachedPackBlockSize;
UInt32 _cachedUnpackBlockSize;
CLimitedSequentialInStream *_limitedInStreamSpec;
CMyComPtr<ISequentialInStream> _limitedInStream;
CBufPtrSeqOutStream *_outStreamSpec;
CMyComPtr<ISequentialOutStream> _outStream;
NCompress::NLzma::CDecoder *_lzmaDecoderSpec;
CMyComPtr<ICompressCoder> _lzmaDecoder;
NCompress::NZlib::CDecoder *_zlibDecoderSpec;
CMyComPtr<ICompressCoder> _zlibDecoder;
CByteBuffer _inputBuffer;
CDynBufSeqOutStream *_dynOutStreamSpec;
CMyComPtr<ISequentialOutStream> _dynOutStream;
void ClearCache()
{
_cachedBlockStartPos = 0;
_cachedPackBlockSize = 0;
_cachedUnpackBlockSize = 0;
}
HRESULT Decompress(ISequentialOutStream *outStream, Byte *outBuf, bool *outBufWasWritten, UInt32 *outBufWasWrittenSize,
UInt32 inSize, UInt32 outSizeMax);
HRESULT ReadMetadataBlock(UInt32 &packSize);
HRESULT ReadData(CData &data, UInt64 start, UInt64 end);
HRESULT OpenDir(int parent, UInt32 startBlock, UInt32 offset, unsigned level, int &nodeIndex);
HRESULT ScanInodes(UInt64 ptr);
// HRESULT ReadUids(UInt64 start, UInt32 num, CByteBuffer &ids);
HRESULT Open2(IInStream *inStream);
AString GetPath(int index) const;
bool GetPackSize(int index, UInt64 &res, bool fillOffsets);
public:
CHandler();
MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
INTERFACE_IInArchive(;)
STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
7zip/CPP/7zip/Archive/SquashfsHandler.cpp view on Meta::CPAN
size++;
if (rem < size)
return S_FALSE;
p += size;
rem -= size;
item.Parent = parent;
_items.Add(item);
tempItems.Add(tempItem);
}
}
int startItemIndex = _items.Size() - tempItems.Size();
for (int i = 0; i < tempItems.Size(); i++)
{
const CTempItem &tempItem = tempItems[i];
int index = startItemIndex + i;
CItem &item = _items[index];
RINOK(OpenDir(index, tempItem.StartBlock, tempItem.Offset, level + 1, item.Node));
}
return S_OK;
}
/*
HRESULT CHandler::ReadUids(UInt64 start, UInt32 num, CByteBuffer &ids)
{
size_t size = num * 4;
ids.SetCapacity(size);
RINOK(_stream->Seek(start, STREAM_SEEK_SET, NULL));
return ReadStream_FALSE(_stream, ids, size);
}
*/
HRESULT CHandler::Open2(IInStream *inStream)
{
{
Byte buf[kHeaderSize3];
RINOK(ReadStream_FALSE(inStream, buf, kHeaderSize3));
if (!_h.Parse(buf))
return S_FALSE;
if (!_h.IsSupported())
return E_NOTIMPL;
switch (_h.Method)
{
case kMethod_ZLIB:
case kMethod_LZMA:
case kMethod_LZO:
break;
default:
return E_NOTIMPL;
}
}
_stream = inStream;
if (_h.NumFrags != 0)
{
if (_h.NumFrags > kNumFilesMax)
return S_FALSE;
_frags.Reserve(_h.NumFrags);
CByteBuffer data;
unsigned bigFrag = (_h.Major > 2);
unsigned fragPtrsInBlockLog = kMetadataBlockSizeLog - (3 + bigFrag);
UInt32 numBlocks = (_h.NumFrags + (1 << fragPtrsInBlockLog) - 1) >> fragPtrsInBlockLog;
size_t numBlocksBytes = (size_t)numBlocks << (2 + bigFrag);
data.SetCapacity(numBlocksBytes);
RINOK(inStream->Seek(_h.FragTable, STREAM_SEEK_SET, NULL));
RINOK(ReadStream_FALSE(inStream, data, numBlocksBytes));
bool be = _h.be;
for (UInt32 i = 0; i < numBlocks; i++)
{
UInt64 offset = bigFrag ? Get64(data + i * 8) : Get32(data + i * 4);
RINOK(_stream->Seek(offset, STREAM_SEEK_SET, NULL));
_dynOutStreamSpec->Init();
UInt32 packSize = kMetadataBlockSize + 3;
RINOK(ReadMetadataBlock(packSize));
UInt32 unpackSize = (UInt32)_dynOutStreamSpec->GetSize();
if (unpackSize != kMetadataBlockSize)
if (i != numBlocks - 1 || unpackSize != ((_h.NumFrags << (3 + bigFrag)) & (kMetadataBlockSize - 1)))
return S_FALSE;
const Byte *buf = _dynOutStreamSpec->GetBuffer();
for (UInt32 j = 0; j < kMetadataBlockSize && j < unpackSize;)
{
CFrag frag;
if (bigFrag)
{
frag.StartBlock = Get64(buf + j);
frag.Size = Get32(buf + j + 8);
// some archives contain nonzero in unused (buf + j + 12)
j += 16;
}
else
{
frag.StartBlock = Get32(buf + j);
frag.Size = Get32(buf + j + 4);
j += 8;
}
_frags.Add(frag);
}
}
if ((UInt32)_frags.Size() != _h.NumFrags)
return S_FALSE;
}
// RINOK(inStream->Seek(_h.InodeTable, STREAM_SEEK_SET, NULL));
RINOK(ReadData(_inodesData, _h.InodeTable, _h.DirTable));
RINOK(ReadData(_dirs, _h.DirTable, _h.FragTable));
UInt64 absOffset = _h.RootInode >> 16;
if (absOffset >= ((UInt64)1 << 32))
return S_FALSE;
{
UInt32 pos = 0;
UInt32 totalSize = (UInt32)_inodesData.Data.GetCapacity();
_nodesPos.Reserve(_h.NumInodes);
_nodes.Reserve(_h.NumInodes);
// we use _blockToNode for binary search seed optimizations
_blockToNode.Reserve(_inodesData.GetNumBlocks() + 1);
int curBlock = 0;
for (UInt32 i = 0; i < _h.NumInodes; i++)
{
CNode n;
const Byte *p = _inodesData.Data + pos;
UInt32 size = totalSize - pos;
switch(_h.Major)
{
case 1: size = n.Parse1(p, size, _h); break;
case 2: size = n.Parse2(p, size, _h); break;
case 3: size = n.Parse3(p, size, _h); break;
default: size = n.Parse4(p, size, _h); break;
}
if (size == 0)
return S_FALSE;
while (pos >= _inodesData.UnpackPos[curBlock])
{
_blockToNode.Add(_nodesPos.Size());
curBlock++;
}
_nodesPos.Add(pos);
_nodes.Add(n);
pos += size;
}
_blockToNode.Add(_nodesPos.Size());
if (pos != totalSize)
return S_FALSE;
}
int rootNodeIndex;
RINOK(OpenDir(-1, (UInt32)absOffset, (UInt32)_h.RootInode & 0xFFFF, 0, rootNodeIndex));
/*
if (_h.Major < 4)
{
RINOK(ReadUids(_h.UidTable, _h.NumUids, _uids));
RINOK(ReadUids(_h.GidTable, _h.NumGids, _gids));
}
else
{
UInt32 size = _h.NumIDs * 4;
_uids.SetCapacity(size);
7zip/CPP/7zip/Archive/SquashfsHandler.cpp view on Meta::CPAN
char *dest = path.GetBuffer(len) + len;
index = indexMem;
for (;;)
{
const CItem &item = _items[index];
index = item.Parent;
const Byte *p = _dirs.Data + item.Ptr;
unsigned size = (_h.IsOldVersion() ? (unsigned)p[2] : (unsigned)Get16(p + 6)) + 1;
p += _h.GetFileNameOffset();
unsigned i;
for (i = 0; i < size && p[i]; i++);
dest -= i;
memcpy(dest, p, i);
if (index < 0)
break;
*(--dest) = CHAR_PATH_SEPARATOR;
}
path.ReleaseBuffer(len);
return path;
}
STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback)
{
COM_TRY_BEGIN
{
Close();
_limitedInStreamSpec->SetStream(stream);
HRESULT res;
try
{
RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL));
_openCallback = callback;
res = Open2(stream);
}
catch(...)
{
Close();
throw;
}
if (res != S_OK)
{
Close();
return res;
}
_stream = stream;
}
return S_OK;
COM_TRY_END
}
STDMETHODIMP CHandler::Close()
{
_limitedInStreamSpec->ReleaseStream();
_stream.Release();
_items.Clear();
_nodes.Clear();
_nodesPos.Clear();
_blockToNode.Clear();
_frags.Clear();
_inodesData.Clear();
_dirs.Clear();
// _uids.Free();
// _gids.Free();;
_cachedBlock.Free();
ClearCache();
return S_OK;
}
bool CHandler::GetPackSize(int index, UInt64 &totalPack, bool fillOffsets)
{
totalPack = 0;
const CItem &item = _items[index];
const CNode &node = _nodes[item.Node];
UInt32 ptr = _nodesPos[item.Node];
const Byte *p = _inodesData.Data + ptr;
bool be = _h.be;
UInt32 type = node.Type;
UInt32 offset;
if (node.IsLink() || node.FileSize == 0)
{
totalPack = node.FileSize;
return true;
}
UInt32 numBlocks = (UInt32)node.GetNumBlocks(_h);
if (fillOffsets)
{
_blockOffsets.Clear();
_blockCompressed.Clear();
_blockOffsets.Add(totalPack);
}
if (_h.Major <= 1)
{
offset = 15;
p += offset;
for (UInt32 i = 0; i < numBlocks; i++)
{
UInt32 t = Get16(p + i * 2);
if (fillOffsets)
_blockCompressed.Add((t & kNotCompressedBit16) == 0);
if (t != kNotCompressedBit16)
t &= ~kNotCompressedBit16;
totalPack += t;
if (fillOffsets)
_blockOffsets.Add(totalPack);
}
}
else
{
if (_h.Major <= 2)
offset = 24;
else if (type == kType_FILE)
offset = 32;
else if (type == kType_FILE + 7)
offset = (_h.Major <= 3 ? 40 : 56);
else
return false;
p += offset;
for (UInt64 i = 0; i < numBlocks; i++)
{
UInt32 t = Get32(p + i * 4);
if (fillOffsets)
_blockCompressed.Add(IS_COMPRESSED_BLOCK(t));
UInt32 size = GET_COMPRESSED_BLOCK_SIZE(t);
if (size > _h.BlockSize)
return false;
totalPack += size;
if (fillOffsets)
_blockOffsets.Add(totalPack);
}
if (node.ThereAreFrags())
{
if (node.Frag >= (UInt32)_frags.Size())
return false;
const CFrag &frag = _frags[node.Frag];
if (node.Offset == 0)
{
UInt32 size = GET_COMPRESSED_BLOCK_SIZE(frag.Size);
if (size > _h.BlockSize)
return false;
totalPack += size;
}
}
}
return true;
}
STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
{
*numItems = _items.Size();
return S_OK;
}
STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
{
COM_TRY_BEGIN
NWindows::NCOM::CPropVariant prop;
switch(propID)
{
case kpidMethod:
{
const char *s;
if (_h.SeveralMethods)
s = "LZMA ZLIB";
else
{
s = k_Methods[0];
if (_h.Method < sizeof(k_Methods) / sizeof(k_Methods[0]))
s = k_Methods[_h.Method];
}
prop = s;
break;
}
case kpidFileSystem:
{
AString res = "SquashFS";
if (_h.SeveralMethods)
res += "-LZMA";
res += ' ';
char s[16];
ConvertUInt32ToString(_h.Major, s);
res += s;
res += '.';
ConvertUInt32ToString(_h.Minor, s);
res += s;
prop = res;
break;
}
case kpidBlock: prop = _h.BlockSize; break;
case kpidBigEndian: prop = _h.be; break;
case kpidCTime:
if (_h.CTime != 0)
{
FILETIME ft;
NWindows::NTime::UnixTimeToFileTime(_h.CTime, ft);
prop = ft;
}
7zip/CPP/7zip/Archive/SquashfsHandler.cpp view on Meta::CPAN
}
case kpidGroup:
{
if (_h.Major == 4 || node.Gid == _h.GetSpecGuidIndex())
{
UInt32 offset = node.Uid * 4;
if (offset < _uids.GetCapacity())
prop = (UInt32)Get32(_uids + offset);
}
else
{
UInt32 offset = node.Gid * 4;
if (offset < _gids.GetCapacity())
prop = (UInt32)Get32(_gids + offset);
}
break;
}
*/
/*
case kpidLinks:
if (_h.Major >= 3 && node.Type != kType_FILE)
prop = node.NumLinks;
break;
*/
}
prop.Detach(value);
return S_OK;
COM_TRY_END
}
class CSquashfsInStream: public CCachedInStream
{
HRESULT ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize);
public:
CHandler *Handler;
};
HRESULT CSquashfsInStream::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize)
{
return Handler->ReadBlock(blockIndex, dest, blockSize);
}
HRESULT CHandler::ReadBlock(UInt64 blockIndex, Byte *dest, size_t blockSize)
{
const CNode &node = _nodes[_nodeIndex];
UInt64 blockOffset;
UInt32 packBlockSize;
UInt32 offsetInBlock = 0;
bool compressed;
if (blockIndex < _blockCompressed.Size())
{
compressed = _blockCompressed[(int)blockIndex];
blockOffset = _blockOffsets[(int)blockIndex];
packBlockSize = (UInt32)(_blockOffsets[(int)blockIndex + 1] - blockOffset);
blockOffset += node.StartBlock;
}
else
{
if (!node.ThereAreFrags())
return S_FALSE;
const CFrag &frag = _frags[node.Frag];
offsetInBlock = node.Offset;
blockOffset = frag.StartBlock;
packBlockSize = GET_COMPRESSED_BLOCK_SIZE(frag.Size);
compressed = IS_COMPRESSED_BLOCK(frag.Size);
}
if (packBlockSize == 0)
{
// sparse file ???
memset(dest, 0, blockSize);
return S_OK;
}
if (blockOffset != _cachedBlockStartPos ||
packBlockSize != _cachedPackBlockSize)
{
ClearCache();
RINOK(_stream->Seek(blockOffset, STREAM_SEEK_SET, NULL));
_limitedInStreamSpec->Init(packBlockSize);
if (compressed)
{
_outStreamSpec->Init((Byte *)_cachedBlock, _h.BlockSize);
bool outBufWasWritten;
UInt32 outBufWasWrittenSize;
HRESULT res = Decompress(_outStream, _cachedBlock, &outBufWasWritten, &outBufWasWrittenSize, packBlockSize, _h.BlockSize);
if (outBufWasWritten)
_cachedUnpackBlockSize = outBufWasWrittenSize;
else
_cachedUnpackBlockSize = (UInt32)_outStreamSpec->GetPos();
RINOK(res);
}
else
{
RINOK(ReadStream_FALSE(_limitedInStream, _cachedBlock, packBlockSize));
_cachedUnpackBlockSize = packBlockSize;
}
_cachedBlockStartPos = blockOffset;
_cachedPackBlockSize = packBlockSize;
}
if (offsetInBlock + blockSize > _cachedUnpackBlockSize)
return S_FALSE;
memcpy(dest, _cachedBlock + offsetInBlock, blockSize);
return S_OK;
}
STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
Int32 testMode, IArchiveExtractCallback *extractCallback)
{
COM_TRY_BEGIN
bool allFilesMode = (numItems == (UInt32)-1);
if (allFilesMode)
numItems = _items.Size();
if (numItems == 0)
return S_OK;
UInt64 totalSize = 0;
UInt32 i;
for (i = 0; i < numItems; i++)
{
const CItem &item = _items[allFilesMode ? i : indices[i]];
const CNode &node = _nodes[item.Node];
totalSize += node.GetSize();
}
extractCallback->SetTotal(totalSize);
( run in 0.928 second using v1.01-cache-2.11-cpan-364913b4093 )