diff options
Diffstat (limited to 'CPP/7zip/Archive/CpioHandler.cpp')
-rw-r--r--[-rwxr-xr-x] | CPP/7zip/Archive/CpioHandler.cpp | 834 |
1 files changed, 504 insertions, 330 deletions
diff --git a/CPP/7zip/Archive/CpioHandler.cpp b/CPP/7zip/Archive/CpioHandler.cpp index 0f32ef66..9c1271c4 100755..100644 --- a/CPP/7zip/Archive/CpioHandler.cpp +++ b/CPP/7zip/Archive/CpioHandler.cpp @@ -2,12 +2,15 @@ #include "StdAfx.h" -#include "Common/ComTry.h" -#include "Common/StringConvert.h" -#include "Common/StringToInt.h" +#include "../../../C/CpuArch.h" -#include "Windows/PropVariant.h" -#include "Windows/Time.h" +#include "../../Common/ComTry.h" +#include "../../Common/StringConvert.h" +#include "../../Common/StringToInt.h" +#include "../../Common/UTFConvert.h" + +#include "../../Windows/PropVariant.h" +#include "../../Windows/TimeUtils.h" #include "../Common/LimitedStreams.h" #include "../Common/ProgressUtils.h" @@ -18,24 +21,30 @@ #include "Common/ItemNameUtils.h" +using namespace NWindows; + namespace NArchive { namespace NCpio { -namespace NFileHeader -{ - namespace NMagic - { - const char *kMagic1 = "070701"; - const char *kMagic2 = "070702"; - const char *kMagic3 = "070707"; - const char *kEndName = "TRAILER!!!"; +static const Byte kMagicBin0 = 0xC7; +static const Byte kMagicBin1 = 0x71; - const Byte kMagicForRecord2[2] = { 0xC7, 0x71 }; - } +// #define MAGIC_ASCII { '0', '7', '0', '7', '0' } + +static const Byte kMagicHex = '1'; // New ASCII Format +static const Byte kMagicHexCrc = '2'; // New CRC Format +static const Byte kMagicOct = '7'; // Portable ASCII Format + +static const char *kName_TRAILER = "TRAILER!!!"; + +static const unsigned k_BinRecord_Size = 2 + 8 * 2 + 2 * 4; +static const unsigned k_OctRecord_Size = 6 + 8 * 6 + 2 * 11; +static const unsigned k_HexRecord_Size = 6 + 13 * 8; + +static const unsigned k_RecordSize_Max = k_HexRecord_Size; - const UInt32 kRecord2Size = 26; /* - struct CRecord2 + struct CBinRecord { unsigned short c_magic; short c_dev; @@ -49,13 +58,10 @@ namespace NFileHeader unsigned short c_namesize; unsigned short c_filesizes[2]; }; - */ - - const UInt32 kRecordSize = 110; - /* - struct CRecord + + struct CHexRecord { - char Magic[6]; // "070701" for "new" portable format, "070702" for CRC format + char Magic[6]; char inode[8]; char Mode[8]; char UID[8]; @@ -69,15 +75,26 @@ namespace NFileHeader char RDevMinor[8]; //only valid for chr and blk special files char NameSize[8]; // count includes terminating NUL in pathname char ChkSum[8]; // 0 for "new" portable format; for CRC format the sum of all the bytes in the file - bool CheckMagic() const - { return memcmp(Magic, NMagic::kMagic1, 6) == 0 || - memcmp(Magic, NMagic::kMagic2, 6) == 0; }; }; - */ +*/ - const UInt32 kOctRecordSize = 76; - -} +enum EType +{ + k_Type_BinLe, + k_Type_BinBe, + k_Type_Oct, + k_Type_Hex, + k_Type_HexCrc +}; + +static const char *k_Types[] = +{ + "Binary LE" + , "Binary BE" + , "Portable ASCII" + , "New ASCII" + , "New CRC" +}; struct CItem { @@ -86,12 +103,9 @@ struct CItem UInt32 Mode; UInt32 UID; UInt32 GID; - UInt32 Size; + UInt64 Size; UInt32 MTime; - // char LinkFlag; - // AString LinkName; ????? - char Magic[8]; UInt32 NumLinks; UInt32 DevMajor; UInt32 DevMinor; @@ -100,394 +114,479 @@ struct CItem UInt32 ChkSum; UInt32 Align; + EType Type; + + UInt32 HeaderSize; + UInt64 HeaderPos; + bool IsBin() const { return Type == k_Type_BinLe || Type == k_Type_BinBe; } + bool IsCrcFormat() const { return Type == k_Type_HexCrc; }; bool IsDir() const { return (Mode & 0170000) == 0040000; } + bool IsTrailer() const { return strcmp(Name, kName_TRAILER) == 0; } + UInt64 GetDataPosition() const { return HeaderPos + HeaderSize; }; }; -class CItemEx: public CItem +enum EErrorType { -public: - UInt64 HeaderPosition; - UInt32 HeaderSize; - UInt64 GetDataPosition() const { return HeaderPosition + HeaderSize; }; + k_ErrorType_OK, + k_ErrorType_Corrupted, + k_ErrorType_UnexpectedEnd, }; -const UInt32 kMaxBlockSize = NFileHeader::kRecordSize; - -class CInArchive +struct CInArchive { - CMyComPtr<IInStream> m_Stream; - UInt64 m_Position; - - UInt16 _blockSize; - Byte _block[kMaxBlockSize]; - UInt32 _blockPos; - Byte ReadByte(); - UInt16 ReadUInt16(); - UInt32 ReadUInt32(); + ISequentialInStream *Stream; + UInt64 Processed; - bool ReadNumber(UInt32 &resultValue); - bool ReadOctNumber(int size, UInt32 &resultValue); - - HRESULT ReadBytes(void *data, UInt32 size, UInt32 &processedSize); -public: - HRESULT Open(IInStream *inStream); - HRESULT GetNextItem(bool &filled, CItemEx &itemInfo); - HRESULT Skip(UInt64 numBytes); - HRESULT SkipDataRecords(UInt64 dataSize, UInt32 align); + HRESULT Read(void *data, size_t *size); + HRESULT GetNextItem(CItem &item, EErrorType &errorType); }; -HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 &processedSize) +HRESULT CInArchive::Read(void *data, size_t *size) { - size_t realProcessedSize = size; - RINOK(ReadStream(m_Stream, data, &realProcessedSize)); - processedSize = (UInt32)realProcessedSize; - m_Position += processedSize; - return S_OK; + HRESULT res = ReadStream(Stream, data, size); + Processed += *size; + return res; } -Byte CInArchive::ReadByte() +static bool ReadHex(const Byte *p, UInt32 &resVal) { - if (_blockPos >= _blockSize) - throw "Incorrect cpio archive"; - return _block[_blockPos++]; + char sz[16]; + memcpy(sz, p, 8); + sz[8] = 0; + const char *end; + resVal = ConvertHexStringToUInt32(sz, &end); + return (unsigned)(end - sz) == 8; } -UInt16 CInArchive::ReadUInt16() +static bool ReadOct6(const Byte *p, UInt32 &resVal) { - UInt16 value = 0; - for (int i = 0; i < 2; i++) - { - Byte b = ReadByte(); - value |= (UInt16(b) << (8 * i)); - } - return value; + char sz[16]; + memcpy(sz, p, 6); + sz[6] = 0; + const char *end; + resVal = ConvertOctStringToUInt32(sz, &end); + return (unsigned)(end - sz) == 6; } -UInt32 CInArchive::ReadUInt32() +static bool ReadOct11(const Byte *p, UInt64 &resVal) { - UInt32 value = 0; - for (int i = 0; i < 4; i++) - { - Byte b = ReadByte(); - value |= (UInt32(b) << (8 * i)); - } - return value; + char sz[16]; + memcpy(sz, p, 11); + sz[11] = 0; + const char *end; + resVal = ConvertOctStringToUInt64(sz, &end); + return (unsigned)(end - sz) == 11; } -HRESULT CInArchive::Open(IInStream *inStream) -{ - RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &m_Position)); - m_Stream = inStream; - return S_OK; -} -bool CInArchive::ReadNumber(UInt32 &resultValue) -{ - resultValue = 0; - for (int i = 0; i < 8; i++) - { - char c = char(ReadByte()); - int d; - if (c >= '0' && c <= '9') - d = c - '0'; - else if (c >= 'A' && c <= 'F') - d = 10 + c - 'A'; - else if (c >= 'a' && c <= 'f') - d = 10 + c - 'a'; - else - return false; - resultValue *= 0x10; - resultValue += d; - } - return true; -} +#define READ_HEX(y) { if (!ReadHex(p2, y)) return S_OK; p2 += 8; } +#define READ_OCT_6(y) { if (!ReadOct6(p2, y)) return S_OK; p2 += 6; } +#define READ_OCT_11(y) { if (!ReadOct11(p2, y)) return S_OK; p2 += 11; } -static bool OctalToNumber(const char *s, UInt64 &res) +static UInt32 GetAlignedSize(UInt32 size, UInt32 align) { - const char *end; - res = ConvertOctStringToUInt64(s, &end); - return (*end == ' ' || *end == 0); + while ((size & (align - 1)) != 0) + size++; + return size; } -static bool OctalToNumber32(const char *s, UInt32 &res) -{ - UInt64 res64; - if (!OctalToNumber(s, res64)) - return false; - res = (UInt32)res64; - return (res64 <= 0xFFFFFFFF); -} +static UInt16 Get16(const Byte *p, bool be) { if (be) return GetBe16(p); return GetUi16(p); } +static UInt32 Get32(const Byte *p, bool be) { return ((UInt32)Get16(p, be) << 16) + Get16(p + 2, be); } -bool CInArchive::ReadOctNumber(int size, UInt32 &resultValue) -{ - char sz[32 + 4]; - int i; - for (i = 0; i < size && i < 32; i++) - sz[i] = (char)ReadByte(); - sz[i] = 0; - return OctalToNumber32(sz, resultValue); -} +#define G16(offs, v) v = Get16(p + (offs), be) +#define G32(offs, v) v = Get32(p + (offs), be) -#define GetFromHex(y) { if (!ReadNumber(y)) return S_FALSE; } -#define GetFromOct6(y) { if (!ReadOctNumber(6, y)) return S_FALSE; } -#define GetFromOct11(y) { if (!ReadOctNumber(11, y)) return S_FALSE; } +static const unsigned kNameSizeMax = 1 << 12; -static unsigned short ConvertValue(unsigned short value, bool convert) +API_FUNC_static_IsArc IsArc_Cpio(const Byte *p, size_t size) { - if (!convert) - return value; - return (unsigned short)((((unsigned short)(value & 0xFF)) << 8) | (value >> 8)); -} + if (size < k_BinRecord_Size) + return k_IsArc_Res_NEED_MORE; -static UInt32 GetAlignedSize(UInt32 size, UInt32 align) -{ - while ((size & (align - 1)) != 0) - size++; - return size; + UInt32 nameSize; + UInt32 numLinks; + if (p[0] == '0') + { + if (p[1] != '7' || + p[2] != '0' || + p[3] != '7' || + p[4] != '0') + return k_IsArc_Res_NO; + if (p[5] == '7') + { + if (size < k_OctRecord_Size) + return k_IsArc_Res_NEED_MORE; + for (int i = 6; i < k_OctRecord_Size; i++) + { + char c = p[i]; + if (c < '0' || c > '7') + return k_IsArc_Res_NO; + } + ReadOct6(p + 6 * 6, numLinks); + ReadOct6(p + 8 * 6 + 11, nameSize); + } + else if (p[5] == '1' || p[5] == '2') + { + if (size < k_HexRecord_Size) + return k_IsArc_Res_NEED_MORE; + for (int i = 6; i < k_HexRecord_Size; i++) + { + char c = p[i]; + if ((c < '0' || c > '9') && + (c < 'A' || c > 'F') && + (c < 'a' || c > 'f')) + return k_IsArc_Res_NO; + } + ReadHex(p + 6 + 4 * 8, numLinks); + ReadHex(p + 6 + 11 * 8, nameSize); + } + else + return k_IsArc_Res_NO; + } + else + { + UInt32 rDevMinor; + if (p[0] == kMagicBin0 && p[1] == kMagicBin1) + { + numLinks = GetUi16(p + 12); + rDevMinor = GetUi16(p + 14); + nameSize = GetUi16(p + 20); + } + else if (p[0] == kMagicBin1 && p[1] == kMagicBin0) + { + numLinks = GetBe16(p + 12); + rDevMinor = GetBe16(p + 14); + nameSize = GetBe16(p + 20); + } + else + return k_IsArc_Res_NO; + + if (rDevMinor != 0) + return k_IsArc_Res_NO; + if (nameSize > (1 << 8)) + return k_IsArc_Res_NO; + } + if (numLinks == 0 || numLinks >= (1 << 10)) + return k_IsArc_Res_NO; + if (nameSize == 0 || nameSize > kNameSizeMax) + return k_IsArc_Res_NO; + return k_IsArc_Res_YES; } +#define READ_STREAM(_dest_, _size_) \ + { size_t processed = (_size_); RINOK(Read(_dest_, &processed)); \ +if (processed != (_size_)) { errorType = k_ErrorType_UnexpectedEnd; return S_OK; } } -HRESULT CInArchive::GetNextItem(bool &filled, CItemEx &item) +HRESULT CInArchive::GetNextItem(CItem &item, EErrorType &errorType) { - filled = false; + errorType = k_ErrorType_Corrupted; - UInt32 processedSize; - item.HeaderPosition = m_Position; + Byte p[k_RecordSize_Max]; - _blockSize = kMaxBlockSize; - RINOK(ReadBytes(_block, 2, processedSize)); - if (processedSize != 2) - return S_FALSE; - _blockPos = 0; + READ_STREAM(p, k_BinRecord_Size) UInt32 nameSize; - bool oldBE = - _block[0] == NFileHeader::NMagic::kMagicForRecord2[1] && - _block[1] == NFileHeader::NMagic::kMagicForRecord2[0]; - - bool binMode = (_block[0] == NFileHeader::NMagic::kMagicForRecord2[0] && - _block[1] == NFileHeader::NMagic::kMagicForRecord2[1]) || - oldBE; - - if (binMode) + if (p[0] != '0') { - RINOK(ReadBytes(_block + 2, NFileHeader::kRecord2Size - 2, processedSize)); - if (processedSize != NFileHeader::kRecord2Size - 2) - return S_FALSE; + bool be; + if (p[0] == kMagicBin0 && p[1] == kMagicBin1) { be = false; item.Type = k_Type_BinLe; } + else if (p[0] == kMagicBin1 && p[1] == kMagicBin0) { be = true; item.Type = k_Type_BinBe; } + else return S_FALSE; + item.Align = 2; - _blockPos = 2; item.DevMajor = 0; - item.DevMinor = ConvertValue(ReadUInt16(), oldBE); - item.inode = ConvertValue(ReadUInt16(), oldBE); - item.Mode = ConvertValue(ReadUInt16(), oldBE); - item.UID = ConvertValue(ReadUInt16(), oldBE); - item.GID = ConvertValue(ReadUInt16(), oldBE); - item.NumLinks = ConvertValue(ReadUInt16(), oldBE); item.RDevMajor =0; - item.RDevMinor = ConvertValue(ReadUInt16(), oldBE); - UInt16 timeHigh = ConvertValue(ReadUInt16(), oldBE); - UInt16 timeLow = ConvertValue(ReadUInt16(), oldBE); - item.MTime = (UInt32(timeHigh) << 16) + timeLow; - nameSize = ConvertValue(ReadUInt16(), oldBE); - UInt16 sizeHigh = ConvertValue(ReadUInt16(), oldBE); - UInt16 sizeLow = ConvertValue(ReadUInt16(), oldBE); - item.Size = (UInt32(sizeHigh) << 16) + sizeLow; - item.ChkSum = 0; - item.HeaderSize = GetAlignedSize( - nameSize + NFileHeader::kRecord2Size, item.Align); - nameSize = item.HeaderSize - NFileHeader::kRecord2Size; + + G16(2, item.DevMinor); + G16(4, item.inode); + G16(6, item.Mode); + G16(8, item.UID); + G16(10, item.GID); + G16(12, item.NumLinks); + G16(14, item.RDevMinor); + G32(16, item.MTime); + G16(20, nameSize); + G32(22, item.Size); + + /* + if (item.RDevMinor != 0) + return S_FALSE; + */ + + item.HeaderSize = GetAlignedSize(nameSize + k_BinRecord_Size, item.Align); + nameSize = item.HeaderSize - k_BinRecord_Size; } else { - RINOK(ReadBytes(_block + 2, 4, processedSize)); - if (processedSize != 4) + if (p[1] != '7' || + p[2] != '0' || + p[3] != '7' || + p[4] != '0') return S_FALSE; - - bool magicOK = - memcmp(_block, NFileHeader::NMagic::kMagic1, 6) == 0 || - memcmp(_block, NFileHeader::NMagic::kMagic2, 6) == 0; - _blockPos = 6; - if (magicOK) + if (p[5] == kMagicOct) { - RINOK(ReadBytes(_block + 6, NFileHeader::kRecordSize - 6, processedSize)); - if (processedSize != NFileHeader::kRecordSize - 6) - return S_FALSE; - item.Align = 4; + item.Type = k_Type_Oct; + READ_STREAM(p + k_BinRecord_Size, k_OctRecord_Size - k_BinRecord_Size) + item.Align = 1; + item.DevMajor = 0; + item.RDevMajor = 0; - GetFromHex(item.inode); - GetFromHex(item.Mode); - GetFromHex(item.UID); - GetFromHex(item.GID); - GetFromHex(item.NumLinks); - UInt32 mTime; - GetFromHex(mTime); - item.MTime = mTime; - GetFromHex(item.Size); - GetFromHex(item.DevMajor); - GetFromHex(item.DevMinor); - GetFromHex(item.RDevMajor); - GetFromHex(item.RDevMinor); - GetFromHex(nameSize); - GetFromHex(item.ChkSum); - item.HeaderSize = GetAlignedSize( - nameSize + NFileHeader::kRecordSize, item.Align); - nameSize = item.HeaderSize - NFileHeader::kRecordSize; + const Byte *p2 = p + 6; + READ_OCT_6(item.DevMinor); + READ_OCT_6(item.inode); + READ_OCT_6(item.Mode); + READ_OCT_6(item.UID); + READ_OCT_6(item.GID); + READ_OCT_6(item.NumLinks); + READ_OCT_6(item.RDevMinor); + { + UInt64 mTime64; + READ_OCT_11(mTime64); + item.MTime = 0; + if (mTime64 < (UInt32)(Int32)-1) + item.MTime = (UInt32)mTime64; + } + READ_OCT_6(nameSize); + READ_OCT_11(item.Size); // ????? + item.HeaderSize = GetAlignedSize(nameSize + k_OctRecord_Size, item.Align); + nameSize = item.HeaderSize - k_OctRecord_Size; } else { - if (!memcmp(_block, NFileHeader::NMagic::kMagic3, 6) == 0) - return S_FALSE; - RINOK(ReadBytes(_block + 6, NFileHeader::kOctRecordSize - 6, processedSize)); - if (processedSize != NFileHeader::kOctRecordSize - 6) + if (p[5] == kMagicHex) + item.Type = k_Type_Hex; + else if (p[5] == kMagicHexCrc) + item.Type = k_Type_HexCrc; + else return S_FALSE; - item.Align = 1; - item.DevMajor = 0; - GetFromOct6(item.DevMinor); - GetFromOct6(item.inode); - GetFromOct6(item.Mode); - GetFromOct6(item.UID); - GetFromOct6(item.GID); - GetFromOct6(item.NumLinks); - item.RDevMajor = 0; - GetFromOct6(item.RDevMinor); - UInt32 mTime; - GetFromOct11(mTime); - item.MTime = mTime; - GetFromOct6(nameSize); - GetFromOct11(item.Size); // ????? - item.HeaderSize = GetAlignedSize( - nameSize + NFileHeader::kOctRecordSize, item.Align); - nameSize = item.HeaderSize - NFileHeader::kOctRecordSize; + + READ_STREAM(p + k_BinRecord_Size, k_HexRecord_Size - k_BinRecord_Size) + + item.Align = 4; + + const Byte *p2 = p + 6; + READ_HEX(item.inode); + READ_HEX(item.Mode); + READ_HEX(item.UID); + READ_HEX(item.GID); + READ_HEX(item.NumLinks); + READ_HEX(item.MTime); + { + UInt32 size32; + READ_HEX(size32); + item.Size = size32; + } + READ_HEX(item.DevMajor); + READ_HEX(item.DevMinor); + READ_HEX(item.RDevMajor); + READ_HEX(item.RDevMinor); + READ_HEX(nameSize); + READ_HEX(item.ChkSum); + if (nameSize >= kNameSizeMax) + return S_OK; + item.HeaderSize = GetAlignedSize(nameSize + k_HexRecord_Size, item.Align); + nameSize = item.HeaderSize - k_HexRecord_Size; } } - if (nameSize == 0 || nameSize >= (1 << 27)) - return E_FAIL; - RINOK(ReadBytes(item.Name.GetBuffer(nameSize), nameSize, processedSize)); - if (processedSize != nameSize) - return E_FAIL; + if (nameSize > kNameSizeMax) + return S_FALSE; + if (nameSize == 0 || nameSize >= kNameSizeMax) + return S_OK; + char *s = item.Name.GetBuffer(nameSize); + size_t processedSize = nameSize; + RINOK(Read(s, &processedSize)); + s[nameSize] = 0; item.Name.ReleaseBuffer(); - if (strcmp(item.Name, NFileHeader::NMagic::kEndName) == 0) + if (processedSize != nameSize) + { + errorType = k_ErrorType_UnexpectedEnd; return S_OK; - filled = true; - return S_OK; -} - -HRESULT CInArchive::Skip(UInt64 numBytes) -{ - UInt64 newPostion; - RINOK(m_Stream->Seek(numBytes, STREAM_SEEK_CUR, &newPostion)); - m_Position += numBytes; - if (m_Position != newPostion) - return E_FAIL; + } + errorType = k_ErrorType_OK; return S_OK; } -HRESULT CInArchive::SkipDataRecords(UInt64 dataSize, UInt32 align) -{ - while ((dataSize & (align - 1)) != 0) - dataSize++; - return Skip(dataSize); -} - - class CHandler: public IInArchive, public IInArchiveGetStream, public CMyUnknownImp { - CObjectVector<CItemEx> _items; + CObjectVector<CItem> _items; CMyComPtr<IInStream> _stream; + UInt64 _phySize; + EType _Type; + EErrorType _error; + bool _isArc; public: MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) INTERFACE_IInArchive(;) STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); }; -/* -enum +static const Byte kArcProps[] = { - kpidinode = kpidUserDefined, - kpidiChkSum + kpidSubType }; -*/ -STATPROPSTG kProps[] = +static const Byte kProps[] = { - { NULL, kpidPath, VT_BSTR}, - { NULL, kpidIsDir, VT_BOOL}, - { NULL, kpidSize, VT_UI8}, - { NULL, kpidPackSize, VT_UI8}, - { NULL, kpidMTime, VT_FILETIME}, - { NULL, kpidPosixAttrib, VT_UI4}, - // { L"inode", kpidinode, VT_UI4} - // { L"CheckSum", kpidiChkSum, VT_UI4} + kpidPath, + kpidIsDir, + kpidSize, + kpidMTime, + kpidPosixAttrib, + kpidLinks }; IMP_IInArchive_Props -IMP_IInArchive_ArcProps_NO +IMP_IInArchive_ArcProps -STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback) +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN - // try + NCOM::CPropVariant prop; + switch (propID) { - CInArchive archive; + case kpidSubType: prop = k_Types[_Type]; break; + case kpidPhySize: prop = _phySize; break; + case kpidErrorFlags: + { + UInt32 v = 0; + if (!_isArc) + v |= kpv_ErrorFlags_IsNotArc; + switch (_error) + { + case k_ErrorType_UnexpectedEnd: v |= kpv_ErrorFlags_UnexpectedEnd; break; + case k_ErrorType_Corrupted: v |= kpv_ErrorFlags_HeadersError; break; + } + prop = v; + break; + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Open(IInStream *stream, const UInt64 *, IArchiveOpenCallback *callback) +{ + COM_TRY_BEGIN + { + Close(); + UInt64 endPos = 0; - bool needSetTotal = true; - if (callback != NULL) + RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos)); + RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); + if (callback) { - RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos)); - RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); + RINOK(callback->SetTotal(NULL, &endPos)); } - RINOK(archive.Open(stream)); - _items.Clear(); + CInArchive arc; + + arc.Stream = stream; + arc.Processed = 0; for (;;) { - CItemEx item; - bool filled; - HRESULT result = archive.GetNextItem(filled, item); + CItem item; + item.HeaderPos = arc.Processed; + HRESULT result = arc.GetNextItem(item, _error); if (result == S_FALSE) return S_FALSE; if (result != S_OK) return S_FALSE; - if (!filled) + if (_error != k_ErrorType_OK) + { + if (_error == k_ErrorType_Corrupted) + arc.Processed = item.HeaderPos; break; + } + if (_items.IsEmpty()) + _Type = item.Type; + else if (_items.Back().Type != item.Type) + { + _error = k_ErrorType_Corrupted; + arc.Processed = item.HeaderPos; + break; + } + if (item.IsTrailer()) + break; + _items.Add(item); - archive.SkipDataRecords(item.Size, item.Align); - if (callback != NULL) + { - if (needSetTotal) + // archive.SkipDataRecords(item.Size, item.Align); + UInt64 dataSize = item.Size; + UInt32 align = item.Align; + while ((dataSize & (align - 1)) != 0) + dataSize++; + + // _error = k_ErrorType_UnexpectedEnd; break; + + arc.Processed += dataSize; + if (arc.Processed > endPos) { - RINOK(callback->SetTotal(NULL, &endPos)); - needSetTotal = false; + _error = k_ErrorType_UnexpectedEnd; + break; } - if (_items.Size() % 100 == 0) + + UInt64 newPostion; + RINOK(stream->Seek(dataSize, STREAM_SEEK_CUR, &newPostion)); + if (arc.Processed != newPostion) + return E_FAIL; + } + + if (callback && (_items.Size() & 0xFF) == 0) + { + UInt64 numFiles = _items.Size(); + RINOK(callback->SetCompleted(&numFiles, &item.HeaderPos)); + } + } + _phySize = arc.Processed; + if (_error != k_ErrorType_OK) + { + if (_items.Size() == 0) + return S_FALSE; + if (_items.Size() == 1 && _items[0].IsBin()) + { + // probably it's false detected archive. So we return error + return S_FALSE; + } + } + else + { + // Read tailing zeros. + // Most of cpio files use 512-bytes aligned zeros + UInt64 pos = arc.Processed; + const UInt32 kTailSize_MAX = 1 << 9; + Byte buf[kTailSize_MAX]; + + UInt32 rem = (kTailSize_MAX - (UInt32)pos) & (kTailSize_MAX - 1); + if (rem != 0) + { + rem++; // we need to see that it's end of file + size_t processed = rem; + RINOK(ReadStream(stream, buf, &processed)); + if (processed < rem) { - UInt64 numFiles = _items.Size(); - UInt64 numBytes = item.HeaderPosition; - RINOK(callback->SetCompleted(&numFiles, &numBytes)); + unsigned i; + for (i = 0; i < processed && buf[i] == 0; i++); + if (i == processed) + _phySize += processed; } } } - if (_items.Size() == 0) - return S_FALSE; - + + _isArc = true; _stream = stream; } - /* - catch(...) - { - return S_FALSE; - } - */ return S_OK; COM_TRY_END } @@ -496,6 +595,10 @@ STDMETHODIMP CHandler::Close() { _items.Clear(); _stream.Release(); + _phySize = 0; + _Type = k_Type_BinLe; + _isArc = false; + _error = k_ErrorType_OK; return S_OK; } @@ -508,12 +611,24 @@ STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN - NWindows::NCOM::CPropVariant prop; - const CItemEx &item = _items[index]; + NCOM::CPropVariant prop; + const CItem &item = _items[index]; - switch(propID) + switch (propID) { - case kpidPath: prop = NItemName::GetOSName(MultiByteToUnicodeString(item.Name, CP_OEMCP)); break; + case kpidPath: + { + UString res; + bool needConvert = true; + #ifdef _WIN32 + if (ConvertUTF8ToUnicode(item.Name, res)) + needConvert = false; + #endif + if (needConvert) + res = MultiByteToUnicodeString(item.Name, CP_OEMCP); + prop = NItemName::GetOSName(res); + break; + } case kpidIsDir: prop = item.IsDir(); break; case kpidSize: case kpidPackSize: @@ -524,12 +639,13 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val if (item.MTime != 0) { FILETIME utc; - NWindows::NTime::UnixTimeToFileTime(item.MTime, utc); + NTime::UnixTimeToFileTime(item.MTime, utc); prop = utc; } break; } case kpidPosixAttrib: prop = item.Mode; break; + case kpidLinks: prop = item.NumLinks; break; /* case kpidinode: prop = item.inode; break; case kpidiChkSum: prop = item.ChkSum; break; @@ -540,11 +656,53 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val COM_TRY_END } +class COutStreamWithSum: + public ISequentialOutStream, + public CMyUnknownImp +{ + CMyComPtr<ISequentialOutStream> _stream; + UInt64 _size; + UInt32 _crc; + bool _calculate; +public: + MY_UNKNOWN_IMP + STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize); + void SetStream(ISequentialOutStream *stream) { _stream = stream; } + void ReleaseStream() { _stream.Release(); } + void Init(bool calculate = true) + { + _size = 0; + _calculate = calculate; + _crc = 0; + } + void EnableCalc(bool calculate) { _calculate = calculate; } + void InitCRC() { _crc = 0; } + UInt64 GetSize() const { return _size; } + UInt32 GetCRC() const { return _crc; } +}; + +STDMETHODIMP COutStreamWithSum::Write(const void *data, UInt32 size, UInt32 *processedSize) +{ + HRESULT result = S_OK; + if (_stream) + result = _stream->Write(data, size, &size); + if (_calculate) + { + UInt32 crc = 0; + for (UInt32 i = 0; i < size; i++) + crc += (UInt32)(((const Byte *)data)[i]); + _crc += crc; + } + if (processedSize) + *processedSize = size; + return result; +} + 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) @@ -568,6 +726,9 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, CMyComPtr<ISequentialInStream> inStream(streamSpec); streamSpec->SetStream(_stream); + COutStreamWithSum *outStreamSumSpec = new COutStreamWithSum; + CMyComPtr<ISequentialOutStream> outStreamSum(outStreamSumSpec); + for (i = 0; i < numItems; i++) { lps->InSize = lps->OutSize = currentTotalSize; @@ -577,7 +738,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; Int32 index = allFilesMode ? i : indices[i]; - const CItemEx &item = _items[index]; + const CItem &item = _items[index]; RINOK(extractCallback->GetStream(index, &outStream, askMode)); currentTotalSize += item.Size; if (item.IsDir()) @@ -588,19 +749,23 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, } if (!testMode && !outStream) continue; + outStreamSumSpec->Init(item.IsCrcFormat()); + outStreamSumSpec->SetStream(outStream); + outStream.Release(); + RINOK(extractCallback->PrepareOperation(askMode)); - if (testMode) - { - RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); - continue; - } RINOK(_stream->Seek(item.GetDataPosition(), STREAM_SEEK_SET, NULL)); streamSpec->Init(item.Size); - RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); - outStream.Release(); - RINOK(extractCallback->SetOperationResult((copyCoderSpec->TotalSize == item.Size) ? - NExtract::NOperationResult::kOK: - NExtract::NOperationResult::kDataError)); + RINOK(copyCoder->Code(inStream, outStreamSum, NULL, NULL, progress)); + outStreamSumSpec->ReleaseStream(); + Int32 res = NExtract::NOperationResult::kDataError; + if (copyCoderSpec->TotalSize == item.Size) + { + res = NExtract::NOperationResult::kOK; + if (item.IsCrcFormat() && item.ChkSum != outStreamSumSpec->GetCRC()) + res = NExtract::NOperationResult::kCRCError; + } + RINOK(extractCallback->SetOperationResult(res)); } return S_OK; COM_TRY_END @@ -609,15 +774,24 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) { COM_TRY_BEGIN - const CItemEx &item = _items[index]; + const CItem &item = _items[index]; return CreateLimitedInStream(_stream, item.GetDataPosition(), item.Size, stream); COM_TRY_END } -static IInArchive *CreateArc() { return new NArchive::NCpio::CHandler; } +IMP_CreateArcIn static CArcInfo g_ArcInfo = - { L"Cpio", L"cpio", 0, 0xED, { 0 }, 0, false, CreateArc, 0 }; + { "Cpio", "cpio", 0, 0xED, + 3 + 5 + 2 + 2, + { + 5, '0', '7', '0', '7', '0', + 2, kMagicBin0, kMagicBin1, + 2, kMagicBin1, kMagicBin0, + }, + 0, + NArcInfoFlags::kMultiSignature, + CreateArc, NULL, IsArc_Cpio }; REGISTER_ARC(Cpio) |