diff options
author | Igor Pavlov <ipavlov@users.sourceforge.net> | 2014-11-23 03:00:00 +0300 |
---|---|---|
committer | Kornel LesiĆski <kornel@geekhood.net> | 2016-05-28 02:16:51 +0300 |
commit | f08f4dcc3c02464c17753b3feafcfe5243b9e236 (patch) | |
tree | b0e1b15bc5368d92dff422e8ec0818564a2b00b8 /CPP/7zip/Archive/Cab/CabIn.cpp | |
parent | 83f8ddcc5b2161e1e3c49666265257fca8aeb12c (diff) |
9.349.34
Diffstat (limited to 'CPP/7zip/Archive/Cab/CabIn.cpp')
-rw-r--r--[-rwxr-xr-x] | CPP/7zip/Archive/Cab/CabIn.cpp | 446 |
1 files changed, 323 insertions, 123 deletions
diff --git a/CPP/7zip/Archive/Cab/CabIn.cpp b/CPP/7zip/Archive/Cab/CabIn.cpp index c0bffa2d..c499f05f 100755..100644 --- a/CPP/7zip/Archive/Cab/CabIn.cpp +++ b/CPP/7zip/Archive/Cab/CabIn.cpp @@ -2,154 +2,353 @@ #include "StdAfx.h" -#include "../Common/FindSignature.h" +// #include <stdio.h> + +#include "../../../../C/CpuArch.h" + +#include "../../Common/LimitedStreams.h" +#include "../../Common/StreamUtils.h" #include "CabIn.h" +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) + namespace NArchive { namespace NCab { -Byte CInArchive::Read8() +struct CUnexpectedEndException {}; + +void CInArchive::Skip(unsigned size) { - Byte b; - if (!inBuffer.ReadByte(b)) - throw CInArchiveException(CInArchiveException::kUnsupported); - return b; + if (_inBuffer.Skip(size) != size) + throw CUnexpectedEndException(); } -UInt16 CInArchive::Read16() +void CInArchive::Read(Byte *data, unsigned size) { - UInt16 value = 0; - for (int i = 0; i < 2; i++) - { - Byte b = Read8(); - value |= (UInt16(b) << (8 * i)); - } - return value; + if (_inBuffer.ReadBytes(data, size) != size) + throw CUnexpectedEndException(); } -UInt32 CInArchive::Read32() +void CInArchive::ReadName(AString &s) { - UInt32 value = 0; - for (int i = 0; i < 4; i++) + for (size_t i = 0; i < ((size_t)1 << 13); i++) { - Byte b = Read8(); - value |= (UInt32(b) << (8 * i)); + Byte b; + if (!_inBuffer.ReadByte(b)) + throw CUnexpectedEndException(); + if (b == 0) + { + memcpy(s.GetBuffer((unsigned)i), _tempBuf, i); + s.ReleaseBuffer((unsigned)i); + return; + } + if (_tempBuf.Size() == i) + _tempBuf.ChangeSize_KeepData(i * 2, i); + _tempBuf[i] = b; } - return value; -} - -AString CInArchive::SafeReadName() -{ - AString name; + for (;;) { - Byte b = Read8(); + Byte b; + if (!_inBuffer.ReadByte(b)) + throw CUnexpectedEndException(); if (b == 0) - return name; - name += (char)b; + break; } + + ErrorInNames = true; + s = "[ERROR-LONG-PATH]"; } -void CInArchive::ReadOtherArchive(COtherArchive &oa) +void CInArchive::ReadOtherArc(COtherArc &oa) { - oa.FileName = SafeReadName(); - oa.DiskName = SafeReadName(); + ReadName(oa.FileName); + ReadName(oa.DiskName); } -void CInArchive::Skip(UInt32 size) +struct CSignatureFinder +{ + Byte *Buf; + UInt32 Pos; + UInt32 End; + const Byte *Signature; + UInt32 SignatureSize; + + UInt32 _HeaderSize; + UInt32 _AlignSize; + UInt32 _BufUseCapacity; + + ISequentialInStream *Stream; + UInt64 Processed; // Global offset of start of Buf + + const UInt64 *SearchLimit; + + UInt32 GetTotalCapacity(UInt32 basicSize, UInt32 headerSize) + { + _HeaderSize = headerSize; + for (_AlignSize = (1 << 5); _AlignSize < _HeaderSize; _AlignSize <<= 1); + _BufUseCapacity = basicSize + _AlignSize; + return _BufUseCapacity + 16; + } + + /* + returns: + S_OK - signature found (at Pos) + S_FALSE - signature not found + */ + HRESULT Find(); +}; + +HRESULT CSignatureFinder::Find() { - while (size-- != 0) - Read8(); + for (;;) + { + Buf[End] = Signature[0]; // it's for fast search; + + while (End - Pos >= _HeaderSize) + { + const Byte *p = Buf + Pos; + Byte b = Signature[0]; + for (;;) + { + if (*p == b) break; p++; + if (*p == b) break; p++; + } + Pos = (UInt32)(p - Buf); + if (End - Pos < _HeaderSize) + { + Pos = End - _HeaderSize + 1; + break; + } + UInt32 i; + for (i = 1; i < SignatureSize && p[i] == Signature[i]; i++); + if (i == SignatureSize) + return S_OK; + Pos++; + } + + if (Pos >= _AlignSize) + { + UInt32 num = (Pos & ~(_AlignSize - 1)); + Processed += num; + Pos -= num; + End -= num; + memmove(Buf, Buf + num, End); + } + UInt32 rem = _BufUseCapacity - End; + if (SearchLimit) + { + if (Processed + Pos > *SearchLimit) + return S_FALSE; + UInt64 rem2 = *SearchLimit - (Processed + End) + _HeaderSize; + if (rem > rem2) + rem = (UInt32)rem2; + } + + UInt32 processedSize; + if (Processed == 0 && rem == _BufUseCapacity - _HeaderSize) + rem -= _AlignSize; // to make reads more aligned. + RINOK(Stream->Read(Buf + End, rem, &processedSize)); + if (processedSize == 0) + return S_FALSE; + End += processedSize; + } } -HRESULT CInArchive::Open(const UInt64 *searchHeaderSizeLimit, CDatabaseEx &db) +bool CInArcInfo::Parse(const Byte *p) +{ + if (Get32(p + 0x0C) != 0 || + Get32(p + 0x14) != 0) + return false; + Size = Get32(p + 8); + if (Size < 36) + return false; + Flags = Get16(p + 0x1E); + if (Flags > 7) + return false; + FileHeadersOffset = Get32(p + 0x10); + if (FileHeadersOffset != 0 && FileHeadersOffset > Size) + return false; + VersionMinor = p[0x18]; + VersionMajor = p[0x19]; + NumFolders = Get16(p + 0x1A); + NumFiles = Get16(p + 0x1C); + return true; +} + +HRESULT CInArchive::Open2(CDatabaseEx &db, const UInt64 *searchHeaderSizeLimit) { - IInStream *stream = db.Stream; + IsArc = false; + ErrorInNames = false; + UnexpectedEnd = false; + HeaderError = false; + db.Clear(); - RINOK(stream->Seek(0, STREAM_SEEK_SET, &db.StartPosition)); - - RINOK(FindSignatureInStream(stream, NHeader::kMarker, NHeader::kMarkerSize, - searchHeaderSizeLimit, db.StartPosition)); - - RINOK(stream->Seek(db.StartPosition + NHeader::kMarkerSize, STREAM_SEEK_SET, NULL)); - if (!inBuffer.Create(1 << 17)) - return E_OUTOFMEMORY; - inBuffer.SetStream(stream); - inBuffer.Init(); - - CInArchiveInfo &ai = db.ArchiveInfo; - - ai.Size = Read32(); - if (Read32() != 0) - return S_FALSE; - ai.FileHeadersOffset = Read32(); - if (Read32() != 0) - return S_FALSE; - - ai.VersionMinor = Read8(); - ai.VersionMajor = Read8(); - ai.NumFolders = Read16(); - ai.NumFiles = Read16(); - ai.Flags = Read16(); - if (ai.Flags > 7) - return S_FALSE; - ai.SetID = Read16(); - ai.CabinetNumber = Read16(); + RINOK(db.Stream->Seek(0, STREAM_SEEK_CUR, &db.StartPosition)); + // UInt64 temp = db.StartPosition; - if (ai.ReserveBlockPresent()) + CByteBuffer buffer; + CInArcInfo &ai = db.ArcInfo; + UInt64 startInBuf = 0; + + CLimitedSequentialInStream *limitedStreamSpec = NULL; + CMyComPtr<ISequentialInStream> limitedStream; + + // for (int iii = 0; iii < 10000; iii++) { - ai.PerCabinetAreaSize = Read16(); - ai.PerFolderAreaSize = Read8(); - ai.PerDataBlockAreaSize = Read8(); + // db.StartPosition = temp; RINOK(db.Stream->Seek(db.StartPosition, STREAM_SEEK_SET, NULL)); + + const UInt32 kMainHeaderSize = 32; + Byte header[kMainHeaderSize]; + const UInt32 kBufSize = 1 << 15; + RINOK(ReadStream_FALSE(db.Stream, header, kMainHeaderSize)); + if (memcmp(header, NHeader::kMarker, NHeader::kMarkerSize) == 0 && ai.Parse(header)) + { + limitedStreamSpec = new CLimitedSequentialInStream; + limitedStream = limitedStreamSpec; + limitedStreamSpec->SetStream(db.Stream); + limitedStreamSpec->Init(ai.Size - NHeader::kMarkerSize); + buffer.Alloc(kBufSize); + memcpy(buffer, header, kMainHeaderSize); + UInt32 numProcessedBytes; + RINOK(limitedStream->Read(buffer + kMainHeaderSize, kBufSize - kMainHeaderSize, &numProcessedBytes)); + _inBuffer.SetBuf(buffer, (UInt32)kBufSize, kMainHeaderSize + numProcessedBytes, kMainHeaderSize); + } + else + { + if (searchHeaderSizeLimit && *searchHeaderSizeLimit == 0) + return S_FALSE; + + CSignatureFinder finder; + + finder.Stream = db.Stream; + finder.Signature = NHeader::kMarker; + finder.SignatureSize = NHeader::kMarkerSize; + finder.SearchLimit = searchHeaderSizeLimit; - Skip(ai.PerCabinetAreaSize); + buffer.Alloc(finder.GetTotalCapacity(kBufSize, kMainHeaderSize)); + finder.Buf = buffer; + + memcpy(buffer, header, kMainHeaderSize); + finder.Processed = db.StartPosition; + finder.End = kMainHeaderSize; + finder.Pos = 1; + + for (;;) + { + RINOK(finder.Find()); + if (ai.Parse(finder.Buf + finder.Pos)) + { + db.StartPosition = finder.Processed + finder.Pos; + limitedStreamSpec = new CLimitedSequentialInStream; + limitedStreamSpec->SetStream(db.Stream); + limitedStream = limitedStreamSpec; + UInt32 remInFinder = finder.End - finder.Pos; + if (ai.Size <= remInFinder) + { + limitedStreamSpec->Init(0); + finder.End = finder.Pos + ai.Size; + } + else + limitedStreamSpec->Init(ai.Size - remInFinder); + + startInBuf = finder.Pos; + _inBuffer.SetBuf(buffer, (UInt32)kBufSize, finder.End, finder.Pos + kMainHeaderSize); + break; + } + finder.Pos++; + } + } } + + IsArc = true; + _inBuffer.SetStream(limitedStream); + if (_tempBuf.Size() == 0) + _tempBuf.Alloc(1 << 12); + + Byte p[16]; + unsigned nextSize = 4 + (ai.ReserveBlockPresent() ? 4 : 0); + Read(p, nextSize); + ai.SetID = Get16(p); + ai.CabinetNumber = Get16(p + 2); + + if (ai.ReserveBlockPresent()) { - if (ai.IsTherePrev()) - ReadOtherArchive(ai.PrevArc); - if (ai.IsThereNext()) - ReadOtherArchive(ai.NextArc); + ai.PerCabinet_AreaSize = Get16(p + 4); + ai.PerFolder_AreaSize = p[6]; + ai.PerDataBlock_AreaSize = p[7]; + Skip(ai.PerCabinet_AreaSize); } + + if (ai.IsTherePrev()) ReadOtherArc(ai.PrevArc); + if (ai.IsThereNext()) ReadOtherArc(ai.NextArc); - int i; + UInt32 i; + db.Folders.ClearAndReserve(ai.NumFolders); for (i = 0; i < ai.NumFolders; i++) { + Read(p, 8); CFolder folder; - - folder.DataStart = Read32(); - folder.NumDataBlocks = Read16(); - folder.CompressionTypeMajor = Read8(); - folder.CompressionTypeMinor = Read8(); - - Skip(ai.PerFolderAreaSize); - db.Folders.Add(folder); + folder.DataStart = Get32(p); + folder.NumDataBlocks = Get16(p + 4); + folder.MethodMajor = p[6]; + folder.MethodMinor = p[7]; + Skip(ai.PerFolder_AreaSize); + db.Folders.AddInReserved(folder); } - RINOK(stream->Seek(db.StartPosition + ai.FileHeadersOffset, STREAM_SEEK_SET, NULL)); + // for (int iii = 0; iii < 10000; iii++) { + + if (_inBuffer.GetProcessedSize() - startInBuf != ai.FileHeadersOffset) + { + // printf("\n!!! Seek Error !!!!\n"); + // fflush(stdout); + RINOK(db.Stream->Seek(db.StartPosition + ai.FileHeadersOffset, STREAM_SEEK_SET, NULL)); + limitedStreamSpec->Init(ai.Size - ai.FileHeadersOffset); + _inBuffer.Init(); + } - inBuffer.SetStream(stream); - inBuffer.Init(); + db.Items.ClearAndReserve(ai.NumFiles); for (i = 0; i < ai.NumFiles; i++) { - CItem item; - item.Size = Read32(); - item.Offset = Read32(); - item.FolderIndex = Read16(); - UInt16 pureDate = Read16(); - UInt16 pureTime = Read16(); - item.Time = ((UInt32(pureDate) << 16)) | pureTime; - item.Attributes = Read16(); - item.Name = SafeReadName(); - int folderIndex = item.GetFolderIndex(db.Folders.Size()); - if (folderIndex >= db.Folders.Size()) + Read(p, 16); + CItem &item = db.Items.AddNewInReserved(); + item.Size = Get32(p); + item.Offset = Get32(p + 4); + item.FolderIndex = Get16(p + 8); + UInt16 pureDate = Get16(p + 10); + UInt16 pureTime = Get16(p + 12); + item.Time = (((UInt32)pureDate << 16)) | pureTime; + item.Attributes = Get16(p + 14); + + ReadName(item.Name); + if (item.GetFolderIndex(db.Folders.Size()) >= (int)db.Folders.Size()) + { + HeaderError = true; return S_FALSE; - db.Items.Add(item); + } } + + // } + return S_OK; } +HRESULT CInArchive::Open(CDatabaseEx &db, const UInt64 *searchHeaderSizeLimit) +{ + try + { + return Open2(db, searchHeaderSizeLimit); + } + catch(const CInBufferException &e) { return e.ErrorCode; } + catch(CUnexpectedEndException &) { UnexpectedEnd = true; return S_FALSE; } +} + + + #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } static int CompareMvItems(const CMvItem *p1, const CMvItem *p2, void *param) @@ -161,10 +360,8 @@ static int CompareMvItems(const CMvItem *p1, const CMvItem *p2, void *param) const CItem &item2 = db2.Items[p2->ItemIndex];; bool isDir1 = item1.IsDir(); bool isDir2 = item2.IsDir(); - if (isDir1 && !isDir2) - return -1; - if (isDir2 && !isDir1) - return 1; + if (isDir1 && !isDir2) return -1; + if (isDir2 && !isDir1) return 1; int f1 = mvDb.GetFolderIndex(p1); int f2 = mvDb.GetFolderIndex(p2); RINOZ(MyCompare(f1, f2)); @@ -174,7 +371,7 @@ static int CompareMvItems(const CMvItem *p1, const CMvItem *p2, void *param) return MyCompare(p1->ItemIndex, p2->ItemIndex); } -bool CMvDatabaseEx::AreItemsEqual(int i1, int i2) +bool CMvDatabaseEx::AreItemsEqual(unsigned i1, unsigned i2) { const CMvItem *p1 = &Items[i1]; const CMvItem *p2 = &Items[i2]; @@ -182,10 +379,10 @@ bool CMvDatabaseEx::AreItemsEqual(int i1, int i2) const CDatabaseEx &db2 = Volumes[p2->VolumeIndex]; const CItem &item1 = db1.Items[p1->ItemIndex]; const CItem &item2 = db2.Items[p2->ItemIndex];; - return GetFolderIndex(p1) == GetFolderIndex(p2) && - item1.Offset == item2.Offset && - item1.Size == item2.Size && - item1.Name == item2.Name; + return GetFolderIndex(p1) == GetFolderIndex(p2) + && item1.Offset == item2.Offset + && item1.Size == item2.Size + && item1.Name == item2.Name; } void CMvDatabaseEx::FillSortAndShrink() @@ -194,7 +391,7 @@ void CMvDatabaseEx::FillSortAndShrink() StartFolderOfVol.Clear(); FolderStartFileIndex.Clear(); int offset = 0; - for (int v = 0; v < Volumes.Size(); v++) + FOR_VECTOR (v, Volumes) { const CDatabaseEx &db = Volumes[v]; int curOffset = offset; @@ -205,32 +402,35 @@ void CMvDatabaseEx::FillSortAndShrink() CMvItem mvItem; mvItem.VolumeIndex = v; - for (int i = 0 ; i < db.Items.Size(); i++) + FOR_VECTOR (i, db.Items) { mvItem.ItemIndex = i; Items.Add(mvItem); } } - Items.Sort(CompareMvItems, (void *)this); - int j = 1; - int i; - for (i = 1; i < Items.Size(); i++) - if (!AreItemsEqual(i, i -1)) - Items[j++] = Items[i]; - Items.DeleteFrom(j); + if (Items.Size() > 1) + { + Items.Sort(CompareMvItems, (void *)this); + unsigned j = 1; + unsigned i = 1; + for (; i < Items.Size(); i++) + if (!AreItemsEqual(i, i - 1)) + Items[j++] = Items[i]; + Items.DeleteFrom(j); + } - for (i = 0; i < Items.Size(); i++) + FOR_VECTOR (i, Items) { int folderIndex = GetFolderIndex(&Items[i]); - if (folderIndex >= FolderStartFileIndex.Size()) + if (folderIndex >= (int)FolderStartFileIndex.Size()) FolderStartFileIndex.Add(i); } } bool CMvDatabaseEx::Check() { - for (int v = 1; v < Volumes.Size(); v++) + for (unsigned v = 1; v < Volumes.Size(); v++) { const CDatabaseEx &db1 = Volumes[v]; if (db1.IsTherePrevFolder()) @@ -240,19 +440,19 @@ bool CMvDatabaseEx::Check() return false; const CFolder &f0 = db0.Folders.Back(); const CFolder &f1 = db1.Folders.Front(); - if (f0.CompressionTypeMajor != f1.CompressionTypeMajor || - f0.CompressionTypeMinor != f1.CompressionTypeMinor) + if (f0.MethodMajor != f1.MethodMajor || + f0.MethodMinor != f1.MethodMinor) return false; } } UInt32 beginPos = 0; UInt64 endPos = 0; int prevFolder = -2; - for (int i = 0; i < Items.Size(); i++) + FOR_VECTOR (i, Items) { const CMvItem &mvItem = Items[i]; int fIndex = GetFolderIndex(&mvItem); - if (fIndex >= FolderStartFileIndex.Size()) + if (fIndex >= (int)FolderStartFileIndex.Size()) return false; const CItem &item = Volumes[mvItem.VolumeIndex].Items[mvItem.ItemIndex]; if (item.IsDir()) |