//writes eocd, and if needed, zip 64 eocd, zip64 eocd locator //should only throw an exception in extremely exceptional cases because it is called from dispose private void WriteArchiveEpilogue(long startOfCentralDirectory, long sizeOfCentralDirectory) { //determine if we need Zip 64 bool needZip64 = false; if (startOfCentralDirectory >= uint.MaxValue || sizeOfCentralDirectory >= uint.MaxValue || _entries.Count >= ZipHelper.Mask16Bit #if DEBUG_FORCE_ZIP64 || _forceZip64 #endif ) { needZip64 = true; } //if we need zip 64, write zip 64 eocd and locator if (needZip64) { long zip64EOCDRecordStart = _archiveStream.Position; Zip64EndOfCentralDirectoryRecord.WriteBlock(_archiveStream, _entries.Count, startOfCentralDirectory, sizeOfCentralDirectory); Zip64EndOfCentralDirectoryLocator.WriteBlock(_archiveStream, zip64EOCDRecordStart); } //write normal eocd ZipEndOfCentralDirectoryBlock.WriteBlock(_archiveStream, _entries.Count, startOfCentralDirectory, sizeOfCentralDirectory, _archiveComment); }
private void WriteArchiveEpilogue(long startOfCentralDirectory, long sizeOfCentralDirectory) { bool flag = false; if (startOfCentralDirectory >= (ulong)-1 || sizeOfCentralDirectory >= (ulong)-1 || this._entries.Count >= 65535) { flag = true; } if (flag) { long position = this._archiveStream.Position; Zip64EndOfCentralDirectoryRecord.WriteBlock(this._archiveStream, (long)this._entries.Count, startOfCentralDirectory, sizeOfCentralDirectory); Zip64EndOfCentralDirectoryLocator.WriteBlock(this._archiveStream, position); } ZipEndOfCentralDirectoryBlock.WriteBlock(this._archiveStream, (long)this._entries.Count, startOfCentralDirectory, sizeOfCentralDirectory, this._archiveComment); }
public static bool TryReadBlock(BinaryReader reader, out ZipEndOfCentralDirectoryBlock eocdBlock) { eocdBlock = new ZipEndOfCentralDirectoryBlock(); if (reader.ReadUInt32() != 101010256) { return(false); } eocdBlock.Signature = 101010256; eocdBlock.NumberOfThisDisk = reader.ReadUInt16(); eocdBlock.NumberOfTheDiskWithTheStartOfTheCentralDirectory = reader.ReadUInt16(); eocdBlock.NumberOfEntriesInTheCentralDirectoryOnThisDisk = reader.ReadUInt16(); eocdBlock.NumberOfEntriesInTheCentralDirectory = reader.ReadUInt16(); eocdBlock.SizeOfCentralDirectory = reader.ReadUInt32(); eocdBlock.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = reader.ReadUInt32(); eocdBlock.ArchiveComment = reader.ReadBytes((int)reader.ReadUInt16()); return(true); }
public static bool TryReadBlock(BinaryReader reader, out ZipEndOfCentralDirectoryBlock eocdBlock) { eocdBlock = default; if (reader.ReadUInt32() != SignatureConstant) { return(false); } eocdBlock.Signature = SignatureConstant; eocdBlock.NumberOfThisDisk = reader.ReadUInt16(); eocdBlock.NumberOfTheDiskWithTheStartOfTheCentralDirectory = reader.ReadUInt16(); eocdBlock.NumberOfEntriesInTheCentralDirectoryOnThisDisk = reader.ReadUInt16(); eocdBlock.NumberOfEntriesInTheCentralDirectory = reader.ReadUInt16(); eocdBlock.SizeOfCentralDirectory = reader.ReadUInt32(); eocdBlock.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = reader.ReadUInt32(); ushort commentLength = reader.ReadUInt16(); eocdBlock.ArchiveComment = reader.ReadBytes(commentLength); return(true); }
//This function reads all the EOCD stuff it needs to find the offset to the start of the central directory //This offset gets put in _centralDirectoryStart and the number of this disk gets put in _numberOfThisDisk //Also does some verification that this isn't a split/spanned archive //Also checks that offset to CD isn't out of bounds private void ReadEndOfCentralDirectory() { try { //this seeks to the start of the end of central directory record _archiveStream.Seek(-ZipEndOfCentralDirectoryBlock.SizeOfBlockWithoutSignature, SeekOrigin.End); if (!ZipHelper.SeekBackwardsToSignature(_archiveStream, ZipEndOfCentralDirectoryBlock.SignatureConstant)) { throw new InvalidDataException(SR.EOCDNotFound); } long eocdStart = _archiveStream.Position; //read the EOCD ZipEndOfCentralDirectoryBlock eocd; bool eocdProper = ZipEndOfCentralDirectoryBlock.TryReadBlock(_archiveReader, out eocd); Debug.Assert(eocdProper); //we just found this using the signature finder, so it should be okay if (eocd.NumberOfThisDisk != eocd.NumberOfTheDiskWithTheStartOfTheCentralDirectory) { throw new InvalidDataException(SR.SplitSpanned); } _numberOfThisDisk = eocd.NumberOfThisDisk; _centralDirectoryStart = eocd.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber; if (eocd.NumberOfEntriesInTheCentralDirectory != eocd.NumberOfEntriesInTheCentralDirectoryOnThisDisk) { throw new InvalidDataException(SR.SplitSpanned); } _expectedNumberOfEntries = eocd.NumberOfEntriesInTheCentralDirectory; //only bother saving the comment if we are in update mode if (_mode == ZipArchiveMode.Update) { _archiveComment = eocd.ArchiveComment; } //only bother looking for zip64 EOCD stuff if we suspect it is needed because some value is FFFFFFFFF //because these are the only two values we need, we only worry about these //if we don't find the zip64 EOCD, we just give up and try to use the original values if (eocd.NumberOfThisDisk == ZipHelper.Mask16Bit || eocd.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber == ZipHelper.Mask32Bit || eocd.NumberOfEntriesInTheCentralDirectory == ZipHelper.Mask16Bit) { //we need to look for zip 64 EOCD stuff //seek to the zip 64 EOCD locator _archiveStream.Seek(eocdStart - Zip64EndOfCentralDirectoryLocator.SizeOfBlockWithoutSignature, SeekOrigin.Begin); //if we don't find it, assume it doesn't exist and use data from normal eocd if (ZipHelper.SeekBackwardsToSignature(_archiveStream, Zip64EndOfCentralDirectoryLocator.SignatureConstant)) { //use locator to get to Zip64EOCD Zip64EndOfCentralDirectoryLocator locator; bool zip64eocdLocatorProper = Zip64EndOfCentralDirectoryLocator.TryReadBlock(_archiveReader, out locator); Debug.Assert(zip64eocdLocatorProper); //we just found this using the signature finder, so it should be okay if (locator.OffsetOfZip64EOCD > (ulong)long.MaxValue) { throw new InvalidDataException(SR.FieldTooBigOffsetToZip64EOCD); } long zip64EOCDOffset = (long)locator.OffsetOfZip64EOCD; _archiveStream.Seek(zip64EOCDOffset, SeekOrigin.Begin); //read Zip64EOCD Zip64EndOfCentralDirectoryRecord record; if (!Zip64EndOfCentralDirectoryRecord.TryReadBlock(_archiveReader, out record)) { throw new InvalidDataException(SR.Zip64EOCDNotWhereExpected); } _numberOfThisDisk = record.NumberOfThisDisk; if (record.NumberOfEntriesTotal > (ulong)long.MaxValue) { throw new InvalidDataException(SR.FieldTooBigNumEntries); } if (record.OffsetOfCentralDirectory > (ulong)long.MaxValue) { throw new InvalidDataException(SR.FieldTooBigOffsetToCD); } if (record.NumberOfEntriesTotal != record.NumberOfEntriesOnThisDisk) { throw new InvalidDataException(SR.SplitSpanned); } _expectedNumberOfEntries = (long)record.NumberOfEntriesTotal; _centralDirectoryStart = (long)record.OffsetOfCentralDirectory; } } if (_centralDirectoryStart > _archiveStream.Length) { throw new InvalidDataException(SR.FieldTooBigOffsetToCD); } } catch (EndOfStreamException ex) { throw new InvalidDataException(SR.CDCorrupt, ex); } catch (IOException ex) { throw new InvalidDataException(SR.CDCorrupt, ex); } }
// Tries to find the Zip64 End of Central Directory Locator, then the Zip64 End of Central Directory, assuming the // End of Central Directory block has already been found, as well as the location in the stream where the EOCD starts. private void TryReadZip64EndOfCentralDirectory(ZipEndOfCentralDirectoryBlock eocd, long eocdStart) { // Only bother looking for the Zip64-EOCD stuff if we suspect it is needed because some value is FFFFFFFFF // because these are the only two values we need, we only worry about these // if we don't find the Zip64-EOCD, we just give up and try to use the original values if (eocd.NumberOfThisDisk == ZipHelper.Mask16Bit || eocd.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber == ZipHelper.Mask32Bit || eocd.NumberOfEntriesInTheCentralDirectory == ZipHelper.Mask16Bit) { // Read Zip64 End of Central Directory Locator // This seeks forwards almost to the beginning of the Zip64-EOCDL, one byte after where the signature would be located _archiveStream.Seek(eocdStart - Zip64EndOfCentralDirectoryLocator.SizeOfBlockWithoutSignature, SeekOrigin.Begin); // Exactly the previous 4 bytes should contain the Zip64-EOCDL signature // if we don't find it, assume it doesn't exist and use data from normal EOCD if (ZipHelper.SeekBackwardsToSignature(_archiveStream, Zip64EndOfCentralDirectoryLocator.SignatureConstant, Zip64EndOfCentralDirectoryLocator.SignatureSize)) { Debug.Assert(_archiveReader != null); // use locator to get to Zip64-EOCD Zip64EndOfCentralDirectoryLocator locator; bool zip64eocdLocatorProper = Zip64EndOfCentralDirectoryLocator.TryReadBlock(_archiveReader, out locator); Debug.Assert(zip64eocdLocatorProper); // we just found this using the signature finder, so it should be okay if (locator.OffsetOfZip64EOCD > long.MaxValue) { throw new InvalidDataException(SR.FieldTooBigOffsetToZip64EOCD); } long zip64EOCDOffset = (long)locator.OffsetOfZip64EOCD; _archiveStream.Seek(zip64EOCDOffset, SeekOrigin.Begin); // Read Zip64 End of Central Directory Record Zip64EndOfCentralDirectoryRecord record; if (!Zip64EndOfCentralDirectoryRecord.TryReadBlock(_archiveReader, out record)) { throw new InvalidDataException(SR.Zip64EOCDNotWhereExpected); } _numberOfThisDisk = record.NumberOfThisDisk; if (record.NumberOfEntriesTotal > long.MaxValue) { throw new InvalidDataException(SR.FieldTooBigNumEntries); } if (record.OffsetOfCentralDirectory > long.MaxValue) { throw new InvalidDataException(SR.FieldTooBigOffsetToCD); } if (record.NumberOfEntriesTotal != record.NumberOfEntriesOnThisDisk) { throw new InvalidDataException(SR.SplitSpanned); } _expectedNumberOfEntries = (long)record.NumberOfEntriesTotal; _centralDirectoryStart = (long)record.OffsetOfCentralDirectory; } } }
// This function reads all the EOCD stuff it needs to find the offset to the start of the central directory // This offset gets put in _centralDirectoryStart and the number of this disk gets put in _numberOfThisDisk // Also does some verification that this isn't a split/spanned archive // Also checks that offset to CD isn't out of bounds private void ReadEndOfCentralDirectory() { try { // This seeks backwards almost to the beginning of the EOCD, one byte after where the signature would be // located if the EOCD had the minimum possible size (no file zip comment) _archiveStream.Seek(-ZipEndOfCentralDirectoryBlock.SizeOfBlockWithoutSignature, SeekOrigin.End); // If the EOCD has the minimum possible size (no zip file comment), then exactly the previous 4 bytes will contain the signature // But if the EOCD has max possible size, the signature should be found somewhere in the previous 64K + 4 bytes if (!ZipHelper.SeekBackwardsToSignature(_archiveStream, ZipEndOfCentralDirectoryBlock.SignatureConstant, ZipEndOfCentralDirectoryBlock.ZipFileCommentMaxLength + ZipEndOfCentralDirectoryBlock.SignatureSize)) { throw new InvalidDataException(SR.EOCDNotFound); } long eocdStart = _archiveStream.Position; // read the EOCD ZipEndOfCentralDirectoryBlock eocd; bool eocdProper = ZipEndOfCentralDirectoryBlock.TryReadBlock(_archiveReader, out eocd); Debug.Assert(eocdProper); // we just found this using the signature finder, so it should be okay if (eocd.NumberOfThisDisk != eocd.NumberOfTheDiskWithTheStartOfTheCentralDirectory) { throw new InvalidDataException(SR.SplitSpanned); } _numberOfThisDisk = eocd.NumberOfThisDisk; _centralDirectoryStart = eocd.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber; if (eocd.NumberOfEntriesInTheCentralDirectory != eocd.NumberOfEntriesInTheCentralDirectoryOnThisDisk) { throw new InvalidDataException(SR.SplitSpanned); } _expectedNumberOfEntries = eocd.NumberOfEntriesInTheCentralDirectory; // only bother saving the comment if we are in update mode if (_mode == ZipArchiveMode.Update) { _archiveComment = eocd.ArchiveComment; } TryReadZip64EndOfCentralDirectory(eocd, eocdStart); if (_centralDirectoryStart > _archiveStream.Length) { throw new InvalidDataException(SR.FieldTooBigOffsetToCD); } } catch (EndOfStreamException ex) { throw new InvalidDataException(SR.CDCorrupt, ex); } catch (IOException ex) { throw new InvalidDataException(SR.CDCorrupt, ex); } }
public static bool TryReadBlock(BinaryReader reader, out ZipEndOfCentralDirectoryBlock eocdBlock) { eocdBlock = new ZipEndOfCentralDirectoryBlock(); if (reader.ReadUInt32() != SignatureConstant) return false; eocdBlock.Signature = SignatureConstant; eocdBlock.NumberOfThisDisk = reader.ReadUInt16(); eocdBlock.NumberOfTheDiskWithTheStartOfTheCentralDirectory = reader.ReadUInt16(); eocdBlock.NumberOfEntriesInTheCentralDirectoryOnThisDisk = reader.ReadUInt16(); eocdBlock.NumberOfEntriesInTheCentralDirectory = reader.ReadUInt16(); eocdBlock.SizeOfCentralDirectory = reader.ReadUInt32(); eocdBlock.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = reader.ReadUInt32(); ushort commentLength = reader.ReadUInt16(); eocdBlock.ArchiveComment = reader.ReadBytes(commentLength); return true; }
private void ReadEndOfCentralDirectory() { ZipEndOfCentralDirectoryBlock zipEndOfCentralDirectoryBlock; Zip64EndOfCentralDirectoryLocator zip64EndOfCentralDirectoryLocator; Zip64EndOfCentralDirectoryRecord zip64EndOfCentralDirectoryRecord; try { this._archiveStream.Seek((long)-18, SeekOrigin.End); if (!ZipHelper.SeekBackwardsToSignature(this._archiveStream, 101010256)) { throw new InvalidDataException(Messages.EOCDNotFound); } long position = this._archiveStream.Position; ZipEndOfCentralDirectoryBlock.TryReadBlock(this._archiveReader, out zipEndOfCentralDirectoryBlock); if (zipEndOfCentralDirectoryBlock.NumberOfThisDisk != zipEndOfCentralDirectoryBlock.NumberOfTheDiskWithTheStartOfTheCentralDirectory) { throw new InvalidDataException(Messages.SplitSpanned); } this._numberOfThisDisk = zipEndOfCentralDirectoryBlock.NumberOfThisDisk; this._centralDirectoryStart = (long)zipEndOfCentralDirectoryBlock.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber; if (zipEndOfCentralDirectoryBlock.NumberOfEntriesInTheCentralDirectory != zipEndOfCentralDirectoryBlock.NumberOfEntriesInTheCentralDirectoryOnThisDisk) { throw new InvalidDataException(Messages.SplitSpanned); } this._expectedNumberOfEntries = (long)zipEndOfCentralDirectoryBlock.NumberOfEntriesInTheCentralDirectory; if (this._mode == ZipArchiveMode.Update) { this._archiveComment = zipEndOfCentralDirectoryBlock.ArchiveComment; } if (zipEndOfCentralDirectoryBlock.NumberOfThisDisk == 65535 || zipEndOfCentralDirectoryBlock.OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber == -1 || zipEndOfCentralDirectoryBlock.NumberOfEntriesInTheCentralDirectory == 65535) { this._archiveStream.Seek(position - (long)16, SeekOrigin.Begin); if (ZipHelper.SeekBackwardsToSignature(this._archiveStream, 117853008)) { Zip64EndOfCentralDirectoryLocator.TryReadBlock(this._archiveReader, out zip64EndOfCentralDirectoryLocator); if (zip64EndOfCentralDirectoryLocator.OffsetOfZip64EOCD > 9223372036854775807L) { throw new InvalidDataException(Messages.FieldTooBigOffsetToZip64EOCD); } long offsetOfZip64EOCD = (long)zip64EndOfCentralDirectoryLocator.OffsetOfZip64EOCD; this._archiveStream.Seek(offsetOfZip64EOCD, SeekOrigin.Begin); if (!Zip64EndOfCentralDirectoryRecord.TryReadBlock(this._archiveReader, out zip64EndOfCentralDirectoryRecord)) { throw new InvalidDataException(Messages.Zip64EOCDNotWhereExpected); } this._numberOfThisDisk = zip64EndOfCentralDirectoryRecord.NumberOfThisDisk; if (zip64EndOfCentralDirectoryRecord.NumberOfEntriesTotal > 9223372036854775807L) { throw new InvalidDataException(Messages.FieldTooBigNumEntries); } if (zip64EndOfCentralDirectoryRecord.OffsetOfCentralDirectory > 9223372036854775807L) { throw new InvalidDataException(Messages.FieldTooBigOffsetToCD); } if (zip64EndOfCentralDirectoryRecord.NumberOfEntriesTotal != zip64EndOfCentralDirectoryRecord.NumberOfEntriesOnThisDisk) { throw new InvalidDataException(Messages.SplitSpanned); } this._expectedNumberOfEntries = (long)zip64EndOfCentralDirectoryRecord.NumberOfEntriesTotal; this._centralDirectoryStart = (long)zip64EndOfCentralDirectoryRecord.OffsetOfCentralDirectory; } } if (this._centralDirectoryStart > this._archiveStream.Length) { throw new InvalidDataException(Messages.FieldTooBigOffsetToCD); } } catch (EndOfStreamException endOfStreamException) { throw new InvalidDataException(Messages.CDCorrupt, endOfStreamException); } catch (IOException oException) { throw new InvalidDataException(Messages.CDCorrupt, oException); } }