in src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs [3447:3658]
private long LocateBlockWithSignature(int signature, long endLocation, int minimumBlockSize, int maximumVariableData)
=> ZipFormat.LocateBlockWithSignature(baseStream_, signature, endLocation, minimumBlockSize, maximumVariableData);
/// <summary>
/// Search for and read the central directory of a zip file filling the entries array.
/// </summary>
/// <exception cref="System.IO.IOException">
/// An i/o error occurs.
/// </exception>
/// <exception cref="ICSharpCode.SharpZipLib.Zip.ZipException">
/// The central directory is malformed or cannot be found
/// </exception>
private void ReadEntries()
{
// Search for the End Of Central Directory. When a zip comment is
// present the directory will start earlier
//
// The search is limited to 64K which is the maximum size of a trailing comment field to aid speed.
// This should be compatible with both SFX and ZIP files but has only been tested for Zip files
// If a SFX file has the Zip data attached as a resource and there are other resources occurring later then
// this could be invalid.
// Could also speed this up by reading memory in larger blocks.
if (baseStream_.CanSeek == false)
{
throw new ZipException("ZipFile stream must be seekable");
}
long locatedEndOfCentralDir = LocateBlockWithSignature(ZipConstants.EndOfCentralDirectorySignature,
baseStream_.Length, ZipConstants.EndOfCentralRecordBaseSize, 0xffff);
if (locatedEndOfCentralDir < 0)
{
throw new ZipException("Cannot find central directory");
}
// Read end of central directory record
ushort thisDiskNumber = ReadLEUshort();
ushort startCentralDirDisk = ReadLEUshort();
ulong entriesForThisDisk = ReadLEUshort();
ulong entriesForWholeCentralDir = ReadLEUshort();
ulong centralDirSize = ReadLEUint();
long offsetOfCentralDir = ReadLEUint();
uint commentSize = ReadLEUshort();
if (commentSize > 0)
{
byte[] comment = new byte[commentSize];
StreamUtils.ReadFully(baseStream_, comment);
comment_ = _stringCodec.ZipArchiveCommentEncoding.GetString(comment);
}
else
{
comment_ = string.Empty;
}
bool isZip64 = false;
bool requireZip64 = false;
// Check if zip64 header information is required.
if ((thisDiskNumber == 0xffff) ||
(startCentralDirDisk == 0xffff) ||
(entriesForThisDisk == 0xffff) ||
(entriesForWholeCentralDir == 0xffff) ||
(centralDirSize == 0xffffffff) ||
(offsetOfCentralDir == 0xffffffff))
{
requireZip64 = true;
}
// #357 - always check for the existance of the Zip64 central directory.
// #403 - Take account of the fixed size of the locator when searching.
// Subtract from locatedEndOfCentralDir so that the endLocation is the location of EndOfCentralDirectorySignature,
// rather than the data following the signature.
long locatedZip64EndOfCentralDirLocator = LocateBlockWithSignature(
ZipConstants.Zip64CentralDirLocatorSignature,
locatedEndOfCentralDir - 4,
ZipConstants.Zip64EndOfCentralDirectoryLocatorSize,
0);
if (locatedZip64EndOfCentralDirLocator < 0)
{
if (requireZip64)
{
// This is only an error in cases where the Zip64 directory is required.
throw new ZipException("Cannot find Zip64 locator");
}
}
else
{
isZip64 = true;
// number of the disk with the start of the zip64 end of central directory 4 bytes
// relative offset of the zip64 end of central directory record 8 bytes
// total number of disks 4 bytes
ReadLEUint(); // startDisk64 is not currently used
ulong offset64 = ReadLEUlong();
uint totalDisks = ReadLEUint();
baseStream_.Position = (long)offset64;
long sig64 = ReadLEUint();
if (sig64 != ZipConstants.Zip64CentralFileHeaderSignature)
{
throw new ZipException(string.Format("Invalid Zip64 Central directory signature at {0:X}", offset64));
}
// NOTE: Record size = SizeOfFixedFields + SizeOfVariableData - 12.
ulong recordSize = ReadLEUlong();
int versionMadeBy = ReadLEUshort();
int versionToExtract = ReadLEUshort();
uint thisDisk = ReadLEUint();
uint centralDirDisk = ReadLEUint();
entriesForThisDisk = ReadLEUlong();
entriesForWholeCentralDir = ReadLEUlong();
centralDirSize = ReadLEUlong();
offsetOfCentralDir = (long)ReadLEUlong();
// NOTE: zip64 extensible data sector (variable size) is ignored.
}
entries_ = new ZipEntry[entriesForThisDisk];
// SFX/embedded support, find the offset of the first entry vis the start of the stream
// This applies to Zip files that are appended to the end of an SFX stub.
// Or are appended as a resource to an executable.
// Zip files created by some archivers have the offsets altered to reflect the true offsets
// and so dont require any adjustment here...
// TODO: Difficulty with Zip64 and SFX offset handling needs resolution - maths?
if (!isZip64 && (offsetOfCentralDir < locatedEndOfCentralDir - (4 + (long)centralDirSize)))
{
offsetOfFirstEntry = locatedEndOfCentralDir - (4 + (long)centralDirSize + offsetOfCentralDir);
if (offsetOfFirstEntry <= 0)
{
throw new ZipException("Invalid embedded zip archive");
}
}
baseStream_.Seek(offsetOfFirstEntry + offsetOfCentralDir, SeekOrigin.Begin);
for (ulong i = 0; i < entriesForThisDisk; i++)
{
if (ReadLEUint() != ZipConstants.CentralHeaderSignature)
{
throw new ZipException("Wrong Central Directory signature");
}
int versionMadeBy = ReadLEUshort();
int versionToExtract = ReadLEUshort();
int bitFlags = ReadLEUshort();
int method = ReadLEUshort();
uint dostime = ReadLEUint();
uint crc = ReadLEUint();
var csize = (long)ReadLEUint();
var size = (long)ReadLEUint();
int nameLen = ReadLEUshort();
int extraLen = ReadLEUshort();
int commentLen = ReadLEUshort();
int diskStartNo = ReadLEUshort(); // Not currently used
int internalAttributes = ReadLEUshort(); // Not currently used
uint externalAttributes = ReadLEUint();
long offset = ReadLEUint();
byte[] buffer = new byte[Math.Max(nameLen, commentLen)];
var entryEncoding = _stringCodec.ZipInputEncoding(bitFlags);
StreamUtils.ReadFully(baseStream_, buffer, 0, nameLen);
string name = entryEncoding.GetString(buffer, 0, nameLen);
var unicode = entryEncoding.IsZipUnicode();
var entry = new ZipEntry(name, versionToExtract, versionMadeBy, (CompressionMethod)method, unicode)
{
Crc = crc & 0xffffffffL,
Size = size & 0xffffffffL,
CompressedSize = csize & 0xffffffffL,
Flags = bitFlags,
DosTime = dostime,
ZipFileIndex = (long)i,
Offset = offset,
ExternalFileAttributes = (int)externalAttributes
};
if ((bitFlags & 8) == 0)
{
entry.CryptoCheckValue = (byte)(crc >> 24);
}
else
{
entry.CryptoCheckValue = (byte)((dostime >> 8) & 0xff);
}
if (extraLen > 0)
{
byte[] extra = new byte[extraLen];
StreamUtils.ReadFully(baseStream_, extra);
entry.ExtraData = extra;
}
entry.ProcessExtraData(false);
if (commentLen > 0)
{
StreamUtils.ReadFully(baseStream_, buffer, 0, commentLen);
entry.Comment = entryEncoding.GetString(buffer, 0, commentLen);
}
entries_[i] = entry;
}
}