diff options
author | Igor Pavlov <ipavlov@users.sourceforge.net> | 2016-05-10 03:00:00 +0300 |
---|---|---|
committer | Kornel LesiĆski <kornel@geekhood.net> | 2016-05-28 02:16:59 +0300 |
commit | 66ac98bb02ac0fadd2a0e3266ea39279984580a3 (patch) | |
tree | c14c790212474e8b51df443d686b202cfe6b2315 /CPP/7zip/Archive | |
parent | c20d013055085bf49b4c93b2c617030b10c1fb4d (diff) |
16.0016.00
Diffstat (limited to 'CPP/7zip/Archive')
28 files changed, 1880 insertions, 587 deletions
diff --git a/CPP/7zip/Archive/7z/7zIn.cpp b/CPP/7zip/Archive/7z/7zIn.cpp index fc527be6..0634768b 100644 --- a/CPP/7zip/Archive/7z/7zIn.cpp +++ b/CPP/7zip/Archive/7z/7zIn.cpp @@ -93,6 +93,8 @@ void CStreamSwitch::Set(CInArchive *archive, const CObjectVector<CByteBuffer> *d Byte external = archive->ReadByte(); if (external != 0) { + if (!dataVector) + ThrowIncorrect(); CNum dataIndex = archive->ReadNum(); if (dataIndex >= dataVector->Size()) ThrowIncorrect(); @@ -761,6 +763,8 @@ void CInArchive::ReadUnpackInfo( folders.FoToCoderUnpackSizes[fo] = numCodersOutStreams; numCodersOutStreams += numCoders; folders.FoStartPackStreamIndex[fo] = packStreamIndex; + if (numPackStreams > folders.NumPackStreams - packStreamIndex) + ThrowIncorrect(); packStreamIndex += numPackStreams; folders.FoToMainUnpackSizeIndex[fo] = (Byte)indexOfMainStream; } @@ -770,6 +774,8 @@ void CInArchive::ReadUnpackInfo( folders.FoStartPackStreamIndex[fo] = packStreamIndex; folders.FoCodersDataOffset[fo] = _inByteBack->GetPtr() - startBufPtr; folders.CodersData.CopyFrom(startBufPtr, dataSize); + + // if (folders.NumPackStreams != packStreamIndex) ThrowUnsupported(); } WaitId(NID::kCodersUnpackSize); diff --git a/CPP/7zip/Archive/7z/7zUpdate.cpp b/CPP/7zip/Archive/7z/7zUpdate.cpp index 7e9478fc..e1c7aad8 100644 --- a/CPP/7zip/Archive/7z/7zUpdate.cpp +++ b/CPP/7zip/Archive/7z/7zUpdate.cpp @@ -380,29 +380,33 @@ static inline bool IsExeFilter(CMethodId m) return false; } -static unsigned Get_FilterGroup_for_Folder(CRecordVector<CFilterMode2> &filters, const CFolderEx &f) +static unsigned Get_FilterGroup_for_Folder( + CRecordVector<CFilterMode2> &filters, const CFolderEx &f, bool extractFilter) { CFilterMode2 m; m.Id = 0; m.Delta = 0; m.Encrypted = f.IsEncrypted(); - const CCoderInfo &coder = f.Coders[f.UnpackCoder]; - - if (coder.MethodID == k_Delta) + if (extractFilter) { - if (coder.Props.Size() == 1) + const CCoderInfo &coder = f.Coders[f.UnpackCoder]; + + if (coder.MethodID == k_Delta) { - m.Delta = (unsigned)coder.Props[0] + 1; - m.Id = k_Delta; + if (coder.Props.Size() == 1) + { + m.Delta = (unsigned)coder.Props[0] + 1; + m.Id = k_Delta; + } + } + else if (IsExeFilter(coder.MethodID)) + { + m.Id = (UInt32)coder.MethodID; + if (m.Id == k_BCJ2) + m.Id = k_BCJ; + m.SetDelta(); } - } - else if (IsExeFilter(coder.MethodID)) - { - m.Id = (UInt32)coder.MethodID; - if (m.Id == k_BCJ2) - m.Id = k_BCJ; - m.SetDelta(); } return GetGroup(filters, m); @@ -1577,7 +1581,7 @@ HRESULT Update( return E_NOTIMPL; */ - UInt64 startBlockSize = db != 0 ? db->ArcInfo.StartPosition: 0; + UInt64 startBlockSize = db ? db->ArcInfo.StartPosition: 0; if (startBlockSize > 0 && !options.RemoveSfxBlock) { RINOK(WriteRange(inStream, seqOutStream, 0, startBlockSize, NULL)); @@ -1591,8 +1595,21 @@ HRESULT Update( CRecordVector<CFilterMode2> filters; CObjectVector<CSolidGroup> groups; bool thereAreRepacks = false; + + bool useFilters = options.UseFilters; + if (useFilters) + { + const CCompressionMethodMode &method = *options.Method; + + FOR_VECTOR (i, method.Methods) + if (IsFilterMethod(method.Methods[i].Id)) + { + useFilters = false; + break; + } + } - if (db != 0) + if (db) { fileIndexToUpdateIndexMap.Alloc(db->Files.Size()); unsigned i; @@ -1638,16 +1655,18 @@ HRESULT Update( CFolderEx f; db->ParseFolderEx(i, f); - bool isEncrypted = f.IsEncrypted(); - - unsigned groupIndex = Get_FilterGroup_for_Folder(filters, f); + const bool isEncrypted = f.IsEncrypted(); + const bool needCopy = (numCopyItems == numUnpackStreams); + const bool extractFilter = (useFilters || needCopy); + + unsigned groupIndex = Get_FilterGroup_for_Folder(filters, f, extractFilter); while (groupIndex >= groups.Size()) groups.AddNew(); groups[groupIndex].folderRefs.Add(rep); - if (numCopyItems == numUnpackStreams) + if (needCopy) complexity += db->GetFolderFullPackSize(i); else { @@ -1732,22 +1751,8 @@ HRESULT Update( // ---------- Split files to groups ---------- - bool useFilters = options.UseFilters; const CCompressionMethodMode &method = *options.Method; - if (useFilters) - for (i = 0; i < method.Methods.Size(); i++) - if (IsFilterMethod(method.Methods[i].Id)) - { - useFilters = false; - break; - } - - /* - if (!method.Bonds.IsEmpty()) - useFilters = false; - */ - for (i = 0; i < updateItems.Size(); i++) { const CUpdateItem &ui = updateItems[i]; @@ -2156,7 +2161,13 @@ HRESULT Update( #ifndef _7ZIP_ST if (options.MultiThreadMixer) { + // 16.00: hang was fixed : for case if decoding was not finished. + // We close CBinderInStream and it calls CStreamBinder::CloseRead() + inStreamSizeCount.Release(); + sbInStream.Release(); + threadDecoder.WaitExecuteFinish(); + HRESULT decodeRes = threadDecoder.Result; // if (res == k_My_HRESULT_CRC_ERROR) if (decodeRes == S_FALSE) diff --git a/CPP/7zip/Archive/Cab/CabHandler.cpp b/CPP/7zip/Archive/Cab/CabHandler.cpp index 9c841545..31aa5e1d 100644 --- a/CPP/7zip/Archive/Cab/CabHandler.cpp +++ b/CPP/7zip/Archive/Cab/CabHandler.cpp @@ -348,15 +348,19 @@ STDMETHODIMP CHandler::Open(IInStream *inStream, CMyComPtr<IInStream> nextStream = inStream; bool prevChecked = false; + UString startVolName; + bool startVolName_was_Requested = false; UInt64 numItems = 0; unsigned numTempVolumes = 0; // try { - while (nextStream != NULL) + while (nextStream) { CDatabaseEx db; db.Stream = nextStream; + HRESULT res = archive.Open(db, maxCheckStartPosition); + _errorInHeaders |= archive.HeaderError; _errorInHeaders |= archive.ErrorInNames; _unexpectedEnd |= archive.UnexpectedEnd; @@ -426,6 +430,7 @@ STDMETHODIMP CHandler::Open(IInStream *inStream, for (;;) { const COtherArc *otherArc = NULL; + if (!prevChecked) { if (numTempVolumes == 0) @@ -449,18 +454,35 @@ STDMETHODIMP CHandler::Open(IInStream *inStream, } } } + if (!otherArc) { const CInArcInfo &ai = m_Database.Volumes.Back().ArcInfo; if (ai.IsThereNext()) otherArc = &ai.NextArc; } + if (!otherArc) break; if (!openVolumeCallback) break; // printf("\n%s", otherArc->FileName); const UString fullName = MultiByteToUnicodeString(otherArc->FileName, CP_ACP); + + if (!startVolName_was_Requested) + { + // some "bad" cab example can contain the link to itself. + startVolName_was_Requested = true; + { + NCOM::CPropVariant prop; + RINOK(openVolumeCallback->GetProperty(kpidName, &prop)); + if (prop.vt == VT_BSTR) + startVolName = prop.bstrVal; + } + if (fullName == startVolName) + break; + } + HRESULT result = openVolumeCallback->GetStream(fullName, &nextStream); if (result == S_OK) break; diff --git a/CPP/7zip/Archive/ComHandler.cpp b/CPP/7zip/Archive/ComHandler.cpp index e39dbab5..2798bd71 100644 --- a/CPP/7zip/Archive/ComHandler.cpp +++ b/CPP/7zip/Archive/ComHandler.cpp @@ -578,14 +578,16 @@ HRESULT CDatabase::Open(IInStream *inStream) if (item.IsDir() || numCabs > 1) continue; bool isMsiName; - UString msiName = ConvertName(item.Name, isMsiName); + const UString msiName = ConvertName(item.Name, isMsiName); if (isMsiName && !msiName.IsEmpty()) { - bool isThereExt = (msiName.Find(L'.') >= 0); + // bool isThereExt = (msiName.Find(L'.') >= 0); bool isMsiSpec = (msiName[0] == k_Msi_SpecChar); if (msiName.Len() >= 4 && StringsAreEqualNoCase_Ascii(msiName.RightPtr(4), ".cab") || !isMsiSpec && msiName.Len() >= 3 && StringsAreEqualNoCase_Ascii(msiName.RightPtr(3), "exe") - || !isMsiSpec && !isThereExt) + // || !isMsiSpec && !isThereExt + ) + { numCabs++; MainSubfile = i; diff --git a/CPP/7zip/Archive/DmgHandler.cpp b/CPP/7zip/Archive/DmgHandler.cpp index 2a80e255..09952ac8 100644 --- a/CPP/7zip/Archive/DmgHandler.cpp +++ b/CPP/7zip/Archive/DmgHandler.cpp @@ -34,48 +34,35 @@ static const Byte k_Base64Table[256] = { - 64,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, - 77,77,77,77,77,77,77,77,77,77,77,62,77,64,77,63,52,53,54,55,56,57,58,59,60,61,77,77,77,77,77,77, - 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,77,77,77,77,77, - 77,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,77,77,77,77,77, - 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, - 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, - 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, - 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77 + 66,77,77,77,77,77,77,77,77,65,65,77,77,65,77,77, + 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, + 65,77,77,77,77,77,77,77,77,77,77,62,77,77,77,63, + 52,53,54,55,56,57,58,59,60,61,77,77,77,64,77,77, + 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, + 15,16,17,18,19,20,21,22,23,24,25,77,77,77,77,77, + 77,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, + 41,42,43,44,45,46,47,48,49,50,51,77,77,77,77,77, + 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, + 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, + 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, + 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, + 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, + 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, + 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77, + 77,77,77,77,77,77,77,77,77,77,77,77,77,77,77,77 }; static Byte *Base64ToBin(Byte *dest, const char *src) { UInt32 val = 1; - UInt32 c = k_Base64Table[(Byte)(*src++)]; for (;;) { - /* - UInt32 c = (Byte)(*src++); - if (c >= 'A') - { - if (c <= 'Z') c -= 'A'; - else if (c >= 'a' && c <= 'z') c -= 'a' - 26; - else continue; - } - else if (c >= '0') - { - if (c <= '9') c += 52 - '0'; - else if (c == '=') break; - else continue; - } - else if (c == '+') c = 62; - else if (c == '/') c = 63; - else if (c == 0) break; - else continue; - */ + UInt32 c = k_Base64Table[(Byte)(*src++)]; - // UInt32 c = k_Base64Table[(Byte)(*src++)]; if (c < 64) { val = (val << 6) | c; - c = k_Base64Table[(Byte)(*src++)]; if ((val & ((UInt32)1 << 24)) == 0) continue; dest[0] = (Byte)(val >> 16); @@ -85,19 +72,41 @@ static Byte *Base64ToBin(Byte *dest, const char *src) val = 1; continue; } - if (c == 64) + + if (c == 65) // space + continue; + + if (c == 64) // '=' break; - c = k_Base64Table[(Byte)(*src++)]; + + if (c == 66 && val == 1) // end of string + return dest; + + return NULL; } - if (val >= ((UInt32)1 << 12)) + if (val < (1 << 12)) + return NULL; + + if (val & (1 << 18)) { - if (val >= ((UInt32)1 << 18)) - *dest++ = (Byte)(val >> 16); - *dest++ = (Byte)(val); + *dest++ = (Byte)(val >> 10); + *dest++ = (Byte)(val >> 2); + } + else if (k_Base64Table[(Byte)(*src++)] != 64) // '=' + return NULL; + else + *dest++ = (Byte)(val >> 4); + + for (;;) + { + Byte c = k_Base64Table[(Byte)(*src++)]; + if (c == 65) // space + continue; + if (c == 66) // end of string + return dest; + return NULL; } - - return dest; } @@ -720,7 +729,13 @@ HRESULT CHandler::Open2(IInStream *stream) return S_FALSE; destLen = dataString->Len() / 4 * 3 + 4; rawBuf.Alloc(destLen); - destLen = (unsigned)(Base64ToBin(rawBuf, *dataString) - rawBuf); + { + const Byte *endPtr = Base64ToBin(rawBuf, *dataString); + if (!endPtr) + return S_FALSE; + destLen = (unsigned)(endPtr - rawBuf); + } + #ifdef DMG_SHOW_RAW CExtraFile &extra = _extras.AddNew(); { diff --git a/CPP/7zip/Archive/GptHandler.cpp b/CPP/7zip/Archive/GptHandler.cpp index e54b1477..53e941c7 100644 --- a/CPP/7zip/Archive/GptHandler.cpp +++ b/CPP/7zip/Archive/GptHandler.cpp @@ -87,6 +87,9 @@ struct CPartType static const CPartType kPartTypes[] = { // { 0x0, 0, "Unused" }, + + { 0x21686148, 0, "BIOS Boot" }, + { 0xC12A7328, 0, "EFI System" }, { 0x024DEE41, 0, "MBR" }, @@ -98,10 +101,13 @@ static const CPartType kPartTypes[] = // { 0x37AFFC90, 0, "IBM GPFS" }, // { 0xE75CAF8F, 0, "Windows Storage Spaces" }, - { 0x83BD6B9D, 0, "FreeBSD Boot" }, + { 0x0FC63DAF, 0, "Linux Data" }, + { 0x0657FD6D, 0, "Linux Swap" }, + + { 0x83BD6B9D, 0, "FreeBSD Boot" }, { 0x516E7CB4, 0, "FreeBSD Data" }, { 0x516E7CB5, 0, "FreeBSD Swap" }, - { 0x516E7CB6, "ufs", "FreeBSD UFS" }, + { 0x516E7CB6, "ufs", "FreeBSD UFS" }, { 0x516E7CB8, 0, "FreeBSD Vinum" }, { 0x516E7CB8, "zfs", "FreeBSD ZFS" }, @@ -335,6 +341,12 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val break; s += c; } + if (s.IsEmpty()) + { + char temp[16]; + ConvertUInt32ToString(index, temp); + s.AddAscii(temp); + } { int typeIndex = FindPartType(item.Type); s += L'.'; diff --git a/CPP/7zip/Archive/HfsHandler.cpp b/CPP/7zip/Archive/HfsHandler.cpp index 8459280d..057a2d79 100644 --- a/CPP/7zip/Archive/HfsHandler.cpp +++ b/CPP/7zip/Archive/HfsHandler.cpp @@ -987,7 +987,9 @@ HRESULT CDatabase::LoadCatalog(const CFork &fork, const CObjectVector<CIdExtents item.GroupID = Get32(r + 0x24); item.AdminFlags = r[0x28]; item.OwnerFlags = r[0x29]; + */ item.FileMode = Get16(r + 0x2A); + /* item.special.iNodeNum = Get16(r + 0x2C); // or .linkCount item.FileType = Get32(r + 0x30); item.FileCreator = Get32(r + 0x34); @@ -1572,6 +1574,9 @@ HRESULT CHandler::ExtractZlibFile( UInt32 size = GetUi32(tableBuf + i * 8 + 4); + if (size > buf.Size() || size > kCompressionBlockSize + 1) + return S_FALSE; + RINOK(ReadStream_FALSE(inStream, buf, size)); if ((buf[0] & 0xF) == 0xF) diff --git a/CPP/7zip/Archive/Iso/IsoItem.h b/CPP/7zip/Archive/Iso/IsoItem.h index 2603afdb..ce154264 100644 --- a/CPP/7zip/Archive/Iso/IsoItem.h +++ b/CPP/7zip/Archive/Iso/IsoItem.h @@ -79,7 +79,7 @@ struct CDirRecord while (rem >= 5) { unsigned len = p[2]; - if (len > rem) + if (len < 3 || len > rem) return 0; if (p[0] == 'N' && p[1] == 'M' && p[3] == 1) { diff --git a/CPP/7zip/Archive/LzmaHandler.cpp b/CPP/7zip/Archive/LzmaHandler.cpp index e4ac2a42..121cd67c 100644 --- a/CPP/7zip/Archive/LzmaHandler.cpp +++ b/CPP/7zip/Archive/LzmaHandler.cpp @@ -110,7 +110,7 @@ HRESULT CDecoder::Create(bool filteredMode, ISequentialInStream *inStream) { _filterCoder = new CFilterCoder(false); CMyComPtr<ICompressCoder> coder = _filterCoder; - _filterCoder->Filter = new CBcjCoder(false); + _filterCoder->Filter = new NCompress::NBcj::CCoder(false); _bcjStream = _filterCoder; } } diff --git a/CPP/7zip/Archive/Nsis/NsisDecode.cpp b/CPP/7zip/Archive/Nsis/NsisDecode.cpp index 68243129..0bbf6094 100644 --- a/CPP/7zip/Archive/Nsis/NsisDecode.cpp +++ b/CPP/7zip/Archive/Nsis/NsisDecode.cpp @@ -59,7 +59,7 @@ HRESULT CDecoder::Init(ISequentialInStream *inStream, bool &useFilter) { _filter = new CFilterCoder(false); _filterInStream = _filter; - _filter->Filter = new CBcjCoder(false); + _filter->Filter = new NCompress::NBcj::CCoder(false); } RINOK(_filter->SetInStream(_codecInStream)); _decoderInStream = _filterInStream; diff --git a/CPP/7zip/Archive/Nsis/NsisIn.cpp b/CPP/7zip/Archive/Nsis/NsisIn.cpp index 6ca87df4..e04a97a8 100644 --- a/CPP/7zip/Archive/Nsis/NsisIn.cpp +++ b/CPP/7zip/Archive/Nsis/NsisIn.cpp @@ -88,7 +88,7 @@ enum EW_SENDMESSAGE, // SendMessage EW_ISWINDOW, // IsWindow EW_GETDLGITEM, // GetDlgItem - EW_SETCTLCOLORS, // SerCtlColors + EW_SETCTLCOLORS, // SetCtlColors EW_SETBRANDINGIMAGE, // SetBrandingImage EW_CREATEFONT, // CreateFont EW_SHOWWINDOW, // ShowWindow, EnableWindow, HideWindow @@ -1155,9 +1155,13 @@ bool CInArchive::IsGoodString(UInt32 param) const if (param == 0) return true; const Byte *p = _data + _stringsPos; + unsigned c; if (IsUnicode) - return (Get16(p + param * 2 - 2)) == 0; - return p[param - 1] == 0; + c = Get16(p + param * 2 - 2); + else + c = p[param - 1]; + // some files have '\\' character before string? + return (c == 0 || c == '\\'); } bool CInArchive::AreTwoParamStringsEqual(UInt32 param1, UInt32 param2) const @@ -1509,7 +1513,7 @@ static const UInt32 CMD_REF_Pre = (1 << 2); static const UInt32 CMD_REF_Show = (1 << 3); static const UInt32 CMD_REF_Leave = (1 << 4); static const UInt32 CMD_REF_OnFunc = (1 << 5); -static const UInt32 CMD_REF_Section = (1 << 6); +static const UInt32 CMD_REF_Section = (1 << 6); static const UInt32 CMD_REF_InitPluginDir = (1 << 7); // static const UInt32 CMD_REF_Creator = (1 << 5); // _Pre is used instead static const unsigned CMD_REF_OnFunc_NumShifts = 28; // it uses for onFunc too @@ -3357,7 +3361,7 @@ HRESULT CInArchive::ReadEntries(const CBlockHeader &bh) #ifdef NSIS_SCRIPT s += isSetOutPath ? "SetOutPath" : "CreateDirectory"; - AddParam(params[0]); + AddParam(params[0]); #endif break; @@ -3571,15 +3575,16 @@ HRESULT CInArchive::ReadEntries(const CBlockHeader &bh) AddParam(params[0]); - SmallSpaceComment(); - /* for (int i = 1; i < 3; i++) AddParam_UInt(params[i]); */ if (params[3] != 0) + { + SmallSpaceComment(); AddParam(params[3]); + } #endif diff --git a/CPP/7zip/Archive/Nsis/NsisIn.h b/CPP/7zip/Archive/Nsis/NsisIn.h index 498784b8..d8e6808b 100644 --- a/CPP/7zip/Archive/Nsis/NsisIn.h +++ b/CPP/7zip/Archive/Nsis/NsisIn.h @@ -421,7 +421,11 @@ public: const char *kRemoveStr = "$INSTDIR\\"; if (s.IsPrefixedBy_Ascii_NoCase(kRemoveStr)) + { s.Delete(0, MyStringLen(kRemoveStr)); + if (s[0] == L'\\') + s.DeleteFrontal(1); + } if (item.IsUninstaller && ExeStub.Size() == 0) s += L".nsis"; return s; diff --git a/CPP/7zip/Archive/NtfsHandler.cpp b/CPP/7zip/Archive/NtfsHandler.cpp index fa4bbc9c..25f4c8a6 100644 --- a/CPP/7zip/Archive/NtfsHandler.cpp +++ b/CPP/7zip/Archive/NtfsHandler.cpp @@ -1148,8 +1148,16 @@ bool CMftRec::Parse(Byte *p, unsigned sectorSizeLog, UInt32 numSectors, UInt32 r return false; if (usaOffset >= 0x30) // NTFS 3.1+ - if (Get32(p + 0x2C) != recNumber) - return false; + { + UInt32 iii = Get32(p + 0x2C); + if (iii != recNumber) + { + // ntfs-3g probably writes 0 (that probably is incorrect value) to this field for unused records. + // so we support that "bad" case. + if (iii != 0) + return false; + } + } UInt16 usn = Get16(p + usaOffset); // PRF(printf("\nusn = %d", usn)); diff --git a/CPP/7zip/Archive/PeHandler.cpp b/CPP/7zip/Archive/PeHandler.cpp index 28562007..160648f6 100644 --- a/CPP/7zip/Archive/PeHandler.cpp +++ b/CPP/7zip/Archive/PeHandler.cpp @@ -347,8 +347,8 @@ struct CSection CSection(): IsRealSect(false), IsDebug(false), IsAdditionalSection(false) {} - // const UInt32 GetSize() const { return PSize; } - const UInt32 GetSize() const { return MyMin(PSize, VSize); } + const UInt32 GetSizeExtract() const { return PSize; } + const UInt32 GetSizeMin() const { return MyMin(PSize, VSize); } void UpdateTotalSize(UInt32 &totalSize) const { @@ -362,8 +362,8 @@ struct CSection int Compare(const CSection &s) const { RINOZ(MyCompare(Pa, s.Pa)); - UInt32 size1 = GetSize(); - UInt32 size2 = s.GetSize(); + UInt32 size1 = GetSizeExtract(); + UInt32 size2 = s.GetSizeExtract(); return MyCompare(size1, size2); } }; @@ -1045,7 +1045,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val switch (propID) { case kpidPath: prop = MultiByteToUnicodeString(item.Name); break; - case kpidSize: prop = (UInt64)item.GetSize(); break; + case kpidSize: prop = (UInt64)item.PSize; break; case kpidPackSize: prop = (UInt64)item.PSize; break; case kpidVirtualSize: prop = (UInt64)item.VSize; break; case kpidOffset: prop = item.Pa; break; @@ -1898,7 +1898,7 @@ static bool ParseVersion(const Byte *p, UInt32 size, CTextFile &f, CObjectVector HRESULT CHandler::OpenResources(unsigned sectionIndex, IInStream *stream, IArchiveOpenCallback *callback) { const CSection § = _sections[sectionIndex]; - const size_t fileSize = sect.GetSize(); + const size_t fileSize = sect.GetSizeMin(); if (fileSize > kFileSizeMax) return S_FALSE; @@ -1926,6 +1926,7 @@ HRESULT CHandler::OpenResources(unsigned sectionIndex, IInStream *stream, IArchi _oneLang = true; bool stringsOk = true; size_t maxOffset = 0; + FOR_VECTOR (i, specItems) { const CTableItem &item1 = specItems[i]; @@ -2007,6 +2008,7 @@ HRESULT CHandler::OpenResources(unsigned sectionIndex, IInStream *stream, IArchi } // PrintError("ver.Parse error"); } + item.Enabled = true; _items.Add(item); } @@ -2041,7 +2043,10 @@ HRESULT CHandler::OpenResources(unsigned sectionIndex, IInStream *stream, IArchi UInt32 mask = (1 << numBits) - 1; size_t end = ((maxOffset + mask) & ~mask); - if (/* end < sect.VSize && */ end <= sect.GetSize()) + // PSize can be much larger than VSize in some exe installers. + // it contains archive data after PE resources. + // So we need to use PSize here! + if (/* end < sect.VSize && */ end <= sect.PSize) { CSection sect2; sect2.Flags = 0; @@ -2059,7 +2064,7 @@ HRESULT CHandler::OpenResources(unsigned sectionIndex, IInStream *stream, IArchi // 9.29: we use sect.PSize instead of sect.VSize to support some CAB-SFX // the code for .rsrc_2 is commented. - sect2.PSize = sect.GetSize() - (UInt32)maxOffset; + sect2.PSize = sect.PSize - (UInt32)maxOffset; if (sect2.PSize != 0) { @@ -2473,7 +2478,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, else if (mixItem.ResourceIndex >= 0) size = _items[mixItem.ResourceIndex].GetSize(); else - size = _sections[mixItem.SectionIndex].GetSize(); + size = _sections[mixItem.SectionIndex].GetSizeExtract(); totalSize += size; } extractCallback->SetTotal(totalSize); @@ -2549,7 +2554,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, } else { - currentItemSize = sect.GetSize(); + currentItemSize = sect.GetSizeExtract(); if (!testMode && !outStream) continue; diff --git a/CPP/7zip/Archive/Rar/Rar5Handler.cpp b/CPP/7zip/Archive/Rar/Rar5Handler.cpp index 131f2c9b..959c2e36 100644 --- a/CPP/7zip/Archive/Rar/Rar5Handler.cpp +++ b/CPP/7zip/Archive/Rar/Rar5Handler.cpp @@ -2333,6 +2333,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, { UInt64 total = 0; + bool isThereUndefinedSize = false; bool thereAreLinks = false; { @@ -2342,9 +2343,14 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, unsigned index = allFilesMode ? t : indices[t]; const CRefItem &ref = _refs[index]; const CItem &item = _items[ref.Item]; + const CItem &lastItem = _items[ref.Last]; extractStatuses[index] |= kStatus_Extract; - total += item.Size; + + if (!lastItem.Is_UnknownSize()) + total += lastItem.Size; + else + isThereUndefinedSize = true; if (ref.Link >= 0) { @@ -2352,11 +2358,18 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, { if ((unsigned)ref.Link < index) { - const CItem &linkItem = _items[_refs[(unsigned)ref.Link].Item]; + const CRefItem &linkRef = _refs[(unsigned)ref.Link]; + const CItem &linkItem = _items[linkRef.Item]; if (linkItem.IsSolid() && linkItem.Size <= k_CopyLinkFile_MaxSize) { if (extractStatuses[(unsigned)ref.Link] == 0) - total += linkItem.Size; + { + const CItem &lastLinkItem = _items[linkRef.Last]; + if (!lastLinkItem.Is_UnknownSize()) + total += lastLinkItem.Size; + else + isThereUndefinedSize = true; + } extractStatuses[(unsigned)ref.Link] |= kStatus_Link; thereAreLinks = true; } @@ -2375,11 +2388,18 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, while (j > solidLimit) { j--; - const CItem &item2 = _items[_refs[j].Item]; + const CRefItem &ref2 = _refs[j]; + const CItem &item2 = _items[ref2.Item]; if (!item2.IsService()) { if (extractStatuses[j] == 0) - total += item2.Size; + { + const CItem &lastItem2 = _items[ref2.Last]; + if (!lastItem2.Is_UnknownSize()) + total += lastItem2.Size; + else + isThereUndefinedSize = true; + } extractStatuses[j] |= kStatus_Skip; if (!item2.IsSolid()) break; @@ -2415,13 +2435,20 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, while (j > solidLimit) { j--; - const CItem &item2 = _items[_refs[j].Item]; + const CRefItem &ref2 = _refs[j]; + const CItem &item2 = _items[ref2.Item]; if (!item2.IsService()) { if (extractStatuses[j] != 0) break; extractStatuses[j] = kStatus_Skip; - total += item2.Size; + { + const CItem &lastItem2 = _items[ref2.Last]; + if (!lastItem2.Is_UnknownSize()) + total += lastItem2.Size; + else + isThereUndefinedSize = true; + } if (!item2.IsSolid()) break; } @@ -2449,7 +2476,10 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, } } - RINOK(extractCallback->SetTotal(total)); + if (total != 0 || !isThereUndefinedSize) + { + RINOK(extractCallback->SetTotal(total)); + } } @@ -2502,8 +2532,12 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, const CRefItem *ref = &_refs[index]; const CItem *item = &_items[ref->Item]; + const CItem &lastItem = _items[ref->Last]; + + curUnpackSize = 0; + if (!lastItem.Is_UnknownSize()) + curUnpackSize = lastItem.Size; - curUnpackSize = item->Size; curPackSize = GetPackSize(index); RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); @@ -2532,11 +2566,15 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, { const CRefItem &ref2 = _refs[index2]; const CItem &item2 = _items[ref2.Item]; + const CItem &lastItem2 = _items[ref2.Last]; if (!item2.IsSolid()) { item = &item2; ref = &ref2; - curUnpackSize = item->Size; + if (!lastItem2.Is_UnknownSize()) + curUnpackSize = lastItem2.Size; + else + curUnpackSize = 0; curPackSize = GetPackSize(index2); } else if ((unsigned)index2 < index) diff --git a/CPP/7zip/Archive/Rar/RarHandler.cpp b/CPP/7zip/Archive/Rar/RarHandler.cpp index 3b5924b4..6f3e0a0c 100644 --- a/CPP/7zip/Archive/Rar/RarHandler.cpp +++ b/CPP/7zip/Archive/Rar/RarHandler.cpp @@ -953,6 +953,8 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val NCOM::CPropVariant prop; const CRefItem &refItem = _refItems[index]; const CItem &item = _items[refItem.ItemIndex]; + const CItem &lastItem = _items[refItem.ItemIndex + refItem.NumItems - 1]; + /* const CItem *mainItem = &item; if (item.BaseFileIndex >= 0) @@ -972,7 +974,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val break; } case kpidIsDir: prop = item.IsDir(); break; - case kpidSize: prop = item.Size; break; + case kpidSize: if (lastItem.Is_Size_Defined()) prop = lastItem.Size; break; case kpidPackSize: prop = GetPackSize(index); break; case kpidMTime: RarTimeToProp(item.MTime, prop); break; case kpidCTime: if (item.CTimeDefined) RarTimeToProp(item.CTime, prop); break; @@ -985,7 +987,6 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val case kpidSplitAfter: prop = _items[refItem.ItemIndex + refItem.NumItems - 1].IsSplitAfter(); break; case kpidCRC: { - const CItem &lastItem = _items[refItem.ItemIndex + refItem.NumItems - 1]; prop = ((lastItem.IsSplitAfter()) ? item.FileCRC : lastItem.FileCRC); break; } @@ -1378,34 +1379,52 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, CRecordVector<unsigned> importantIndexes; CRecordVector<bool> extractStatuses; + bool isThereUndefinedSize = false; + for (UInt32 t = 0; t < numItems; t++) { unsigned index = allFilesMode ? t : indices[t]; - const CRefItem &refItem = _refItems[index]; - const CItem &item = _items[refItem.ItemIndex]; - censoredTotalUnPacked += item.Size; - // censoredTotalPacked += item.PackSize; + + { + const CRefItem &refItem = _refItems[index]; + const CItem &item = _items[refItem.ItemIndex + refItem.NumItems - 1]; + + if (item.Is_Size_Defined()) + censoredTotalUnPacked += item.Size; + else + isThereUndefinedSize = true; + + // censoredTotalPacked += item.PackSize; + } + unsigned j; for (j = lastIndex; j <= index; j++) // if (!_items[_refItems[j].ItemIndex].IsSolid()) if (!IsSolid(j)) lastIndex = j; + for (j = lastIndex; j <= index; j++) { const CRefItem &refItem = _refItems[j]; - const CItem &item = _items[refItem.ItemIndex]; + const CItem &item = _items[refItem.ItemIndex + refItem.NumItems - 1]; - // const CItem &item = _items[j]; - - importantTotalUnPacked += item.Size; + if (item.Is_Size_Defined()) + importantTotalUnPacked += item.Size; + else + isThereUndefinedSize = true; // importantTotalPacked += item.PackSize; importantIndexes.Add(j); extractStatuses.Add(j == index); } + lastIndex = index + 1; } - RINOK(extractCallback->SetTotal(importantTotalUnPacked)); + if (importantTotalUnPacked != 0 || !isThereUndefinedSize) + { + RINOK(extractCallback->SetTotal(importantTotalUnPacked)); + } + UInt64 currentImportantTotalUnPacked = 0; UInt64 currentImportantTotalPacked = 0; UInt64 currentUnPackSize, currentPackSize; @@ -1431,13 +1450,19 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, lps->Init(extractCallback, false); bool solidStart = true; - for (unsigned i = 0; i < importantIndexes.Size(); i++, + + for (unsigned i = 0;; + i++, currentImportantTotalUnPacked += currentUnPackSize, currentImportantTotalPacked += currentPackSize) { lps->InSize = currentImportantTotalPacked; lps->OutSize = currentImportantTotalUnPacked; RINOK(lps->SetCur()); + + if (i >= importantIndexes.Size()) + break; + CMyComPtr<ISequentialOutStream> realOutStream; Int32 askMode; @@ -1452,8 +1477,15 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, const CRefItem &refItem = _refItems[index]; const CItem &item = _items[refItem.ItemIndex]; - - currentUnPackSize = item.Size; + const CItem &lastItem = _items[refItem.ItemIndex + refItem.NumItems - 1]; + + UInt64 outSize = (UInt64)(Int64)-1; + currentUnPackSize = 0; + if (lastItem.Is_Size_Defined()) + { + outSize = lastItem.Size; + currentUnPackSize = outSize; + } currentPackSize = GetPackSize(index); @@ -1678,12 +1710,14 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, continue; } - HRESULT result = commonCoder->Code(inStream, outStream, &packSize, &item.Size, progress); + HRESULT result = commonCoder->Code(inStream, outStream, &packSize, &outSize, progress); if (item.IsEncrypted()) filterStreamSpec->ReleaseInStream(); - const CItem &lastItem = _items[refItem.ItemIndex + refItem.NumItems - 1]; + if (outSize == (UInt64)(Int64)-1) + currentUnPackSize = outStreamSpec->GetSize(); + int opRes = (volsInStreamSpec->CrcIsOK && outStreamSpec->GetCRC() == lastItem.FileCRC) ? NExtract::NOperationResult::kOK: NExtract::NOperationResult::kCRCError; diff --git a/CPP/7zip/Archive/Rar/RarItem.h b/CPP/7zip/Archive/Rar/RarItem.h index 13daa1cb..10e21c57 100644 --- a/CPP/7zip/Archive/Rar/RarItem.h +++ b/CPP/7zip/Archive/Rar/RarItem.h @@ -42,6 +42,8 @@ struct CItem Byte Salt[8]; + bool Is_Size_Defined() const { return Size != (UInt64)(Int64)-1; } + bool IsEncrypted() const { return (Flags & NHeader::NFile::kEncrypted) != 0; } bool IsSolid() const { return (Flags & NHeader::NFile::kSolid) != 0; } bool IsCommented() const { return (Flags & NHeader::NFile::kComment) != 0; } diff --git a/CPP/7zip/Archive/SplitHandler.cpp b/CPP/7zip/Archive/SplitHandler.cpp index 23187064..4ca417e0 100644 --- a/CPP/7zip/Archive/SplitHandler.cpp +++ b/CPP/7zip/Archive/SplitHandler.cpp @@ -187,11 +187,15 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback) UInt64 size; { + /* NCOM::CPropVariant prop; RINOK(volumeCallback->GetProperty(kpidSize, &prop)); if (prop.vt != VT_UI8) return E_INVALIDARG; size = prop.uhVal.QuadPart; + */ + RINOK(stream->Seek(0, STREAM_SEEK_END, &size)); + RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); } _totalSize += size; @@ -217,11 +221,15 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *callback) if (!stream) break; { + /* NCOM::CPropVariant prop; RINOK(volumeCallback->GetProperty(kpidSize, &prop)); if (prop.vt != VT_UI8) return E_INVALIDARG; size = prop.uhVal.QuadPart; + */ + RINOK(stream->Seek(0, STREAM_SEEK_END, &size)); + RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); } _totalSize += size; _sizes.Add(size); diff --git a/CPP/7zip/Archive/Udf/UdfIn.cpp b/CPP/7zip/Archive/Udf/UdfIn.cpp index a051a271..908f2356 100644 --- a/CPP/7zip/Archive/Udf/UdfIn.cpp +++ b/CPP/7zip/Archive/Udf/UdfIn.cpp @@ -389,7 +389,10 @@ HRESULT CInArchive::ReadFileItem(int volIndex, int fsIndex, const CLongAllocDesc return S_FALSE; CFile &file = Files.Back(); const CLogVol &vol = LogVols[volIndex]; - CPartition &partition = Partitions[vol.PartitionMaps[lad.Location.PartitionRef].PartitionIndex]; + unsigned partitionRef = lad.Location.PartitionRef; + if (partitionRef >= vol.PartitionMaps.Size()) + return S_FALSE; + CPartition &partition = Partitions[vol.PartitionMaps[partitionRef].PartitionIndex]; UInt32 key = lad.Location.Pos; UInt32 value; diff --git a/CPP/7zip/Archive/Wim/WimHandler.cpp b/CPP/7zip/Archive/Wim/WimHandler.cpp index 910de06d..e6bc21f6 100644 --- a/CPP/7zip/Archive/Wim/WimHandler.cpp +++ b/CPP/7zip/Archive/Wim/WimHandler.cpp @@ -475,14 +475,10 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val case kpidPackSize: { - UInt64 size = 0; if (si) { if (!si->Resource.IsSolidSmall()) - { - size = si->Resource.PackSize; - prop = size; - } + prop = si->Resource.PackSize; else { if (si->Resource.SolidIndex >= 0) @@ -493,12 +489,14 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val } } } + else if (!item.IsDir) + prop = (UInt64)0; + break; } case kpidSize: { - UInt64 size = 0; if (si) { if (si->Resource.IsSolid()) @@ -507,22 +505,19 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val { if (si->Resource.SolidIndex >= 0) { - CSolid &ss = _db.Solids[(unsigned)si->Resource.SolidIndex]; + const CSolid &ss = _db.Solids[(unsigned)si->Resource.SolidIndex]; prop = ss.UnpackSize; } } else - { - size = si->Resource.PackSize; - prop = size; - } + prop = si->Resource.PackSize; } else - { - size = si->Resource.UnpackSize; - prop = size; - } + prop = si->Resource.UnpackSize; } + else if (!item.IsDir) + prop = (UInt64)0; + break; } diff --git a/CPP/7zip/Archive/XzHandler.cpp b/CPP/7zip/Archive/XzHandler.cpp index 11f4b444..318be190 100644 --- a/CPP/7zip/Archive/XzHandler.cpp +++ b/CPP/7zip/Archive/XzHandler.cpp @@ -10,6 +10,8 @@ #include "../../Common/Defs.h" #include "../../Common/IntToString.h" +#include "../../Windows/PropVariant.h" + #include "../ICoder.h" #include "../Common/CWrappers.h" @@ -21,7 +23,9 @@ #include "IArchive.h" +#ifndef EXTRACT_ONLY #include "Common/HandlerOut.h" +#endif #include "XzHandler.h" @@ -83,14 +87,19 @@ class CHandler: CMyComPtr<IInStream> _stream; CMyComPtr<ISequentialInStream> _seqStream; - UInt32 _filterId; AString _methodsString; + #ifndef EXTRACT_ONLY + + UInt32 _filterId; + void Init() { _filterId = 0; CMultiMethodProps::Init(); } + + #endif HRESULT Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCallback *callback); @@ -126,9 +135,12 @@ public: CHandler::CHandler() { + #ifndef EXTRACT_ONLY Init(); + #endif } + static const Byte kProps[] = { kpidSize, diff --git a/CPP/7zip/Archive/Zip/ZipHandler.cpp b/CPP/7zip/Archive/Zip/ZipHandler.cpp index 092dcbc9..75034de0 100644 --- a/CPP/7zip/Archive/Zip/ZipHandler.cpp +++ b/CPP/7zip/Archive/Zip/ZipHandler.cpp @@ -12,6 +12,7 @@ #include "../../IPassword.h" #include "../../Common/FilterCoder.h" +#include "../../Common/LimitedStreams.h" #include "../../Common/ProgressUtils.h" #include "../../Common/StreamObjects.h" #include "../../Common/StreamUtils.h" @@ -142,14 +143,19 @@ static const Byte kProps[] = kpidCRC, kpidMethod, kpidHostOS, - kpidUnpackVer + kpidUnpackVer, + kpidVolumeIndex }; static const Byte kArcProps[] = { kpidEmbeddedStubSize, kpidBit64, - kpidComment + kpidComment, + kpidTotalPhySize, + kpidIsVolume, + kpidVolumeIndex, + kpidNumVolumes }; CHandler::CHandler() @@ -175,18 +181,23 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { case kpidBit64: if (m_Archive.IsZip64) prop = m_Archive.IsZip64; break; case kpidComment: if (m_Archive.ArcInfo.Comment.Size() != 0) prop = MultiByteToUnicodeString(BytesToString(m_Archive.ArcInfo.Comment), CP_ACP); break; - case kpidPhySize: prop = m_Archive.ArcInfo.GetPhySize(); break; - case kpidOffset: /* if (m_Archive.ArcInfo.Base != 0) */ - prop = m_Archive.ArcInfo.Base; break; + + case kpidPhySize: prop = m_Archive.GetPhySize(); break; + case kpidOffset: prop = m_Archive.GetOffset(); break; case kpidEmbeddedStubSize: { - UInt64 stubSize = m_Archive.ArcInfo.GetEmbeddedStubSize(); + UInt64 stubSize = m_Archive.GetEmbeddedStubSize(); if (stubSize != 0) prop = stubSize; break; } + case kpidTotalPhySize: if (m_Archive.IsMultiVol) prop = m_Archive.Vols.GetTotalSize(); break; + case kpidVolumeIndex: if (m_Archive.IsMultiVol) prop = (UInt32)m_Archive.Vols.StartVolIndex; break; + case kpidIsVolume: if (m_Archive.IsMultiVol) prop = true; break; + case kpidNumVolumes: if (m_Archive.IsMultiVol) prop = (UInt32)m_Archive.Vols.Streams.Size(); break; + case kpidWarningFlags: { UInt32 v = 0; @@ -197,6 +208,18 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) break; } + case kpidError: + { + if (!m_Archive.Vols.MissingName.IsEmpty()) + { + UString s; + s.SetFromAscii("Missing volume : "); + s += m_Archive.Vols.MissingName; + prop = s; + } + break; + } + case kpidErrorFlags: { UInt32 v = 0; @@ -209,7 +232,7 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) but the stream has access only to zip part. In that case we ignore UnavailableStart error. maybe we must show warning in that case. */ - UInt64 stubSize = m_Archive.ArcInfo.GetEmbeddedStubSize(); + UInt64 stubSize = m_Archive.GetEmbeddedStubSize(); if (stubSize < (UInt64)-m_Archive.ArcInfo.Base) v |= kpv_ErrorFlags_UnavailableStart; } @@ -429,6 +452,10 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val case kpidUnpackVer: prop = (UInt32)item.ExtractVersion.Version; break; + + case kpidVolumeIndex: + prop = item.Disk; + break; } prop.Detach(value); @@ -436,30 +463,6 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val COM_TRY_END } -class CProgressImp: public CProgressVirt -{ - CMyComPtr<IArchiveOpenCallback> _callback; -public: - virtual HRESULT SetCompletedLocal(UInt64 numFiles, UInt64 numBytes); - virtual HRESULT SetTotalCD(UInt64 numFiles); - virtual HRESULT SetCompletedCD(UInt64 numFiles); - CProgressImp(IArchiveOpenCallback *callback): _callback(callback) {} -}; - -HRESULT CProgressImp::SetCompletedLocal(UInt64 numFiles, UInt64 numBytes) -{ - return _callback->SetCompleted(&numFiles, &numBytes); -} - -HRESULT CProgressImp::SetTotalCD(UInt64 numFiles) -{ - return _callback->SetTotal(&numFiles, NULL); -} - -HRESULT CProgressImp::SetCompletedCD(UInt64 numFiles) -{ - return _callback->SetCompleted(&numFiles, NULL); -} STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback) @@ -468,9 +471,13 @@ STDMETHODIMP CHandler::Open(IInStream *inStream, try { Close(); - RINOK(m_Archive.Open(inStream, maxCheckStartPosition)); - CProgressImp progressImp(callback); - return m_Archive.ReadHeaders(m_Items, callback ? &progressImp : NULL); + HRESULT res = m_Archive.Open(inStream, maxCheckStartPosition, callback, m_Items); + if (res != S_OK) + { + m_Items.Clear(); + m_Archive.ClearRefs(); + } + return res; } catch(...) { Close(); throw; } COM_TRY_END @@ -483,8 +490,6 @@ STDMETHODIMP CHandler::Close() return S_OK; } -////////////////////////////////////// -// CHandler::DecompressItems class CLzmaDecoder: public ICompressCoder, @@ -550,6 +555,8 @@ struct CMethodItem CMyComPtr<ICompressCoder> Coder; }; + + class CZipDecoder { NCrypto::NZip::CDecoder *_zipCryptoDecoderSpec; @@ -584,6 +591,24 @@ public: Int32 &res); }; + +static HRESULT SkipStreamData(ISequentialInStream *stream, UInt64 size) +{ + const size_t kBufSize = 1 << 12; + Byte buf[kBufSize]; + for (;;) + { + if (size == 0) + return S_OK; + size_t curSize = kBufSize; + if (curSize > size) + curSize = (size_t)size; + RINOK(ReadStream_FALSE(stream, buf, curSize)); + size -= curSize; + } +} + + HRESULT CZipDecoder::Decode( DECL_EXTERNAL_CODECS_LOC_VARS CInArchive &archive, const CItemEx &item, @@ -634,9 +659,11 @@ HRESULT CZipDecoder::Decode( outStreamSpec->SetStream(realOutStream); outStreamSpec->Init(needCRC); - UInt64 authenticationPos; - - CMyComPtr<ISequentialInStream> inStream; + CMyComPtr<ISequentialInStream> packStream; + + CLimitedSequentialInStream *limitedStreamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(limitedStreamSpec); + { UInt64 packSize = item.PackSize; if (wzAesMode) @@ -645,9 +672,14 @@ HRESULT CZipDecoder::Decode( return S_OK; packSize -= NCrypto::NWzAes::kMacSize; } - UInt64 dataPos = item.GetDataPosition(); - inStream.Attach(archive.CreateLimitedStream(dataPos, packSize)); - authenticationPos = dataPos + packSize; + RINOK(archive.GetItemStream(item, true, packStream)); + if (!packStream) + { + res = NExtract::NOperationResult::kUnavailable; + return S_OK; + } + limitedStreamSpec->SetStream(packStream); + limitedStreamSpec->Init(packSize); } CMyComPtr<ICompressFilter> cryptoFilter; @@ -912,9 +944,15 @@ HRESULT CZipDecoder::Decode( bool authOk = true; if (needCRC) crcOK = (outStreamSpec->GetCRC() == item.Crc); + if (wzAesMode) { - inStream.Attach(archive.CreateLimitedStream(authenticationPos, NCrypto::NWzAes::kMacSize)); + const UInt64 rem = limitedStreamSpec->GetRem(); + if (rem != 0) + if (SkipStreamData(inStream, rem) != S_OK) + authOk = false; + + limitedStreamSpec->Init(NCrypto::NWzAes::kMacSize); if (_wzAesDecoderSpec->CheckMac(inStream, authOk) != S_OK) authOk = false; } @@ -988,16 +1026,21 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnavailable)); continue; } + if (!item.FromLocal) { - HRESULT res = m_Archive.ReadLocalItemAfterCdItem(item); + bool isAvail = true; + HRESULT res = m_Archive.ReadLocalItemAfterCdItem(item, isAvail); if (res == S_FALSE) { if (item.IsDir() || realOutStream || testMode) { RINOK(extractCallback->PrepareOperation(askMode)); realOutStream.Release(); - RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kHeadersError)); + RINOK(extractCallback->SetOperationResult( + isAvail ? + NExtract::NOperationResult::kHeadersError : + NExtract::NOperationResult::kUnavailable)); } continue; } @@ -1034,6 +1077,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, RINOK(extractCallback->SetOperationResult(res)) } + lps->InSize = currentTotalPacked; lps->OutSize = currentTotalUnPacked; return lps->SetCur(); diff --git a/CPP/7zip/Archive/Zip/ZipHeader.h b/CPP/7zip/Archive/Zip/ZipHeader.h index 82e46eb6..fead0192 100644 --- a/CPP/7zip/Archive/Zip/ZipHeader.h +++ b/CPP/7zip/Archive/Zip/ZipHeader.h @@ -18,9 +18,8 @@ namespace NSignature const UInt32 kEcd = 0x06054B50; const UInt32 kEcd64 = 0x06064B50; const UInt32 kEcd64Locator = 0x07064B50; - - // const UInt32 kSpan = 0x08074B50; - const UInt32 kNoSpan = 0x30304b50; // PK00, replaces kSpan, if there is only 1 segment + const UInt32 kSpan = 0x08074B50; + const UInt32 kNoSpan = 0x30304B50; // PK00, replaces kSpan, if there is only 1 segment } const unsigned kLocalHeaderSize = 4 + 26; // including signature diff --git a/CPP/7zip/Archive/Zip/ZipIn.cpp b/CPP/7zip/Archive/Zip/ZipIn.cpp index 2ba7e3fa..fe5200f7 100644 --- a/CPP/7zip/Archive/Zip/ZipIn.cpp +++ b/CPP/7zip/Archive/Zip/ZipIn.cpp @@ -5,8 +5,11 @@ // #include <stdio.h> #include "../../../Common/DynamicBuffer.h" +#include "../../../Common/IntToString.h" +#include "../../../Common/StringToInt.h" + +#include "../../../Windows/PropVariant.h" -#include "../../Common/LimitedStreams.h" #include "../../Common/StreamUtils.h" #include "../IArchive.h" @@ -17,100 +20,133 @@ #define Get32(p) GetUi32(p) #define Get64(p) GetUi64(p) +#define G16(offs, v) v = Get16(p + (offs)) +#define G32(offs, v) v = Get32(p + (offs)) +#define G64(offs, v) v = Get64(p + (offs)) + namespace NArchive { namespace NZip { struct CEcd { - UInt16 thisDiskNumber; - UInt16 startCDDiskNumber; - UInt16 numEntriesInCDOnThisDisk; - UInt16 numEntriesInCD; - UInt32 cdSize; - UInt32 cdStartOffset; - UInt16 commentSize; + UInt16 ThisDisk; + UInt16 CdDisk; + UInt16 NumEntries_in_ThisDisk; + UInt16 NumEntries; + UInt32 Size; + UInt32 Offset; + UInt16 CommentSize; - void Parse(const Byte *p); - - bool IsEmptyArc() + bool IsEmptyArc() const { - return thisDiskNumber == 0 && startCDDiskNumber == 0 && - numEntriesInCDOnThisDisk == 0 && numEntriesInCD == 0 && cdSize == 0 - && cdStartOffset == 0 // test it + return ThisDisk == 0 + && CdDisk == 0 + && NumEntries_in_ThisDisk == 0 + && NumEntries == 0 + && Size == 0 + && Offset == 0 // test it ; } + + void Parse(const Byte *p); // (p) doesn't include signature }; void CEcd::Parse(const Byte *p) { - thisDiskNumber = Get16(p); - startCDDiskNumber = Get16(p + 2); - numEntriesInCDOnThisDisk = Get16(p + 4); - numEntriesInCD = Get16(p + 6); - cdSize = Get32(p + 8); - cdStartOffset = Get32(p + 12); - commentSize = Get16(p + 16); + // (p) doesn't include signature + G16(0, ThisDisk); + G16(2, CdDisk); + G16(4, NumEntries_in_ThisDisk); + G16(6, NumEntries); + G32(8, Size); + G32(12, Offset); + G16(16, CommentSize); } -struct CEcd64 + +void CCdInfo::ParseEcd32(const Byte *p) { - UInt16 versionMade; - UInt16 versionNeedExtract; - UInt32 thisDiskNumber; - UInt32 startCDDiskNumber; - UInt64 numEntriesInCDOnThisDisk; - UInt64 numEntriesInCD; - UInt64 cdSize; - UInt64 cdStartOffset; - - void Parse(const Byte *p); - CEcd64() { memset(this, 0, sizeof(*this)); } -}; + // (p) includes signature + p += 4; + G16(0, ThisDisk); + G16(2, CdDisk); + G16(4, NumEntries_in_ThisDisk); + G16(6, NumEntries); + G32(8, Size); + G32(12, Offset); + G16(16, CommentSize); +} -void CEcd64::Parse(const Byte *p) +void CCdInfo::ParseEcd64e(const Byte *p) { - versionMade = Get16(p); - versionNeedExtract = Get16(p + 2); - thisDiskNumber = Get32(p + 4); - startCDDiskNumber = Get32(p + 8); - numEntriesInCDOnThisDisk = Get64(p + 12); - numEntriesInCD = Get64(p + 20); - cdSize = Get64(p + 28); - cdStartOffset = Get64(p + 36); + // (p) exclude signature + G16(0, VersionMade); + G16(2, VersionNeedExtract); + G32(4, ThisDisk); + G32(8, CdDisk); + + G64(12, NumEntries_in_ThisDisk); + G64(20, NumEntries); + G64(28, Size); + G64(36, Offset); } -HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit) + +struct CLocator { - _inBufMode = false; - Close(); - RINOK(stream->Seek(0, STREAM_SEEK_CUR, &m_Position)); - RINOK(stream->Seek(0, STREAM_SEEK_END, &ArcInfo.FileEndPos)); - RINOK(stream->Seek(m_Position, STREAM_SEEK_SET, NULL)); + UInt32 Ecd64Disk; + UInt32 NumDisks; + UInt64 Ecd64Offset; + + CLocator(): Ecd64Disk(0), NumDisks(0), Ecd64Offset(0) {} - // printf("\nOpen offset = %d", (int)m_Position); - RINOK(FindAndReadMarker(stream, searchHeaderSizeLimit)); - RINOK(stream->Seek(m_Position, STREAM_SEEK_SET, NULL)); - Stream = stream; - return S_OK; + void Parse(const Byte *p) + { + G32(0, Ecd64Disk); + G64(4, Ecd64Offset); + G32(12, NumDisks); + } +}; + + + + +void CInArchive::ClearRefs() +{ + StreamRef.Release(); + Stream = NULL; + StartStream = NULL; + Callback = NULL; + + Vols.Clear(); } void CInArchive::Close() { + _processedCnt = 0; IsArc = false; + IsArcOpen = false; + IsMultiVol = false; + UseDisk_in_SingleVol = false; + EcdVolIndex = 0; HeadersError = false; HeadersWarning = false; ExtraMinorError = false; UnexpectedEnd = false; NoCentralDir = false; IsZip64 = false; - Stream.Release(); + MarkerIsFound = false; + + ClearRefs(); } + HRESULT CInArchive::Seek(UInt64 offset) { return Stream->Seek(offset, STREAM_SEEK_SET, NULL); } + static bool CheckDosTime(UInt32 dosTime) { if (dosTime == 0) @@ -134,7 +170,8 @@ API_FUNC_IsArc IsArc_Zip(const Byte *p, size_t size) UInt32 value = Get32(p); - if (value == NSignature::kNoSpan) + if (value == NSignature::kNoSpan + || value == NSignature::kSpan) { p += 4; size -= 4; @@ -250,28 +287,35 @@ static UInt32 IsArc_Zip_2(const Byte *p, size_t size, bool isFinal) return res; } -HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchLimit) + + +HRESULT CInArchive::FindMarker(IInStream *stream, const UInt64 *searchLimit) { - ArcInfo.Clear(); ArcInfo.MarkerPos = m_Position; ArcInfo.MarkerPos2 = m_Position; if (searchLimit && *searchLimit == 0) { - const unsigned kStartBufSize = kMarkerSize; - Byte startBuf[kStartBufSize]; - size_t processed = kStartBufSize; + Byte startBuf[kMarkerSize]; + size_t processed = kMarkerSize; RINOK(ReadStream(stream, startBuf, &processed)); m_Position += processed; if (processed < kMarkerSize) return S_FALSE; m_Signature = Get32(startBuf); + if (m_Signature != NSignature::kEcd && m_Signature != NSignature::kLocalFileHeader) { if (m_Signature != NSignature::kNoSpan) - return S_FALSE; - size_t processed = kStartBufSize; + { + if (m_Signature != NSignature::kSpan) + return S_FALSE; + if (m_Position != 4) // we don't support multivol archives with sfx stub + return S_FALSE; + ArcInfo.IsSpanMode = true; + } + size_t processed = kMarkerSize; RINOK(ReadStream(stream, startBuf, &processed)); m_Position += processed; if (processed < kMarkerSize) @@ -283,9 +327,8 @@ HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchLim ArcInfo.MarkerPos2 += 4; } - // we use weak test in case of *searchLimit == 0) + // we use weak test in case of (*searchLimit == 0) // since error will be detected later in Open function - // m_Position = ArcInfo.MarkerPos2 + 4; return S_OK; // maybe we need to search backward. } @@ -302,9 +345,17 @@ HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchLim RINOK(ReadStream(stream, buffer + numBytesInBuffer, &numReadBytes)); m_Position += numReadBytes; numBytesInBuffer += numReadBytes; - bool isFinished = (numBytesInBuffer != kBufSize); + const bool isFinished = (numBytesInBuffer != kBufSize); - size_t limit = (isFinished ? numBytesInBuffer : numBytesInBuffer - kCheckSize); + size_t limit = numBytesInBuffer;; + if (isFinished) + { + if (limit == 0) + break; + limit--; + } + else + limit -= kCheckSize; if (searchLimit && curScanPos + limit > *searchLimit) limit = (size_t)(*searchLimit - curScanPos + 1); @@ -328,7 +379,8 @@ HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchLim m_Signature = Get32(buf + pos); ArcInfo.MarkerPos += curScanPos + pos; ArcInfo.MarkerPos2 = ArcInfo.MarkerPos; - if (m_Signature == NSignature::kNoSpan) + if (m_Signature == NSignature::kNoSpan + || m_Signature == NSignature::kSpan) { m_Signature = Get32(buf + pos + 4); ArcInfo.MarkerPos2 += 4; @@ -349,13 +401,86 @@ HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchLim return S_FALSE; } -HRESULT CInArchive::IncreaseRealPosition(Int64 addValue) + +HRESULT CInArchive::IncreaseRealPosition(Int64 addValue, bool &isFinished) { - return Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position); + isFinished = false; + if (!IsMultiVol) + return Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position); + + for (;;) + { + if (addValue == 0) + return S_OK; + if (addValue > 0) + { + if (Vols.StreamIndex < 0) + return S_FALSE; + if ((unsigned)Vols.StreamIndex >= Vols.Streams.Size()) + { + isFinished = true; + return S_OK; + } + { + const CVols::CSubStreamInfo &s = Vols.Streams[Vols.StreamIndex]; + if (!s.Stream) + { + isFinished = true; + return S_OK; + } + if (m_Position > s.Size) + return S_FALSE; + UInt64 rem = s.Size - m_Position; + if ((UInt64)addValue <= rem) + return Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position); + RINOK(Stream->Seek(s.Size, STREAM_SEEK_SET, &m_Position)); + addValue -= rem; + Stream = NULL; + Vols.StreamIndex++; + if ((unsigned)Vols.StreamIndex >= Vols.Streams.Size()) + { + isFinished = true; + return S_OK; + } + } + const CVols::CSubStreamInfo &s2 = Vols.Streams[Vols.StreamIndex]; + if (!s2.Stream) + { + isFinished = true; + return S_OK; + } + Stream = s2.Stream; + m_Position = 0; + RINOK(Stream->Seek(0, STREAM_SEEK_SET, &m_Position)); + } + else + { + if (!Stream) + return S_FALSE; + { + if (m_Position >= (UInt64)(-addValue)) + return Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position); + addValue += m_Position; + RINOK(Stream->Seek(0, STREAM_SEEK_SET, &m_Position)); + m_Position = 0; + Stream = NULL; + if (--Vols.StreamIndex < 0) + return S_FALSE; + } + const CVols::CSubStreamInfo &s2 = Vols.Streams[Vols.StreamIndex]; + if (!s2.Stream) + return S_FALSE; + Stream = s2.Stream; + m_Position = s2.Size; + RINOK(Stream->Seek(s2.Size, STREAM_SEEK_SET, &m_Position)); + } + } } + class CUnexpectEnd {}; + HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize) { size_t realProcessedSize = size; @@ -376,18 +501,46 @@ HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize) void CInArchive::SafeReadBytes(void *data, unsigned size) { size_t processed = size; - if (_inBufMode) - { - processed = _inBuffer.ReadBytes((Byte *)data, size); - m_Position += processed; - } + + HRESULT result = S_OK; + + if (!_inBufMode) + result = ReadStream(Stream, data, &processed); else { - HRESULT result = ReadStream(Stream, data, &processed); - m_Position += processed; - if (result != S_OK) - throw CSystemException(result); + for (;;) + { + processed = _inBuffer.ReadBytes((Byte *)data, size); + if (processed != 0 + || IsMultiVol + || !CanStartNewVol + || Vols.StreamIndex < 0 + || (unsigned)Vols.StreamIndex >= Vols.Streams.Size()) + break; + Vols.StreamIndex++; + const CVols::CSubStreamInfo &s = Vols.Streams[Vols.StreamIndex]; + if (!s.Stream) + break; + // if (Vols.NeedSeek) + { + result = s.Stream->Seek(0, STREAM_SEEK_SET, NULL); + m_Position = 0; + if (result != S_OK) + break; + Vols.NeedSeek = false; + } + _inBuffer.SetStream(s.Stream); + _inBuffer.Init(); + } + CanStartNewVol = false; } + + m_Position += processed; + _processedCnt += processed; + + if (result != S_OK) + throw CSystemException(result); + if (processed != size) throw CUnexpectEnd(); } @@ -410,12 +563,15 @@ UInt16 CInArchive::ReadUInt16() { Byte buf[2]; SafeReadBytes(buf, 2); return Get UInt32 CInArchive::ReadUInt32() { Byte buf[4]; SafeReadBytes(buf, 4); return Get32(buf); } UInt64 CInArchive::ReadUInt64() { Byte buf[8]; SafeReadBytes(buf, 8); return Get64(buf); } +// we use Skip() inside headers only, so no need for stream change in multivol. + void CInArchive::Skip(unsigned num) { if (_inBufMode) { size_t skip = _inBuffer.Skip(num); m_Position += skip; + _processedCnt += skip; if (skip != num) throw CUnexpectEnd(); } @@ -440,16 +596,18 @@ void CInArchive::ReadFileName(unsigned size, AString &s) s.Empty(); return; } - char *p = s.GetBuf(size); - SafeReadBytes(p, size); + SafeReadBytes(s.GetBuf(size), size); s.ReleaseBuf_CalcLen(size); } + bool CInArchive::ReadExtra(unsigned extraSize, CExtraBlock &extraBlock, UInt64 &unpackSize, UInt64 &packSize, UInt64 &localHeaderOffset, UInt32 &diskStartNumber) { extraBlock.Clear(); + UInt32 remain = extraSize; + while (remain >= 4) { CExtraSubBlock subBlock; @@ -509,6 +667,7 @@ bool CInArchive::ReadExtra(unsigned extraSize, CExtraBlock &extraBlock, } remain -= dataSize; } + if (remain != 0) { ExtraMinorError = true; @@ -516,12 +675,17 @@ bool CInArchive::ReadExtra(unsigned extraSize, CExtraBlock &extraBlock, // so we don't return false, but just set warning flag // return false; } + Skip(remain); return true; } + bool CInArchive::ReadLocalItem(CItemEx &item) { + item.Disk = 0; + if (IsMultiVol && Vols.StreamIndex >= 0) + item.Disk = Vols.StreamIndex; const unsigned kPureHeaderSize = kLocalHeaderSize - 4; Byte p[kPureHeaderSize]; SafeReadBytes(p, kPureHeaderSize); @@ -534,14 +698,14 @@ bool CInArchive::ReadLocalItem(CItemEx &item) item.ExtractVersion.Version = p[0]; item.ExtractVersion.HostOS = p[1]; - item.Flags = Get16(p + 2); - item.Method = Get16(p + 4); - item.Time = Get32(p + 6); - item.Crc = Get32(p + 10); - item.PackSize = Get32(p + 14); - item.Size = Get32(p + 18); - unsigned nameSize = Get16(p + 22); - unsigned extraSize = Get16(p + 24); + G16(2, item.Flags); + G16(4, item.Method); + G32(6, item.Time); + G32(10, item.Crc); + G32(14, item.PackSize); + G32(18, item.Size); + const unsigned nameSize = Get16(p + 22); + const unsigned extraSize = Get16(p + 24); ReadFileName(nameSize, item.Name); item.LocalFullHeaderSize = kLocalHeaderSize + (UInt32)nameSize + extraSize; @@ -581,6 +745,7 @@ bool CInArchive::ReadLocalItem(CItemEx &item) return item.LocalFullHeaderSize <= ((UInt32)1 << 16); } + static bool FlagsAreSame(const CItem &i1, const CItem &i2) { if (i1.Method != i2.Method) @@ -597,9 +762,16 @@ static bool FlagsAreSame(const CItem &i1, const CItem &i2) if (i1.Method <= NFileHeader::NCompressionMethod::kImploded) mask = 0x7FFF; } + + // we can ignore utf8 flag, if name is ascii + if ((i1.Flags ^ i2.Flags) & NFileHeader::NFlags::kUtf8) + if (i1.Name.IsAscii() && i2.Name.IsAscii()) + mask &= ~NFileHeader::NFlags::kUtf8; + return ((i1.Flags & mask) == (i2.Flags & mask)); } + // #ifdef _WIN32 static bool AreEqualPaths_IgnoreSlashes(const char *s1, const char *s2) { @@ -623,6 +795,7 @@ static bool AreEqualPaths_IgnoreSlashes(const char *s1, const char *s2) } // #endif + static bool AreItemsEqual(const CItemEx &localItem, const CItemEx &cdItem) { if (!FlagsAreSame(cdItem, localItem)) @@ -670,16 +843,52 @@ static bool AreItemsEqual(const CItemEx &localItem, const CItemEx &cdItem) return true; } -HRESULT CInArchive::ReadLocalItemAfterCdItem(CItemEx &item) + +HRESULT CInArchive::ReadLocalItemAfterCdItem(CItemEx &item, bool &isAvail) { + isAvail = true; if (item.FromLocal) return S_OK; try { - UInt64 offset = ArcInfo.Base + item.LocalHeaderPos; - if (ArcInfo.Base < 0 && (Int64)offset < 0) - return S_FALSE; - RINOK(Seek(offset)); + UInt64 offset = item.LocalHeaderPos; + + if (IsMultiVol) + { + if (item.Disk >= Vols.Streams.Size()) + { + isAvail = false; + return S_FALSE; + } + IInStream *str2 = Vols.Streams[item.Disk].Stream; + if (!str2) + { + isAvail = false; + return S_FALSE; + } + RINOK(str2->Seek(offset, STREAM_SEEK_SET, NULL)); + Stream = str2; + Vols.StreamIndex = item.Disk; + } + else + { + if (UseDisk_in_SingleVol && item.Disk != EcdVolIndex) + { + isAvail = false; + return S_FALSE; + } + Stream = StreamRef; + + offset += ArcInfo.Base; + if (ArcInfo.Base < 0 && (Int64)offset < 0) + { + isAvail = false; + return S_FALSE; + } + RINOK(Seek(offset)); + } + + CItemEx localItem; if (ReadUInt32() != NSignature::kLocalFileHeader) return S_FALSE; @@ -694,6 +903,7 @@ HRESULT CInArchive::ReadLocalItemAfterCdItem(CItemEx &item) return S_OK; } + HRESULT CInArchive::ReadLocalItemDescriptor(CItemEx &item) { const unsigned kBufSize = (1 << 12); @@ -726,7 +936,8 @@ HRESULT CInArchive::ReadLocalItemDescriptor(CItemEx &item) item.Crc = Get32(buf + i + 4); item.PackSize = descriptorPackSize; item.Size = Get32(buf + i + 12); - return IncreaseRealPosition((Int64)(Int32)(0 - (numBytesInBuffer - i - kDataDescriptorSize))); + bool isFinished; + return IncreaseRealPosition((Int64)(Int32)(0 - (numBytesInBuffer - i - kDataDescriptorSize)), isFinished); } } } @@ -739,15 +950,18 @@ HRESULT CInArchive::ReadLocalItemDescriptor(CItemEx &item) } } + HRESULT CInArchive::ReadLocalItemAfterCdItemFull(CItemEx &item) { if (item.FromLocal) return S_OK; try { - RINOK(ReadLocalItemAfterCdItem(item)); + bool isAvail = true; + RINOK(ReadLocalItemAfterCdItem(item, isAvail)); if (item.HasDescriptor()) { + // pkzip's version without descriptor is not supported RINOK(Seek(ArcInfo.Base + item.GetDataPosition() + item.PackSize)); if (ReadUInt32() != NSignature::kDataDescriptor) return S_FALSE; @@ -775,6 +989,7 @@ HRESULT CInArchive::ReadLocalItemAfterCdItemFull(CItemEx &item) return S_OK; } + HRESULT CInArchive::ReadCdItem(CItemEx &item) { item.FromCentral = true; @@ -785,30 +1000,24 @@ HRESULT CInArchive::ReadCdItem(CItemEx &item) item.MadeByVersion.HostOS = p[1]; item.ExtractVersion.Version = p[2]; item.ExtractVersion.HostOS = p[3]; - item.Flags = Get16(p + 4); - item.Method = Get16(p + 6); - item.Time = Get32(p + 8); - item.Crc = Get32(p + 12); - item.PackSize = Get32(p + 16); - item.Size = Get32(p + 20); + G16(4, item.Flags); + G16(6, item.Method); + G32(8, item.Time); + G32(12, item.Crc); + G32(16, item.PackSize); + G32(20, item.Size); const unsigned nameSize = Get16(p + 24); const unsigned extraSize = Get16(p + 26); const unsigned commentSize = Get16(p + 28); - UInt32 diskNumberStart = Get16(p + 30); - item.InternalAttrib = Get16(p + 32); - item.ExternalAttrib = Get32(p + 34); - item.LocalHeaderPos = Get32(p + 38); + G16(30, item.Disk); + G16(32, item.InternalAttrib); + G32(34, item.ExternalAttrib); + G32(38, item.LocalHeaderPos); ReadFileName(nameSize, item.Name); if (extraSize > 0) - { - ReadExtra(extraSize, item.CentralExtra, item.Size, item.PackSize, - item.LocalHeaderPos, diskNumberStart); - } + ReadExtra(extraSize, item.CentralExtra, item.Size, item.PackSize, item.LocalHeaderPos, item.Disk); - if (diskNumberStart != 0) - return E_NOTIMPL; - // May be these strings must be deleted /* if (item.IsDir()) @@ -819,19 +1028,6 @@ HRESULT CInArchive::ReadCdItem(CItemEx &item) return S_OK; } -void CCdInfo::ParseEcd(const Byte *p) -{ - NumEntries = Get16(p + 10); - Size = Get32(p + 12); - Offset = Get32(p + 16); -} - -void CCdInfo::ParseEcd64(const Byte *p) -{ - NumEntries = Get64(p + 24); - Size = Get64(p + 40); - Offset = Get64(p + 48); -} HRESULT CInArchive::TryEcd64(UInt64 offset, CCdInfo &cdInfo) { @@ -847,155 +1043,256 @@ HRESULT CInArchive::TryEcd64(UInt64 offset, CCdInfo &cdInfo) UInt64 mainSize = Get64(buf + 4); if (mainSize < kEcd64_MainSize || mainSize > ((UInt64)1 << 32)) return S_FALSE; - cdInfo.ParseEcd64(buf); + cdInfo.ParseEcd64e(buf + 12); return S_OK; } -HRESULT CInArchive::FindCd(CCdInfo &cdInfo) + +HRESULT CInArchive::FindCd(bool checkOffsetMode) { - UInt64 endPosition; - RINOK(Stream->Seek(0, STREAM_SEEK_END, &endPosition)); + CCdInfo &cdInfo = Vols.ecd; + + UInt64 endPos; + + RINOK(Stream->Seek(0, STREAM_SEEK_END, &endPos)); const UInt32 kBufSizeMax = ((UInt32)1 << 16) + kEcdSize + kEcd64Locator_Size + kEcd64_FullSize; - UInt32 bufSize = (endPosition < kBufSizeMax) ? (UInt32)endPosition : kBufSizeMax; + const UInt32 bufSize = (endPos < kBufSizeMax) ? (UInt32)endPos : kBufSizeMax; if (bufSize < kEcdSize) return S_FALSE; CByteArr byteBuffer(bufSize); - UInt64 startPosition = endPosition - bufSize; - RINOK(Stream->Seek(startPosition, STREAM_SEEK_SET, &m_Position)); - if (m_Position != startPosition) + const UInt64 startPos = endPos - bufSize; + RINOK(Stream->Seek(startPos, STREAM_SEEK_SET, &m_Position)); + if (m_Position != startPos) return S_FALSE; RINOK(ReadStream_FALSE(Stream, byteBuffer, bufSize)); - const Byte *buf = byteBuffer; - for (UInt32 i = bufSize - kEcdSize;; i--) + for (UInt32 i = bufSize - kEcdSize + 1;;) { - if (buf[i] != 0x50) + if (i == 0) + return S_FALSE; + + const Byte *buf = byteBuffer; + + for (;;) { - if (i == 0) return S_FALSE; i--; - if (buf[i] != 0x50) - { - if (i == 0) return S_FALSE; - continue; - } + if (buf[i] == 0x50) + break; + if (i == 0) + return S_FALSE; } - if (Get32(buf + i) == NSignature::kEcd) + + if (Get32(buf + i) != NSignature::kEcd) + continue; + + cdInfo.ParseEcd32(buf + i); + + if (i >= kEcd64Locator_Size) { - if (i >= kEcd64_FullSize + kEcd64Locator_Size) + const Byte *locatorPtr = buf + i - kEcd64Locator_Size; + if (Get32(locatorPtr) == NSignature::kEcd64Locator) { - const Byte *locator = buf + i - kEcd64Locator_Size; - if (Get32(locator) == NSignature::kEcd64Locator && - Get32(locator + 4) == 0) // number of the disk with the start of the zip64 ECD + CLocator locator; + locator.Parse(locatorPtr + 4); + if ((cdInfo.ThisDisk == locator.NumDisks - 1 || cdInfo.ThisDisk == 0xFFFF) + && locator.Ecd64Disk < locator.NumDisks) { - // Most of the zip64 use fixed size Zip64 ECD + if (locator.Ecd64Disk != cdInfo.ThisDisk && cdInfo.ThisDisk != 0xFFFF) + return E_NOTIMPL; - UInt64 ecd64Offset = Get64(locator + 8); - UInt64 absEcd64 = endPosition - bufSize + i - (kEcd64Locator_Size + kEcd64_FullSize); + // Most of the zip64 use fixed size Zip64 ECD + // we try relative backward reading. + + UInt64 absEcd64 = endPos - bufSize + i - (kEcd64Locator_Size + kEcd64_FullSize); + if (checkOffsetMode || absEcd64 == locator.Ecd64Offset) { - const Byte *ecd64 = locator - kEcd64_FullSize; - if (Get32(ecd64) == NSignature::kEcd64 && - Get64(ecd64 + 4) == kEcd64_MainSize) + const Byte *ecd64 = locatorPtr - kEcd64_FullSize; + if (Get32(ecd64) == NSignature::kEcd64) { - cdInfo.ParseEcd64(ecd64); - ArcInfo.Base = absEcd64 - ecd64Offset; - return S_OK; + UInt64 mainEcd64Size = Get64(ecd64 + 4); + if (mainEcd64Size == kEcd64_MainSize) + { + cdInfo.ParseEcd64e(ecd64 + 12); + ArcInfo.Base = absEcd64 - locator.Ecd64Offset; + // ArcInfo.BaseVolIndex = cdInfo.ThisDisk; + return S_OK; + } } } - + // some zip64 use variable size Zip64 ECD. - // we try to find it - if (absEcd64 != ecd64Offset) + // we try to use absolute offset from locator. + + if (absEcd64 != locator.Ecd64Offset) { - if (TryEcd64(ecd64Offset, cdInfo) == S_OK) + if (TryEcd64(locator.Ecd64Offset, cdInfo) == S_OK) { ArcInfo.Base = 0; + // ArcInfo.BaseVolIndex = cdInfo.ThisDisk; return S_OK; } } - if (ArcInfo.MarkerPos != 0 && - ArcInfo.MarkerPos + ecd64Offset != absEcd64) + + // for variable Zip64 ECD with for archives with offset != 0. + + if (checkOffsetMode + && ArcInfo.MarkerPos != 0 + && ArcInfo.MarkerPos + locator.Ecd64Offset != absEcd64) { - if (TryEcd64(ArcInfo.MarkerPos + ecd64Offset, cdInfo) == S_OK) + if (TryEcd64(ArcInfo.MarkerPos + locator.Ecd64Offset, cdInfo) == S_OK) { ArcInfo.Base = ArcInfo.MarkerPos; + // ArcInfo.BaseVolIndex = cdInfo.ThisDisk; return S_OK; } } } } - if (Get32(buf + i + 4) == 0) // ThisDiskNumber, StartCentralDirectoryDiskNumber; + } + + // bool isVolMode = (Vols.EndVolIndex != -1); + // UInt32 searchDisk = (isVolMode ? Vols.EndVolIndex : 0); + + if (/* searchDisk == thisDisk && */ cdInfo.CdDisk <= cdInfo.ThisDisk) + { + // if (isVolMode) + { + if (cdInfo.CdDisk != cdInfo.ThisDisk) + return S_OK; + } + + UInt64 absEcdPos = endPos - bufSize + i; + UInt64 cdEnd = cdInfo.Size + cdInfo.Offset; + ArcInfo.Base = 0; + // ArcInfo.BaseVolIndex = cdInfo.ThisDisk; + if (absEcdPos != cdEnd) { - cdInfo.ParseEcd(buf + i); - UInt64 absEcdPos = endPosition - bufSize + i; - UInt64 cdEnd = cdInfo.Size + cdInfo.Offset; - ArcInfo.Base = 0; - if (absEcdPos != cdEnd) + /* + if (cdInfo.Offset <= 16 && cdInfo.Size != 0) { - /* - if (cdInfo.Offset <= 16 && cdInfo.Size != 0) - { - // here we support some rare ZIP files with Central directory at the start - ArcInfo.Base = 0; - } - else - */ - ArcInfo.Base = absEcdPos - cdEnd; + // here we support some rare ZIP files with Central directory at the start + ArcInfo.Base = 0; } - return S_OK; + else + */ + ArcInfo.Base = absEcdPos - cdEnd; } + return S_OK; } - if (i == 0) - return S_FALSE; } } -HRESULT CInArchive::TryReadCd(CObjectVector<CItemEx> &items, UInt64 cdOffset, UInt64 cdSize, CProgressVirt *progress) +HRESULT CInArchive::TryReadCd(CObjectVector<CItemEx> &items, const CCdInfo &cdInfo, UInt64 cdOffset, UInt64 cdSize) { items.Clear(); - RINOK(Stream->Seek(cdOffset, STREAM_SEEK_SET, &m_Position)); - if (m_Position != cdOffset) - return S_FALSE; + + ISequentialInStream *stream; + + if (!IsMultiVol) + { + stream = this->StartStream; + Vols.StreamIndex = -1; + RINOK(this->StartStream->Seek(cdOffset, STREAM_SEEK_SET, &m_Position)); + if (m_Position != cdOffset) + return S_FALSE; + } + else + { + if (cdInfo.CdDisk >= Vols.Streams.Size()) + return S_FALSE; + IInStream *str2 = Vols.Streams[cdInfo.CdDisk].Stream; + if (!str2) + return S_FALSE; + RINOK(str2->Seek(cdOffset, STREAM_SEEK_SET, NULL)); + stream = str2; + Vols.NeedSeek = false; + Vols.StreamIndex = cdInfo.CdDisk; + m_Position = cdOffset; + } + + _inBuffer.SetStream(stream); _inBuffer.Init(); _inBufMode = true; - while (m_Position - cdOffset < cdSize) + _processedCnt = 0; + + while (_processedCnt < cdSize) { + CanStartNewVol = true; if (ReadUInt32() != NSignature::kCentralFileHeader) return S_FALSE; - CItemEx cdItem; - RINOK(ReadCdItem(cdItem)); - items.Add(cdItem); - if (progress && (items.Size() & 0xFFF) == 0) - RINOK(progress->SetCompletedCD(items.Size())); + { + CItemEx cdItem; + RINOK(ReadCdItem(cdItem)); + items.Add(cdItem); + } + if (Callback && (items.Size() & 0xFFF) == 0) + { + const UInt64 numFiles = items.Size(); + RINOK(Callback->SetCompleted(&numFiles, NULL)); + } } - return (m_Position - cdOffset == cdSize) ? S_OK : S_FALSE; + + CanStartNewVol = true; + + return (_processedCnt == cdSize) ? S_OK : S_FALSE; } -HRESULT CInArchive::ReadCd(CObjectVector<CItemEx> &items, UInt64 &cdOffset, UInt64 &cdSize, CProgressVirt *progress) + +HRESULT CInArchive::ReadCd(CObjectVector<CItemEx> &items, UInt32 &cdDisk, UInt64 &cdOffset, UInt64 &cdSize) { - CCdInfo cdInfo; - RINOK(FindCd(cdInfo)); + bool checkOffsetMode = true; + + if (IsMultiVol) + { + if (Vols.EndVolIndex == -1) + return S_FALSE; + Stream = Vols.Streams[Vols.EndVolIndex].Stream; + if (!Vols.StartIsZip) + checkOffsetMode = false; + } + else + Stream = StartStream; + + if (!Vols.ecd_wasRead) + { + RINOK(FindCd(checkOffsetMode)); + } + + CCdInfo &cdInfo = Vols.ecd; + HRESULT res = S_FALSE; + cdSize = cdInfo.Size; cdOffset = cdInfo.Offset; - if (progress) - progress->SetTotalCD(cdInfo.NumEntries); - res = TryReadCd(items, ArcInfo.Base + cdOffset, cdSize, progress); - if (res == S_FALSE && ArcInfo.Base == 0) + cdDisk = cdInfo.CdDisk; + + if (Callback) { - res = TryReadCd(items, ArcInfo.MarkerPos + cdOffset, cdSize, progress); + RINOK(Callback->SetTotal(&cdInfo.NumEntries, NULL)); + } + + const UInt64 base = (IsMultiVol ? 0 : ArcInfo.Base); + res = TryReadCd(items, cdInfo, base + cdOffset, cdSize); + + if (res == S_FALSE && !IsMultiVol && base != ArcInfo.MarkerPos) + { + // do we need that additional attempt to read cd? + res = TryReadCd(items, cdInfo, ArcInfo.MarkerPos + cdOffset, cdSize); if (res == S_OK) ArcInfo.Base = ArcInfo.MarkerPos; } + return res; } -static HRESULT FindItem(const CObjectVector<CItemEx> &items, UInt64 offset) + +static int FindItem(const CObjectVector<CItemEx> &items, const CItemEx &item) { unsigned left = 0, right = items.Size(); for (;;) @@ -1003,42 +1300,66 @@ static HRESULT FindItem(const CObjectVector<CItemEx> &items, UInt64 offset) if (left >= right) return -1; unsigned index = (left + right) / 2; - UInt64 position = items[index].LocalHeaderPos; - if (offset == position) + const CItemEx &item2 = items[index]; + if (item.Disk < item2.Disk) + right = index; + else if (item.Disk > item2.Disk) + left = index + 1; + else if (item.LocalHeaderPos == item2.LocalHeaderPos) return index; - if (offset < position) + else if (item.LocalHeaderPos < item2.LocalHeaderPos) right = index; else left = index + 1; } } -bool IsStrangeItem(const CItem &item) +static bool IsStrangeItem(const CItem &item) { return item.Name.Len() > (1 << 14) || item.Method > (1 << 8); } -HRESULT CInArchive::ReadLocals( - CObjectVector<CItemEx> &items, CProgressVirt *progress) +HRESULT CInArchive::ReadLocals(CObjectVector<CItemEx> &items) { items.Clear(); + while (m_Signature == NSignature::kLocalFileHeader) { CItemEx item; - item.LocalHeaderPos = m_Position - 4 - ArcInfo.MarkerPos; + item.LocalHeaderPos = m_Position - 4; + if (!IsMultiVol) + item.LocalHeaderPos -= ArcInfo.MarkerPos; + // we write ralative LocalHeaderPos here. Later we can correct it to real Base. + try { ReadLocalItem(item); item.FromLocal = true; + bool isFinished = false; + if (item.HasDescriptor()) ReadLocalItemDescriptor(item); else { - RINOK(IncreaseRealPosition(item.PackSize)); + /* + if (IsMultiVol) + { + const int kStep = 10000; + RINOK(IncreaseRealPosition(-kStep, isFinished)); + RINOK(IncreaseRealPosition(item.PackSize + kStep, isFinished)); + } + else + */ + RINOK(IncreaseRealPosition(item.PackSize, isFinished)); } + items.Add(item); + + if (isFinished) + throw CUnexpectEnd(); + m_Signature = ReadUInt32(); } catch (CUnexpectEnd &) @@ -1048,34 +1369,364 @@ HRESULT CInArchive::ReadLocals( throw; } - if (progress && (items.Size() & 0xFF) == 0) - RINOK(progress->SetCompletedLocal(items.Size(), item.LocalHeaderPos)); + if (Callback && (items.Size() & 0xFF) == 0) + { + const UInt64 numFiles = items.Size(); + UInt64 numBytes = 0; + // if (!sMultiVol) + numBytes = item.LocalHeaderPos; + RINOK(Callback->SetCompleted(&numFiles, &numBytes)); + } } if (items.Size() == 1 && m_Signature != NSignature::kCentralFileHeader) if (IsStrangeItem(items[0])) return S_FALSE; + + return S_OK; +} + + + +HRESULT CVols::ParseArcName(IArchiveOpenVolumeCallback *volCallback) +{ + UString name; + { + NWindows::NCOM::CPropVariant prop; + RINOK(volCallback->GetProperty(kpidName, &prop)); + if (prop.vt != VT_BSTR) + return S_OK; + name = prop.bstrVal; + } + + UString base = name; + int dotPos = name.ReverseFind_Dot(); + + if (dotPos < 0) + return S_OK; + + base.DeleteFrom(dotPos + 1); + + const UString ext = name.Ptr(dotPos + 1); + StartVolIndex = (Int32)(-1); + + if (ext.IsEmpty()) + return S_OK; + else + { + wchar_t c = ext[0]; + IsUpperCase = (c >= 'A' && c <= 'Z'); + if (ext.IsEqualTo_Ascii_NoCase("zip")) + { + BaseName = base; + StartIsZ = true; + StartIsZip = true; + return S_OK; + } + else if (ext.IsEqualTo_Ascii_NoCase("exe")) + { + StartIsExe = true; + BaseName = base; + StartVolIndex = 0; + } + else if (ext[0] == 'z' || ext[0] == 'Z') + { + if (ext.Len() < 3) + return S_OK; + const wchar_t *end = NULL; + UInt32 volNum = ConvertStringToUInt32(ext.Ptr(1), &end); + if (*end != 0 || volNum < 1 || volNum > ((UInt32)1 << 30)) + return S_OK; + StartVolIndex = volNum - 1; + BaseName = base; + StartIsZ = true; + } + else + return S_OK; + } + + UString volName = BaseName; + volName.AddAscii(IsUpperCase ? "ZIP" : "zip"); + HRESULT result = volCallback->GetStream(volName, &ZipStream); + if (result == S_FALSE || !ZipStream) + { + if (MissingName.IsEmpty()) + MissingName = volName; + return S_OK; + } + + return result; +} + + +HRESULT CInArchive::ReadVols2(IArchiveOpenVolumeCallback *volCallback, + unsigned start, int lastDisk, int zipDisk, unsigned numMissingVolsMax, unsigned &numMissingVols) +{ + numMissingVols = 0; + + for (unsigned i = start;; i++) + { + if (lastDisk >= 0 && i >= (unsigned)lastDisk) + break; + + if (i < Vols.Streams.Size()) + if (Vols.Streams[i].Stream) + continue; + + CMyComPtr<IInStream> stream; + + if ((int)i == zipDisk) + { + stream = Vols.ZipStream; + } + else if ((int)i == Vols.StartVolIndex) + { + stream = StartStream; + } + else + { + UString volName = Vols.BaseName; + { + volName += (wchar_t)(Vols.IsUpperCase ? 'Z' : 'z'); + { + char s[32]; + ConvertUInt32ToString(i + 1, s); + unsigned len = (unsigned)strlen(s); + while (len < 2) + { + volName += (wchar_t)'0'; + len++; + } + volName.AddAscii(s); + } + } + + HRESULT result = volCallback->GetStream(volName, &stream); + if (result != S_OK && result != S_FALSE) + return result; + if (result == S_FALSE || !stream) + { + if (Vols.MissingName.IsEmpty()) + Vols.MissingName = volName; + numMissingVols++; + if (numMissingVols > numMissingVolsMax) + return S_OK; + if (lastDisk == -1 && numMissingVols != 0) + return S_OK; + continue; + } + } + + UInt64 size; + + UInt64 pos; + RINOK(stream->Seek(0, STREAM_SEEK_CUR, &pos)); + RINOK(stream->Seek(0, STREAM_SEEK_END, &size)); + RINOK(stream->Seek(pos, STREAM_SEEK_SET, NULL)); + + while (i >= Vols.Streams.Size()) + Vols.Streams.AddNew(); + + CVols::CSubStreamInfo &ss = Vols.Streams[i]; + Vols.NumVols++; + ss.Stream = stream; + ss.Size = size; + + if ((int)i == zipDisk) + { + Vols.EndVolIndex = Vols.Streams.Size() - 1; + break; + } + } + return S_OK; } +HRESULT CInArchive::ReadVols() +{ + CMyComPtr<IArchiveOpenVolumeCallback> volCallback; + + Callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&volCallback); + if (!volCallback) + return S_OK; + + RINOK(Vols.ParseArcName(volCallback)); + + int startZIndex = Vols.StartVolIndex; + + if (!Vols.StartIsZ) + { + // if (!Vols.StartIsExe) + return S_OK; + } + + int zipDisk = -1; + int cdDisk = -1; + + if (Vols.StartIsZip) + Vols.ZipStream = StartStream; + + bool cdOK = false; + + if (Vols.ZipStream) + { + Stream = Vols.ZipStream; + HRESULT res = FindCd(true); + CCdInfo &ecd = Vols.ecd; + if (res == S_OK) + { + zipDisk = ecd.ThisDisk; + Vols.ecd_wasRead = true; + if (ecd.ThisDisk == 0 + || ecd.ThisDisk >= ((UInt32)1 << 30) + || ecd.ThisDisk < ecd.CdDisk) + return S_OK; + cdDisk = ecd.CdDisk; + if (Vols.StartVolIndex < 0) + Vols.StartVolIndex = ecd.ThisDisk; + // Vols.StartVolIndex = ecd.ThisDisk; + // Vols.EndVolIndex = ecd.ThisDisk; + unsigned numMissingVols; + if (cdDisk == zipDisk) + cdOK = true; + else + { + RINOK(ReadVols2(volCallback, cdDisk, zipDisk, zipDisk, 0, numMissingVols)); + if (numMissingVols == 0) + cdOK = false; + } + } + else if (res != S_FALSE) + return res; + } + + if (Vols.Streams.Size() > 0) + IsMultiVol = true; + + if (Vols.StartVolIndex < 0) + return S_OK; + + unsigned numMissingVols; + + if (cdDisk != 0) + { + RINOK(ReadVols2(volCallback, 0, cdDisk < 0 ? -1 : cdDisk, zipDisk, 1 << 10, numMissingVols)); + } + + if (Vols.ZipStream) + { + if (Vols.Streams.IsEmpty()) + if (zipDisk > (1 << 10)) + return S_OK; + RINOK(ReadVols2(volCallback, zipDisk, zipDisk + 1, zipDisk, 0, numMissingVols)); + } + + if (!Vols.Streams.IsEmpty()) + { + IsMultiVol = true; + /* + if (cdDisk) + IsMultiVol = true; + */ + if (startZIndex >= 0) + { + if (Vols.Streams.Size() >= (unsigned)startZIndex) + { + for (unsigned i = 0; i < (unsigned)startZIndex; i++) + if (!Vols.Streams[i].Stream) + { + Vols.StartParsingVol = startZIndex; + break; + } + } + } + } + + return S_OK; +} + + + + + + + +HRESULT CVols::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize) + *processedSize = 0; + if (size == 0) + return S_OK; + + for (;;) + { + if (StreamIndex < 0) + return S_OK; + if ((unsigned)StreamIndex >= Streams.Size()) + return S_OK; + const CVols::CSubStreamInfo &s = Streams[StreamIndex]; + if (!s.Stream) + return S_FALSE; + if (NeedSeek) + { + RINOK(s.Stream->Seek(0, STREAM_SEEK_SET, NULL)); + NeedSeek = false; + } + UInt32 realProcessedSize = 0; + HRESULT res = s.Stream->Read(data, size, &realProcessedSize); + if (processedSize) + *processedSize = realProcessedSize; + if (res != S_OK) + return res; + if (realProcessedSize != 0) + return res; + StreamIndex++; + NeedSeek = true; + } +} + +STDMETHODIMP CVolStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + return Vols->Read(data, size, processedSize); +} + + + + #define COPY_ECD_ITEM_16(n) if (!isZip64 || ecd. n != 0xFFFF) ecd64. n = ecd. n; #define COPY_ECD_ITEM_32(n) if (!isZip64 || ecd. n != 0xFFFFFFFF) ecd64. n = ecd. n; -HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *progress) + +HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items) { - items.Clear(); + HRESULT res = S_OK; + + bool localsWereRead = false; + UInt64 cdSize = 0, cdRelatOffset = 0, cdAbsOffset = 0; + UInt32 cdDisk = 0; + if (!_inBuffer.Create(1 << 15)) + return E_OUTOFMEMORY; + + if (!MarkerIsFound) + { + IsArc = true; + res = ReadCd(items, cdDisk, cdRelatOffset, cdSize); + if (res == S_OK) + m_Signature = ReadUInt32(); + } + else + { + // m_Signature must be kLocalFileHeader or kEcd // m_Position points to next byte after signature RINOK(Stream->Seek(m_Position, STREAM_SEEK_SET, NULL)); - if (!_inBuffer.Create(1 << 15)) - return E_OUTOFMEMORY; _inBuffer.SetStream(Stream); bool needReadCd = true; - bool localsWereRead = false; + if (m_Signature == NSignature::kEcd) { // It must be empty archive or backware archive @@ -1097,9 +1748,6 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p RINOK(Stream->Seek(ArcInfo.MarkerPos2 + 4, STREAM_SEEK_SET, &m_Position)); } - UInt64 cdSize = 0, cdRelatOffset = 0, cdAbsOffset = 0; - HRESULT res = S_OK; - if (needReadCd) { CItemEx firstItem; @@ -1116,7 +1764,7 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p } IsArc = true; - res = ReadCd(items, cdRelatOffset, cdSize, progress); + res = ReadCd(items, cdDisk, cdRelatOffset, cdSize); if (res == S_OK) m_Signature = ReadUInt32(); } @@ -1130,16 +1778,27 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p if (res == S_OK) { // we can't read local items here to keep _inBufMode state - firstItem.LocalHeaderPos = ArcInfo.MarkerPos2 - ArcInfo.Base; - int index = FindItem(items, firstItem.LocalHeaderPos); - if (index == -1) - res = S_FALSE; - else if (!AreItemsEqual(firstItem, items[index])) + if ((Int64)ArcInfo.MarkerPos2 < ArcInfo.Base) res = S_FALSE; - ArcInfo.CdWasRead = true; - ArcInfo.FirstItemRelatOffset = items[0].LocalHeaderPos; + else + { + firstItem.LocalHeaderPos = ArcInfo.MarkerPos2 - ArcInfo.Base; + int index = FindItem(items, firstItem); + if (index == -1) + res = S_FALSE; + else if (!AreItemsEqual(firstItem, items[index])) + res = S_FALSE; + else + { + ArcInfo.CdWasRead = true; + ArcInfo.FirstItemRelatOffset = items[0].LocalHeaderPos; + } + } } } + } + + CObjectVector<CItemEx> cdItems; @@ -1148,19 +1807,32 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p if (res == S_FALSE) { - // CD doesn't match firstItem so we clear items and read Locals. + // CD doesn't match firstItem, + // so we clear items and read Locals. items.Clear(); localsWereRead = true; _inBufMode = false; ArcInfo.Base = ArcInfo.MarkerPos; + + if (IsMultiVol) + { + Vols.StreamIndex = Vols.StartParsingVol; + if (Vols.StartParsingVol >= (int)Vols.Streams.Size()) + return S_FALSE; + Stream = Vols.Streams[Vols.StartParsingVol].Stream; + if (!Stream) + return S_FALSE; + } + RINOK(Stream->Seek(ArcInfo.MarkerPos2, STREAM_SEEK_SET, &m_Position)); m_Signature = ReadUInt32(); - - RINOK(ReadLocals(items, progress)); + RINOK(ReadLocals(items)); + if (m_Signature != NSignature::kCentralFileHeader) { - m_Position -= 4; + // if (!UnexpectedEnd) + m_Position -= 4; NoCentralDir = true; HeadersError = true; return S_OK; @@ -1168,15 +1840,24 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p _inBufMode = true; _inBuffer.Init(); + cdAbsOffset = m_Position - 4; + cdDisk = Vols.StreamIndex; for (;;) { CItemEx cdItem; + CanStartNewVol = true; + RINOK(ReadCdItem(cdItem)); + cdItems.Add(cdItem); - if (progress && (cdItems.Size() & 0xFFF) == 0) - RINOK(progress->SetCompletedCD(items.Size())); + if (Callback && (cdItems.Size() & 0xFFF) == 0) + { + const UInt64 numFiles = items.Size(); + RINOK(Callback->SetCompleted(&numFiles, NULL)); + } + CanStartNewVol = true; m_Signature = ReadUInt32(); if (m_Signature != NSignature::kCentralFileHeader) break; @@ -1193,95 +1874,159 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p } } - CEcd64 ecd64; + + + CCdInfo ecd64; + CLocator locator; bool isZip64 = false; - UInt64 ecd64AbsOffset = m_Position - 4; + const UInt64 ecd64AbsOffset = m_Position - 4; + int ecd64Disk = -1; if (m_Signature == NSignature::kEcd64) { + ecd64Disk = Vols.StreamIndex; + IsZip64 = isZip64 = true; - UInt64 recordSize = ReadUInt64(); - const unsigned kBufSize = kEcd64_MainSize; - Byte buf[kBufSize]; - SafeReadBytes(buf, kBufSize); - ecd64.Parse(buf); + { + const UInt64 recordSize = ReadUInt64(); + if (recordSize < kEcd64_MainSize) + { + HeadersError = true; + return S_OK; + } + + { + const unsigned kBufSize = kEcd64_MainSize; + Byte buf[kBufSize]; + SafeReadBytes(buf, kBufSize); + ecd64.ParseEcd64e(buf); + } + + Skip64(recordSize - kEcd64_MainSize); + } - Skip64(recordSize - kEcd64_MainSize); - m_Signature = ReadUInt32(); - if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0) - return E_NOTIMPL; + m_Signature = ReadUInt32(); - if (needSetBase) + if (m_Signature != NSignature::kEcd64Locator) + { + HeadersError = true; + return S_OK; + } + { - ArcInfo.Base = cdAbsOffset - ecd64.cdStartOffset; - cdRelatOffset = ecd64.cdStartOffset; - needSetBase = false; + const unsigned kBufSize = 16; + Byte buf[kBufSize]; + SafeReadBytes(buf, kBufSize); + locator.Parse(buf); } - if (ecd64.numEntriesInCDOnThisDisk != numCdItems || - ecd64.numEntriesInCD != numCdItems || - ecd64.cdSize != cdSize || - (ecd64.cdStartOffset != cdRelatOffset && - (!items.IsEmpty()))) - return S_FALSE; + m_Signature = ReadUInt32(); } - if (m_Signature == NSignature::kEcd64Locator) + + if (m_Signature != NSignature::kEcd) { - if (!isZip64) - return S_FALSE; - /* UInt32 startEndCDDiskNumber = */ ReadUInt32(); - UInt64 ecd64RelatOffset = ReadUInt64(); - /* UInt32 numberOfDisks = */ ReadUInt32(); - if (ecd64AbsOffset != ArcInfo.Base + ecd64RelatOffset) - return S_FALSE; - m_Signature = ReadUInt32(); + HeadersError = true; + return S_OK; } + - if (m_Signature != NSignature::kEcd) - return S_FALSE; + // ---------- ECD ---------- - const unsigned kBufSize = kEcdSize - 4; - Byte buf[kBufSize]; - SafeReadBytes(buf, kBufSize); CEcd ecd; - ecd.Parse(buf); + { + const unsigned kBufSize = kEcdSize - 4; + Byte buf[kBufSize]; + SafeReadBytes(buf, kBufSize); + ecd.Parse(buf); + } - COPY_ECD_ITEM_16(thisDiskNumber); - COPY_ECD_ITEM_16(startCDDiskNumber); - COPY_ECD_ITEM_16(numEntriesInCDOnThisDisk); - COPY_ECD_ITEM_16(numEntriesInCD); - COPY_ECD_ITEM_32(cdSize); - COPY_ECD_ITEM_32(cdStartOffset); + COPY_ECD_ITEM_16(ThisDisk); + COPY_ECD_ITEM_16(CdDisk); + COPY_ECD_ITEM_16(NumEntries_in_ThisDisk); + COPY_ECD_ITEM_16(NumEntries); + COPY_ECD_ITEM_32(Size); + COPY_ECD_ITEM_32(Offset); - if (needSetBase) + if (IsMultiVol) { - ArcInfo.Base = cdAbsOffset - ecd64.cdStartOffset; - cdRelatOffset = ecd64.cdStartOffset; - needSetBase = false; + if (cdDisk != (int)ecd64.CdDisk) + HeadersError = true; } + else if (needSetBase) + { + if (isZip64) + { + if (ecd64Disk == Vols.StartVolIndex) + { + ArcInfo.Base = ecd64AbsOffset - locator.Ecd64Offset; + // cdRelatOffset = ecd64.Offset; + needSetBase = false; + } + } + else + { + if ((int)cdDisk == Vols.StartVolIndex) + { + ArcInfo.Base = cdAbsOffset - ecd64.Offset; + cdRelatOffset = ecd64.Offset; + needSetBase = false; + } + } + } + + EcdVolIndex = ecd64.ThisDisk; - if (localsWereRead && (UInt64)ArcInfo.Base != ArcInfo.MarkerPos) + if (!IsMultiVol) { - UInt64 delta = ArcInfo.MarkerPos - ArcInfo.Base; - for (unsigned i = 0; i < items.Size(); i++) - items[i].LocalHeaderPos += delta; + UseDisk_in_SingleVol = true; + + if (localsWereRead) + { + if ((UInt64)ArcInfo.Base != ArcInfo.MarkerPos) + { + const UInt64 delta = ArcInfo.MarkerPos - ArcInfo.Base; + FOR_VECTOR (i, items) + items[i].LocalHeaderPos += delta; + } + + if (EcdVolIndex != 0) + { + FOR_VECTOR (i, items) + items[i].Disk = EcdVolIndex; + } + } } + if (isZip64) + { + if (ecd64.ThisDisk == 0 && ecd64AbsOffset != ArcInfo.Base + locator.Ecd64Offset + // || ecd64.NumEntries_in_ThisDisk != numCdItems + || ecd64.NumEntries != numCdItems + || ecd64.Size != cdSize + || (ecd64.Offset != cdRelatOffset && !items.IsEmpty())) + { + HeadersError = true; + return S_OK; + } + } // ---------- merge Central Directory Items ---------- if (!cdItems.IsEmpty()) { - for (unsigned i = 0; i < cdItems.Size(); i++) + CObjectVector<CItemEx> items2; + + FOR_VECTOR (i, cdItems) { const CItemEx &cdItem = cdItems[i]; - int index = FindItem(items, cdItem.LocalHeaderPos); + int index = FindItem(items, cdItem); if (index == -1) { - items.Add(cdItem); + items2.Add(cdItem); + HeadersError = true; continue; } CItemEx &item = items[index]; @@ -1292,9 +2037,11 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p // item.ExtractVersion != cdItem.ExtractVersion || !FlagsAreSame(item, cdItem) || item.Crc != cdItem.Crc) + { + HeadersError = true; continue; + } - // item.LocalHeaderPos = cdItem.LocalHeaderPos; // item.Name = cdItem.Name; item.MadeByVersion = cdItem.MadeByVersion; item.CentralExtra = cdItem.CentralExtra; @@ -1303,33 +2050,47 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p item.Comment = cdItem.Comment; item.FromCentral = cdItem.FromCentral; } + + items += items2; } - if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0) - return E_NOTIMPL; - + + if (ecd.NumEntries < ecd.NumEntries_in_ThisDisk) + HeadersError = true; + + if (ecd.ThisDisk == 0) + { + // if (isZip64) + { + if (ecd.NumEntries != ecd.NumEntries_in_ThisDisk) + HeadersError = true; + } + } + + if (ecd.NumEntries > items.Size()) + HeadersError = true; + if (isZip64) { - if (ecd64.numEntriesInCDOnThisDisk != items.Size()) + if (ecd64.NumEntries != items.Size()) HeadersError = true; } else { // old 7-zip could store 32-bit number of CD items to 16-bit field. - if ((UInt16)ecd64.numEntriesInCDOnThisDisk != (UInt16)numCdItems || - (UInt16)ecd64.numEntriesInCDOnThisDisk != (UInt16)items.Size()) + /* + if ((UInt16)ecd64.NumEntries == (UInt16)items.Size()) HeadersError = true; + */ } - ReadBuffer(ArcInfo.Comment, ecd.commentSize); + ReadBuffer(ArcInfo.Comment, ecd.CommentSize); _inBufMode = false; _inBuffer.Free(); - if ( - (UInt16)ecd64.numEntriesInCD != ((UInt16)numCdItems) || - (UInt32)ecd64.cdSize != (UInt32)cdSize || - ((UInt32)(ecd64.cdStartOffset) != (UInt32)cdRelatOffset && - (!items.IsEmpty()))) + if ((UInt16)ecd64.NumEntries != (UInt16)numCdItems + || (UInt32)ecd64.Size != (UInt32)cdSize + || ((UInt32)ecd64.Offset != (UInt32)cdRelatOffset && !items.IsEmpty())) { // return S_FALSE; HeadersError = true; @@ -1340,39 +2101,170 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p } -HRESULT CInArchive::ReadHeaders(CObjectVector<CItemEx> &items, CProgressVirt *progress) + +HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchLimit, + IArchiveOpenCallback *callback, CObjectVector<CItemEx> &items) { - HRESULT res; - try + _inBufMode = false; + items.Clear(); + + Close(); + ArcInfo.Clear(); + + UInt64 startPos; + RINOK(stream->Seek(0, STREAM_SEEK_CUR, &startPos)); + RINOK(stream->Seek(0, STREAM_SEEK_END, &ArcInfo.FileEndPos)); + m_Position = ArcInfo.FileEndPos; + + StartStream = stream; + Callback = callback; + + bool volWasRequested = false; + + if (callback + && (startPos == 0 || !searchLimit || *searchLimit != 0)) { - res = ReadHeaders2(items, progress); + volWasRequested = true; + RINOK(ReadVols()); } - catch (const CInBufferException &e) { res = e.ErrorCode; } - catch (const CUnexpectEnd &) + + if (IsMultiVol && Vols.StartVolIndex != 0) { - if (items.IsEmpty()) - return S_FALSE; - UnexpectedEnd = true; - res = S_OK; + Stream = Vols.Streams[0].Stream; + if (Stream) + { + m_Position = 0; + RINOK(Stream->Seek(0, STREAM_SEEK_SET, NULL)); + UInt64 limit = 0; + HRESULT res = FindMarker(Stream, &limit); + if (res == S_OK) + MarkerIsFound = true; + else if (res != S_FALSE) + return res; + } + } + else + { + // printf("\nOpen offset = %u\n", (unsigned)startPos); + RINOK(stream->Seek(startPos, STREAM_SEEK_SET, NULL)); + m_Position = startPos; + HRESULT res = FindMarker(stream, searchLimit); + UInt64 curPos = m_Position; + if (res == S_OK) + MarkerIsFound = true; + else + { + // if (res != S_FALSE) + return res; + } + + MarkerIsFound = true; + + if (ArcInfo.IsSpanMode && !volWasRequested) + { + RINOK(ReadVols()); + } + + if (IsMultiVol && (unsigned)Vols.StartVolIndex < Vols.Streams.Size()) + { + Stream = Vols.Streams[Vols.StartVolIndex].Stream; + if (!Stream) + IsMultiVol = false; + else + { + RINOK(Stream->Seek(curPos, STREAM_SEEK_SET, NULL)); + m_Position = curPos; + } + } + else + IsMultiVol = false; + + if (!IsMultiVol) + { + RINOK(stream->Seek(curPos, STREAM_SEEK_SET, NULL)); + m_Position = curPos; + StreamRef = stream; + Stream = stream; + } } - catch (...) + + { + HRESULT res; + try + { + res = ReadHeaders2(items); + } + catch (const CInBufferException &e) { res = e.ErrorCode; } + catch (const CUnexpectEnd &) + { + if (items.IsEmpty()) + return S_FALSE; + UnexpectedEnd = true; + res = S_OK; + } + catch (...) + { + _inBufMode = false; + throw; + } + + if (IsMultiVol) + { + ArcInfo.FinishPos = ArcInfo.FileEndPos; + if ((unsigned)Vols.StreamIndex < Vols.Streams.Size()) + if (m_Position < Vols.Streams[Vols.StreamIndex].Size) + ArcInfo.ThereIsTail = true; + } + else + { + ArcInfo.FinishPos = m_Position; + ArcInfo.ThereIsTail = (ArcInfo.FileEndPos > m_Position); + } + _inBufMode = false; - throw; + IsArcOpen = true; + if (!IsMultiVol) + Vols.Streams.Clear(); + return res; } - ArcInfo.FinishPos = m_Position; - _inBufMode = false; - return res; } -ISequentialInStream* CInArchive::CreateLimitedStream(UInt64 position, UInt64 size) + +HRESULT CInArchive::GetItemStream(const CItemEx &item, bool seekPackData, CMyComPtr<ISequentialInStream> &stream) { - CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; - CMyComPtr<ISequentialInStream> stream(streamSpec); - Stream->Seek(ArcInfo.Base + position, STREAM_SEEK_SET, NULL); - streamSpec->SetStream(Stream); - streamSpec->Init(size); - return stream.Detach(); + stream.Release(); + + UInt64 pos = item.LocalHeaderPos; + if (seekPackData) + pos += item.LocalFullHeaderSize; + + if (!IsMultiVol) + { + if (UseDisk_in_SingleVol && item.Disk != EcdVolIndex) + return S_OK; + pos += ArcInfo.Base; + RINOK(StreamRef->Seek(pos, STREAM_SEEK_SET, NULL)); + stream = StreamRef; + return S_OK; + } + + if (item.Disk >= Vols.Streams.Size()) + return S_OK; + + IInStream *str2 = Vols.Streams[item.Disk].Stream; + if (!str2) + return S_OK; + RINOK(str2->Seek(pos, STREAM_SEEK_SET, NULL)); + + Vols.NeedSeek = false; + Vols.StreamIndex = item.Disk; + + CVolStream *volsStreamSpec = new CVolStream; + volsStreamSpec->Vols = &Vols; + stream = volsStreamSpec; + + return S_OK; } }} diff --git a/CPP/7zip/Archive/Zip/ZipIn.h b/CPP/7zip/Archive/Zip/ZipIn.h index 734d3bcb..9b10905e 100644 --- a/CPP/7zip/Archive/Zip/ZipIn.h +++ b/CPP/7zip/Archive/Zip/ZipIn.h @@ -28,6 +28,7 @@ public: { return LocalHeaderPos + LocalFullHeaderSize; } }; + struct CInArchiveInfo { Int64 Base; /* Base offset of start of archive in stream. @@ -40,74 +41,195 @@ struct CInArchiveInfo Base = ArcInfo.MarkerPos; */ /* The following *Pos variables contain absolute offsets in Stream */ - UInt64 MarkerPos; /* Pos of first signature, it can point to PK00 signature + + UInt64 MarkerPos; /* Pos of first signature, it can point to kSpan/kNoSpan signature = MarkerPos2 in most archives - = MarkerPos2 - 4 if there is PK00 signature */ + = MarkerPos2 - 4 if there is kSpan/kNoSpan signature */ UInt64 MarkerPos2; // Pos of first local item signature in stream - UInt64 FinishPos; // Finish pos of archive data + UInt64 FinishPos; // Finish pos of archive data in starting volume UInt64 FileEndPos; // Finish pos of stream UInt64 FirstItemRelatOffset; /* Relative offset of first local (read from cd) (relative to Base). = 0 in most archives = size of stub for some SFXs */ bool CdWasRead; + bool IsSpanMode; + bool ThereIsTail; + + // UInt32 BaseVolIndex; CByteBuffer Comment; - CInArchiveInfo(): Base(0), MarkerPos(0), MarkerPos2(0), FinishPos(0), FileEndPos(0), - FirstItemRelatOffset(0), CdWasRead(false) {} - - UInt64 GetPhySize() const { return FinishPos - Base; } - UInt64 GetEmbeddedStubSize() const - { - if (CdWasRead) - return FirstItemRelatOffset; - return MarkerPos2 - Base; - } - bool ThereIsTail() const { return FileEndPos > FinishPos; } + CInArchiveInfo(): + Base(0), + MarkerPos(0), + MarkerPos2(0), + FinishPos(0), + FileEndPos(0), + FirstItemRelatOffset(0), + CdWasRead(false), + IsSpanMode(false), + ThereIsTail(false) + // BaseVolIndex(0) + {} + void Clear() { + // BaseVolIndex = 0; Base = 0; MarkerPos = 0; MarkerPos2 = 0; FinishPos = 0; FileEndPos = 0; + ThereIsTail = false; FirstItemRelatOffset = 0; + CdWasRead = false; + IsSpanMode = false; Comment.Free(); } }; -struct CProgressVirt -{ - virtual HRESULT SetCompletedLocal(UInt64 numFiles, UInt64 numBytes) = 0; - virtual HRESULT SetTotalCD(UInt64 numFiles) = 0; - virtual HRESULT SetCompletedCD(UInt64 numFiles) = 0; -}; struct CCdInfo { + // 64 + UInt16 VersionMade; + UInt16 VersionNeedExtract; + + // old zip + UInt32 ThisDisk; + UInt32 CdDisk; + UInt64 NumEntries_in_ThisDisk; UInt64 NumEntries; UInt64 Size; UInt64 Offset; - void ParseEcd(const Byte *p); - void ParseEcd64(const Byte *p); + UInt16 CommentSize; + + CCdInfo() { memset(this, 0, sizeof(*this)); } + + void ParseEcd32(const Byte *p); // (p) includes signature + void ParseEcd64e(const Byte *p); // (p) exclude signature }; + +class CVols +{ +public: + + struct CSubStreamInfo + { + CMyComPtr<IInStream> Stream; + UInt64 Size; + + CSubStreamInfo(): Size(0) {} + }; + + CObjectVector<CSubStreamInfo> Streams; + int StreamIndex; + bool NeedSeek; + + CMyComPtr<IInStream> ZipStream; + + bool StartIsExe; // is .exe + bool StartIsZ; // is .zip or .zNN + bool StartIsZip; // is .zip + bool IsUpperCase; + Int32 StartVolIndex; // = (NN - 1), if StartStream is .zNN + + Int32 StartParsingVol; // if we need local parsing, we must use that stream + unsigned NumVols; + + int EndVolIndex; // index of last volume (ecd volume), + // -1, if is not multivol + + UString BaseName; // including '.' + + UString MissingName; + + CCdInfo ecd; + bool ecd_wasRead; + + void Clear() + { + StreamIndex = -1; + NeedSeek = false; + + + StartIsExe = false; + StartIsZ = false; + StartIsZip = false; + IsUpperCase = false; + + StartVolIndex = -1; + StartParsingVol = 0; + NumVols = 0; + EndVolIndex = -1; + + BaseName.Empty(); + MissingName.Empty(); + + ecd_wasRead = false; + + Streams.Clear(); + ZipStream.Release(); + } + + HRESULT ParseArcName(IArchiveOpenVolumeCallback *volCallback); + + HRESULT Read(void *data, UInt32 size, UInt32 *processedSize); + + UInt64 GetTotalSize() const + { + UInt64 total = 0; + FOR_VECTOR (i, Streams) + total += Streams[i].Size; + return total; + } +}; + + +class CVolStream: + public ISequentialInStream, + public CMyUnknownImp +{ +public: + CVols *Vols; + + MY_UNKNOWN_IMP1(ISequentialInStream) + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); +}; + + class CInArchive { CInBuffer _inBuffer; bool _inBufMode; UInt32 m_Signature; UInt64 m_Position; + + UInt64 _processedCnt; + bool CanStartNewVol; + + CMyComPtr<IInStream> StreamRef; + IInStream *Stream; + IInStream *StartStream; + + bool IsArcOpen; + + HRESULT ReadVols2(IArchiveOpenVolumeCallback *volCallback, + unsigned start, int lastDisk, int zipDisk, unsigned numMissingVolsMax, unsigned &numMissingVols); + HRESULT ReadVols(); + HRESULT Seek(UInt64 offset); - HRESULT FindAndReadMarker(IInStream *stream, const UInt64 *searchHeaderSizeLimit); - HRESULT IncreaseRealPosition(Int64 addValue); + HRESULT FindMarker(IInStream *stream, const UInt64 *searchLimit); + HRESULT IncreaseRealPosition(Int64 addValue, bool &isFinished); HRESULT ReadBytes(void *data, UInt32 size, UInt32 *processedSize); void SafeReadBytes(void *data, unsigned size); @@ -126,12 +248,14 @@ class CInArchive HRESULT ReadLocalItemDescriptor(CItemEx &item); HRESULT ReadCdItem(CItemEx &item); HRESULT TryEcd64(UInt64 offset, CCdInfo &cdInfo); - HRESULT FindCd(CCdInfo &cdInfo); - HRESULT TryReadCd(CObjectVector<CItemEx> &items, UInt64 cdOffset, UInt64 cdSize, CProgressVirt *progress); - HRESULT ReadCd(CObjectVector<CItemEx> &items, UInt64 &cdOffset, UInt64 &cdSize, CProgressVirt *progress); - HRESULT ReadLocals(CObjectVector<CItemEx> &localItems, CProgressVirt *progress); + HRESULT FindCd(bool checkOffsetMode); + HRESULT TryReadCd(CObjectVector<CItemEx> &items, const CCdInfo &cdInfo, UInt64 cdOffset, UInt64 cdSize); + HRESULT ReadCd(CObjectVector<CItemEx> &items, UInt32 &cdDisk, UInt64 &cdOffset, UInt64 &cdSize); + HRESULT ReadLocals(CObjectVector<CItemEx> &localItems); - HRESULT ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *progress); + HRESULT ReadHeaders2(CObjectVector<CItemEx> &items); + + HRESULT GetVolStream(unsigned vol, UInt64 pos, CMyComPtr<ISequentialInStream> &stream); public: CInArchiveInfo ArcInfo; @@ -142,46 +266,87 @@ public: bool ExtraMinorError; bool UnexpectedEnd; bool NoCentralDir; + + bool MarkerIsFound; + + bool IsMultiVol; + bool UseDisk_in_SingleVol; + UInt32 EcdVolIndex; + + CVols Vols; + + IArchiveOpenCallback *Callback; + + CInArchive(): Stream(NULL), Callback(NULL), IsArcOpen(false) {} + + UInt64 GetPhySize() const + { + if (IsMultiVol) + return ArcInfo.FinishPos; + else + return ArcInfo.FinishPos - ArcInfo.Base; + } + + UInt64 GetOffset() const + { + if (IsMultiVol) + return 0; + else + return ArcInfo.Base; + } + - CMyComPtr<IInStream> Stream; - + void ClearRefs(); void Close(); - HRESULT Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit); - HRESULT ReadHeaders(CObjectVector<CItemEx> &items, CProgressVirt *progress); + HRESULT Open(IInStream *stream, const UInt64 *searchLimit, IArchiveOpenCallback *callback, CObjectVector<CItemEx> &items); + HRESULT ReadHeaders(CObjectVector<CItemEx> &items); - bool IsOpen() const { return Stream != NULL; } - bool AreThereErrors() const { return HeadersError || UnexpectedEnd; } + bool IsOpen() const { return IsArcOpen; } + + bool AreThereErrors() const + { + return HeadersError + || UnexpectedEnd + || !Vols.MissingName.IsEmpty(); + } bool IsLocalOffsetOK(const CItemEx &item) const { if (item.FromLocal) return true; - return /* ArcInfo.Base >= 0 || */ ArcInfo.Base + (Int64)item.LocalHeaderPos >= 0; + return (Int64)GetOffset() + (Int64)item.LocalHeaderPos >= 0; + } + + UInt64 GetEmbeddedStubSize() const + { + if (ArcInfo.CdWasRead) + return ArcInfo.FirstItemRelatOffset; + if (IsMultiVol) + return 0; + return ArcInfo.MarkerPos2 - ArcInfo.Base; } - HRESULT ReadLocalItemAfterCdItem(CItemEx &item); + + HRESULT ReadLocalItemAfterCdItem(CItemEx &item, bool &isAvail); HRESULT ReadLocalItemAfterCdItemFull(CItemEx &item); - ISequentialInStream *CreateLimitedStream(UInt64 position, UInt64 size); + HRESULT GetItemStream(const CItemEx &item, bool seekPackData, CMyComPtr<ISequentialInStream> &stream); - UInt64 GetOffsetInStream(UInt64 offsetFromArc) const { return ArcInfo.Base + offsetFromArc; } + IInStream *GetBaseStream() { return StreamRef; } bool CanUpdate() const { - if (AreThereErrors()) - return false; - if (ArcInfo.Base < 0) - return false; - if ((Int64)ArcInfo.MarkerPos2 < ArcInfo.Base) + if (AreThereErrors() + || IsMultiVol + || ArcInfo.Base < 0 + || (Int64)ArcInfo.MarkerPos2 < ArcInfo.Base + || ArcInfo.ThereIsTail + || GetEmbeddedStubSize() != 0) return false; // 7-zip probably can update archives with embedded stubs. // we just disable that feature for more safety. - if (ArcInfo.GetEmbeddedStubSize() != 0) - return false; - if (ArcInfo.ThereIsTail()) - return false; return true; } }; diff --git a/CPP/7zip/Archive/Zip/ZipItem.h b/CPP/7zip/Archive/Zip/ZipItem.h index 5f078b60..c134ec79 100644 --- a/CPP/7zip/Archive/Zip/ZipItem.h +++ b/CPP/7zip/Archive/Zip/ZipItem.h @@ -199,6 +199,8 @@ public: UInt64 PackSize; UInt32 Time; UInt32 Crc; + + UInt32 Disk; AString Name; diff --git a/CPP/7zip/Archive/Zip/ZipRegister.cpp b/CPP/7zip/Archive/Zip/ZipRegister.cpp index 2be783e6..6674189f 100644 --- a/CPP/7zip/Archive/Zip/ZipRegister.cpp +++ b/CPP/7zip/Archive/Zip/ZipRegister.cpp @@ -12,10 +12,11 @@ namespace NZip { static const Byte k_Signature[] = { 4, 0x50, 0x4B, 0x03, 0x04, 4, 0x50, 0x4B, 0x05, 0x06, + 6, 0x50, 0x4B, 0x07, 0x08, 0x50, 0x4B, 6, 0x50, 0x4B, 0x30, 0x30, 0x50, 0x4B }; REGISTER_ARC_IO( - "zip", "zip zipx jar xpi odt ods docx xlsx epub", 0, 1, + "zip", "zip z01 zipx jar xpi odt ods docx xlsx epub", 0, 1, k_Signature, 0, NArcInfoFlags::kFindSignature | diff --git a/CPP/7zip/Archive/Zip/ZipUpdate.cpp b/CPP/7zip/Archive/Zip/ZipUpdate.cpp index 3128939e..1546a2af 100644 --- a/CPP/7zip/Archive/Zip/ZipUpdate.cpp +++ b/CPP/7zip/Archive/Zip/ZipUpdate.cpp @@ -52,16 +52,6 @@ static HRESULT CopyBlockToArchive(ISequentialInStream *inStream, UInt64 size, return NCompress::CopyStream_ExactSize(inStream, outStream, size, progress); } -static HRESULT WriteRange(IInStream *inStream, COutArchive &outArchive, - const CUpdateRange &range, ICompressProgressInfo *progress) -{ - UInt64 position; - RINOK(inStream->Seek(range.Position, STREAM_SEEK_SET, &position)); - RINOK(CopyBlockToArchive(inStream, range.Size, outArchive, progress)); - return progress->SetRatioInfo(&range.Size, &range.Size); -} - - static void SetFileHeader( COutArchive &archive, const CCompressionMethodMode &options, @@ -358,9 +348,12 @@ static HRESULT UpdateItemOldData( return E_NOTIMPL; // use old name size. - // CUpdateRange range(item.GetLocalExtraPosition(), item.LocalExtraSize + item.PackSize); - CUpdateRange range(inArchive->GetOffsetInStream(itemEx.GetDataPosition()), itemEx.PackSize); + CMyComPtr<ISequentialInStream> packStream; + RINOK(inArchive->GetItemStream(itemEx, true, packStream)); + if (!packStream) + return E_NOTIMPL; + // we keep ExternalAttrib and some another properties from old archive // item.ExternalAttrib = ui.Attrib; @@ -378,19 +371,27 @@ static HRESULT UpdateItemOldData( archive.PrepareWriteCompressedData2(item.Name.Len(), item.Size, item.PackSize, item.LocalExtra.HasWzAes()); archive.WriteLocalHeader(item); - RINOK(WriteRange(inArchive->Stream, archive, range, progress)); - complexity += range.Size; + + RINOK(CopyBlockToArchive(packStream, itemEx.PackSize, archive, progress)); + + complexity += itemEx.PackSize; } else { - CUpdateRange range(inArchive->GetOffsetInStream(itemEx.LocalHeaderPos), itemEx.GetLocalFullSize()); + CMyComPtr<ISequentialInStream> packStream; + RINOK(inArchive->GetItemStream(itemEx, false, packStream)); + if (!packStream) + return E_NOTIMPL; // set new header position item.LocalHeaderPos = archive.GetCurPos(); - RINOK(WriteRange(inArchive->Stream, archive, range, progress)); - complexity += range.Size; - archive.MoveCurPos(range.Size); + const UInt64 rangeSize = itemEx.GetLocalFullSize(); + + RINOK(CopyBlockToArchive(packStream, rangeSize, archive, progress)); + + complexity += rangeSize; + archive.MoveCurPos(rangeSize); } return S_OK; @@ -1191,10 +1192,11 @@ HRESULT Update( if (inArchive) { - if (inArchive->ArcInfo.Base > 0 && !removeSfx) + if (!inArchive->IsMultiVol && inArchive->ArcInfo.Base > 0 && !removeSfx) { - RINOK(inArchive->Stream->Seek(0, STREAM_SEEK_SET, NULL)); - RINOK(NCompress::CopyStream_ExactSize(inArchive->Stream, outStreamReal, inArchive->ArcInfo.Base, NULL)); + IInStream *baseStream = inArchive->GetBaseStream(); + RINOK(baseStream->Seek(0, STREAM_SEEK_SET, NULL)); + RINOK(NCompress::CopyStream_ExactSize(baseStream, outStreamReal, inArchive->ArcInfo.Base, NULL)); } } @@ -1210,11 +1212,12 @@ HRESULT Update( if (inArchive) { - if ((Int64)inArchive->ArcInfo.MarkerPos2 > inArchive->ArcInfo.Base) + if (!inArchive->IsMultiVol && (Int64)inArchive->ArcInfo.MarkerPos2 > inArchive->ArcInfo.Base) { - RINOK(inArchive->Stream->Seek(inArchive->ArcInfo.Base, STREAM_SEEK_SET, NULL)); + IInStream *baseStream = inArchive->GetBaseStream(); + RINOK(baseStream->Seek(inArchive->ArcInfo.Base, STREAM_SEEK_SET, NULL)); UInt64 embStubSize = inArchive->ArcInfo.MarkerPos2 - inArchive->ArcInfo.Base; - RINOK(NCompress::CopyStream_ExactSize(inArchive->Stream, outStream, embStubSize, NULL)); + RINOK(NCompress::CopyStream_ExactSize(baseStream, outStream, embStubSize, NULL)); outArchive.MoveCurPos(embStubSize); } } |