// DebHandler.cpp #include "StdAfx.h" #include "Common/ComTry.h" #include "Common/StringConvert.h" #include "Common/StringToInt.h" #include "Windows/PropVariant.h" #include "Windows/Time.h" #include "../Common/LimitedStreams.h" #include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" #include "../Common/StreamUtils.h" #include "../Compress/CopyCoder.h" #include "Common/ItemNameUtils.h" using namespace NWindows; using namespace NTime; namespace NArchive { namespace NDeb { namespace NHeader { const int kSignatureLen = 8; const char *kSignature = "!\n"; const int kNameSize = 16; const int kTimeSize = 12; const int kModeSize = 8; const int kSizeSize = 10; /* struct CHeader { char Name[kNameSize]; char MTime[kTimeSize]; char Number0[6]; char Number1[6]; char Mode[kModeSize]; char Size[kSizeSize]; char Quote; char NewLine; }; */ const int kHeaderSize = kNameSize + kTimeSize + 6 + 6 + kModeSize + kSizeSize + 1 + 1; } struct CItem { AString Name; UInt64 Size; UInt32 MTime; UInt32 Mode; UInt64 HeaderPos; UInt64 GetDataPos() const { return HeaderPos + NHeader::kHeaderSize; }; // UInt64 GetFullSize() const { return NFileHeader::kRecordSize + Size; }; }; class CInArchive { CMyComPtr m_Stream; HRESULT GetNextItemReal(bool &filled, CItem &itemInfo); public: UInt64 m_Position; HRESULT Open(IInStream *inStream); HRESULT GetNextItem(bool &filled, CItem &itemInfo); HRESULT SkipData(UInt64 dataSize); }; HRESULT CInArchive::Open(IInStream *inStream) { RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &m_Position)); char signature[NHeader::kSignatureLen]; RINOK(ReadStream_FALSE(inStream, signature, NHeader::kSignatureLen)); m_Position += NHeader::kSignatureLen; if (memcmp(signature, NHeader::kSignature, NHeader::kSignatureLen) != 0) return S_FALSE; m_Stream = inStream; return S_OK; } static void MyStrNCpy(char *dest, const char *src, int size) { for (int i = 0; i < size; i++) { char c = src[i]; dest[i] = c; if (c == 0) break; } } static bool OctalToNumber(const char *s, int size, UInt64 &res) { char sz[32]; MyStrNCpy(sz, s, size); sz[size] = 0; const char *end; int i; for (i = 0; sz[i] == ' '; i++); res = ConvertOctStringToUInt64(sz + i, &end); return (*end == ' ' || *end == 0); } static bool OctalToNumber32(const char *s, int size, UInt32 &res) { UInt64 res64; if (!OctalToNumber(s, size, res64)) return false; res = (UInt32)res64; return (res64 <= 0xFFFFFFFF); } static bool DecimalToNumber(const char *s, int size, UInt64 &res) { char sz[32]; MyStrNCpy(sz, s, size); sz[size] = 0; const char *end; int i; for (i = 0; sz[i] == ' '; i++); res = ConvertStringToUInt64(sz + i, &end); return (*end == ' ' || *end == 0); } static bool DecimalToNumber32(const char *s, int size, UInt32 &res) { UInt64 res64; if (!DecimalToNumber(s, size, res64)) return false; res = (UInt32)res64; return (res64 <= 0xFFFFFFFF); } #define RIF(x) { if (!(x)) return S_FALSE; } HRESULT CInArchive::GetNextItemReal(bool &filled, CItem &item) { filled = false; char header[NHeader::kHeaderSize]; const char *cur = header; size_t processedSize = sizeof(header); item.HeaderPos = m_Position; RINOK(ReadStream(m_Stream, header, &processedSize)); if (processedSize != sizeof(header)) return S_OK; m_Position += processedSize; char tempString[NHeader::kNameSize + 1]; MyStrNCpy(tempString, cur, NHeader::kNameSize); cur += NHeader::kNameSize; tempString[NHeader::kNameSize] = '\0'; item.Name = tempString; item.Name.Trim(); for (int i = 0; i < item.Name.Length(); i++) if (((Byte)item.Name[i]) < 0x20) return S_FALSE; RIF(DecimalToNumber32(cur, NHeader::kTimeSize, item.MTime)); cur += NHeader::kTimeSize; cur += 6 + 6; RIF(OctalToNumber32(cur, NHeader::kModeSize, item.Mode)); cur += NHeader::kModeSize; RIF(DecimalToNumber(cur, NHeader::kSizeSize, item.Size)); cur += NHeader::kSizeSize; filled = true; return S_OK; } HRESULT CInArchive::GetNextItem(bool &filled, CItem &item) { for (;;) { RINOK(GetNextItemReal(filled, item)); if (!filled) return S_OK; if (item.Name.Compare("debian-binary") != 0) return S_OK; if (item.Size != 4) return S_OK; SkipData(item.Size); } } HRESULT CInArchive::SkipData(UInt64 dataSize) { return m_Stream->Seek((dataSize + 1) & (~((UInt64)0x1)), STREAM_SEEK_CUR, &m_Position); } class CHandler: public IInArchive, public IInArchiveGetStream, public CMyUnknownImp { CObjectVector _items; CMyComPtr _stream; Int32 _mainSubfile; UInt64 _phySize; public: MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) INTERFACE_IInArchive(;) STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); }; static STATPROPSTG kArcProps[] = { { NULL, kpidPhySize, VT_UI8} }; static STATPROPSTG kProps[] = { { NULL, kpidPath, VT_BSTR}, { NULL, kpidSize, VT_UI8}, { NULL, kpidMTime, VT_FILETIME} }; IMP_IInArchive_Props IMP_IInArchive_ArcProps STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 * /* maxCheckStartPosition */, IArchiveOpenCallback *openArchiveCallback) { COM_TRY_BEGIN { _mainSubfile = -1; CInArchive archive; if (archive.Open(stream) != S_OK) return S_FALSE; _items.Clear(); if (openArchiveCallback != NULL) { RINOK(openArchiveCallback->SetTotal(NULL, NULL)); UInt64 numFiles = _items.Size(); RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL)); } for (;;) { CItem item; bool filled; HRESULT result = archive.GetNextItem(filled, item); if (result == S_FALSE) return S_FALSE; if (result != S_OK) return S_FALSE; if (!filled) break; if (item.Name.Left(5) == "data.") _mainSubfile = _items.Size(); _items.Add(item); archive.SkipData(item.Size); if (openArchiveCallback != NULL) { UInt64 numFiles = _items.Size(); RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL)); } } _stream = stream; _phySize = archive.m_Position; } return S_OK; COM_TRY_END } STDMETHODIMP CHandler::Close() { _stream.Release(); _items.Clear(); return S_OK; } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = _items.Size(); return S_OK; } STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { NCOM::CPropVariant prop; switch(propID) { case kpidPhySize: prop = _phySize; break; case kpidMainSubfile: if (_mainSubfile >= 0) prop = (UInt32)_mainSubfile; break; } prop.Detach(value); return S_OK; } STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NWindows::NCOM::CPropVariant prop; const CItem &item = _items[index]; switch(propID) { case kpidPath: prop = (const wchar_t *)NItemName::GetOSName2(MultiByteToUnicodeString(item.Name, CP_OEMCP)); break; case kpidSize: case kpidPackSize: prop = item.Size; break; case kpidMTime: { if (item.MTime != 0) { FILETIME fileTime; NTime::UnixTimeToFileTime(item.MTime, fileTime); prop = fileTime; } break; } } prop.Detach(value); return S_OK; COM_TRY_END } 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++) totalSize += _items[allFilesMode ? i : indices[i]].Size; extractCallback->SetTotal(totalSize); UInt64 currentTotalSize = 0; NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); CMyComPtr copyCoder = copyCoderSpec; CLocalProgress *lps = new CLocalProgress; CMyComPtr progress = lps; lps->Init(extractCallback, false); CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; CMyComPtr inStream(streamSpec); streamSpec->SetStream(_stream); for (i = 0; i < numItems; i++) { lps->InSize = lps->OutSize = currentTotalSize; RINOK(lps->SetCur()); CMyComPtr realOutStream; Int32 askMode = testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; Int32 index = allFilesMode ? i : indices[i]; const CItem &item = _items[index]; RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); currentTotalSize += item.Size; if (!testMode && !realOutStream) continue; RINOK(extractCallback->PrepareOperation(askMode)); if (testMode) { RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); continue; } RINOK(_stream->Seek(item.GetDataPos(), STREAM_SEEK_SET, NULL)); streamSpec->Init(item.Size); RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress)); realOutStream.Release(); RINOK(extractCallback->SetOperationResult((copyCoderSpec->TotalSize == item.Size) ? NExtract::NOperationResult::kOK: NExtract::NOperationResult::kDataError)); } return S_OK; COM_TRY_END } STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) { COM_TRY_BEGIN const CItem &item = _items[index]; return CreateLimitedInStream(_stream, item.GetDataPos(), item.Size, stream); COM_TRY_END } static IInArchive *CreateArc() { return new NArchive::NDeb::CHandler; } static CArcInfo g_ArcInfo = { L"Deb", L"deb", 0, 0xEC, { '!', '<', 'a', 'r', 'c', 'h', '>', '\n' }, 8, false, CreateArc, 0 }; REGISTER_ARC(Deb) }}