Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Base / MS / Internal / IO / Zip / ZipIOEndOfCentralDirectoryBlock.cs / 1 / ZipIOEndOfCentralDirectoryBlock.cs
//------------------------------------------------------------------------------ //------------- *** WARNING *** //------------- This file is part of a legally monitored development project. //------------- Do not check in changes to this project. Do not raid bugs on this //------------- code in the main PS database. Do not contact the owner of this //------------- code directly. Contact the legal team at ‘ZSLegal’ for assistance. //------------- *** WARNING *** //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: // This is an internal class that enables interactions with Zip archives // for OPC scenarios // // History: // 11/19/2004: IgorBel: Initial creation. // //----------------------------------------------------------------------------- using System; using System.IO; using System.Diagnostics; using System.Runtime.Serialization; using System.Windows; using MS.Internal.IO.Packaging; // for PackagingUtilities namespace MS.Internal.IO.Zip { internal class ZipIOEndOfCentralDirectoryBlock : IZipIOBlock { //------------------------------------------------------ // // Public Properties // //----------------------------------------------------- // standard IZipIOBlock functionality public long Offset { get { return _offset; } } public long Size { get { return _fixedMinimalRecordSize + _zipFileCommentLength; } } public bool GetDirtyFlag(bool closingFlag) { return _dirtyFlag; } //------------------------------------------------------ // // Public Methods // //------------------------------------------------------ public void Move(long shiftSize) { if (shiftSize != 0) { checked{_offset +=shiftSize;} _dirtyFlag = true; Debug.Assert(_offset >=0); } } public void Save() { if (GetDirtyFlag(true)) { BinaryWriter writer = _blockManager.BinaryWriter; // never seek in streaming mode if (!_blockManager.Streaming && _blockManager.Stream.Position != _offset) { // we need to seek _blockManager.Stream.Seek(_offset, SeekOrigin.Begin); } writer.Write(_signatureConstant); writer.Write(_numberOfThisDisk); writer.Write(_numberOfTheDiskWithTheStartOfTheCentralDirectory); writer.Write(_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk); writer.Write(_totalNumberOfEntriesInTheCentralDirectory); writer.Write(_sizeOfTheCentralDirectory); writer.Write(_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber); writer.Write(_zipFileCommentLength); if (_zipFileCommentLength > 0) { writer.Write(_zipFileComment, 0, _zipFileCommentLength); } writer.Flush(); _dirtyFlag = false; } } public void UpdateReferences(bool closingFlag) { // check whether Central directory is loaded and update references accordingly // if one or more of the following conditions are true // 1. Central Directory is dirty // 2. Zip64 End of Central Directory is dirty // 3. Zip64 End of Central Directory Locator is dirty // 4. streaming mode // if Central Directory isn't loded or none of the relevant structure is dirty, // there is nothing to update for End Of Central directory record if (_blockManager.IsCentralDirectoryBlockLoaded && (_blockManager.Streaming || _blockManager.CentralDirectoryBlock.GetDirtyFlag(closingFlag) || _blockManager.Zip64EndOfCentralDirectoryBlock.GetDirtyFlag(closingFlag) || _blockManager.Zip64EndOfCentralDirectoryLocatorBlock.GetDirtyFlag(closingFlag))) { // intialize them to zIP64 case, and update them if needed UInt16 centralDirCount = UInt16.MaxValue; UInt32 centralDirBlockSize = UInt32.MaxValue; UInt32 centralDirOffset = UInt32.MaxValue; UInt16 numberOfTheDiskWithTheStartOfTheCentralDirectory = 0; UInt16 numberOfThisDisk = 0; // If we don't need Zip 64 struture if (!_blockManager.CentralDirectoryBlock.IsZip64BitRequiredForStoring) { // if it isn't zip 64 let's get the data out centralDirCount = (UInt16)_blockManager.CentralDirectoryBlock.Count; centralDirBlockSize = (UInt32)_blockManager.CentralDirectoryBlock.Size; centralDirOffset = (UInt32)_blockManager.CentralDirectoryBlock.Offset; } // update value and mark record dirty if either it is already dirty or there is a mismatch if ((_dirtyFlag) || (_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk != centralDirCount) || (_totalNumberOfEntriesInTheCentralDirectory != centralDirCount ) || (_sizeOfTheCentralDirectory != centralDirBlockSize) || (_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber != centralDirOffset) || (_numberOfTheDiskWithTheStartOfTheCentralDirectory != numberOfTheDiskWithTheStartOfTheCentralDirectory) || (_numberOfThisDisk != numberOfThisDisk)) { _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk = centralDirCount; _totalNumberOfEntriesInTheCentralDirectory = centralDirCount; _sizeOfTheCentralDirectory = centralDirBlockSize; _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = centralDirOffset; _numberOfTheDiskWithTheStartOfTheCentralDirectory = numberOfTheDiskWithTheStartOfTheCentralDirectory; _numberOfThisDisk = numberOfThisDisk; _dirtyFlag = true; } } } public PreSaveNotificationScanControlInstruction PreSaveNotification(long offset, long size) { // we can safely ignore this notification as we do not keep any data // after parsing on disk. Everything is in memory, it is ok to override // original End of Central directory without any additional backups // we can also safely state that there is no need to continue the PreSafeNotification loop // as there shouldn't be any blocks after the EOCD return PreSaveNotificationScanControlInstruction.Stop; } //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ internal static ZipIOEndOfCentralDirectoryBlock SeekableLoad (ZipIOBlockManager blockManager) { // perform custom serach for record long blockPosition = FindPosition(blockManager.Stream); blockManager.Stream.Seek(blockPosition, SeekOrigin.Begin); ZipIOEndOfCentralDirectoryBlock block = new ZipIOEndOfCentralDirectoryBlock(blockManager); block.ParseRecord(blockManager.BinaryReader, blockPosition); return block; } internal static ZipIOEndOfCentralDirectoryBlock CreateNew(ZipIOBlockManager blockManager, long offset) { ZipIOEndOfCentralDirectoryBlock block = new ZipIOEndOfCentralDirectoryBlock(blockManager); block._offset = offset; block._dirtyFlag = true; return block; } internal void ValidateZip64TriggerValues() { if ((_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber > _offset) || ((_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber == _offset) && (_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk > 0))) { // central directory must start prior to the offset of the end of central directory. // the only exception is when size of the central directory is 0 throw new FileFormatException(SR.Get(SRID.CorruptedData)); } if ((_numberOfThisDisk != 0) || (_numberOfTheDiskWithTheStartOfTheCentralDirectory != 0) || (_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk != _totalNumberOfEntriesInTheCentralDirectory)) { throw new NotSupportedException(SR.Get(SRID.NotSupportedMultiDisk)); } } internal uint NumberOfThisDisk { get { return _numberOfThisDisk; } } internal uint NumberOfTheDiskWithTheStartOfTheCentralDirectory { get { return _numberOfTheDiskWithTheStartOfTheCentralDirectory; } } internal uint TotalNumberOfEntriesInTheCentralDirectoryOnThisDisk { get { return _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk ; } } internal uint TotalNumberOfEntriesInTheCentralDirectory { get { return _totalNumberOfEntriesInTheCentralDirectory; } } internal uint SizeOfTheCentralDirectory { get { return _sizeOfTheCentralDirectory; } } internal uint OffsetOfStartOfCentralDirectory { get { return _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber; } } #if false internal string Comment { get { return _stringZipFileComment; } } #endif internal bool ContainValuesHintingToPossibilityOfZip64 { get { return ((_numberOfThisDisk == UInt16.MaxValue) || (_numberOfTheDiskWithTheStartOfTheCentralDirectory == UInt16.MaxValue) || (_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk == UInt16.MaxValue) || (_totalNumberOfEntriesInTheCentralDirectory == UInt16.MaxValue) || (_sizeOfTheCentralDirectory == UInt32.MaxValue) || (_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber == UInt32.MaxValue)); } } //----------------------------------------------------- // // Private Methods // //----------------------------------------------------- private ZipIOEndOfCentralDirectoryBlock(ZipIOBlockManager blockManager) { Debug.Assert(blockManager != null); _blockManager= blockManager; } private static long FindPosition(Stream archiveStream) { Debug.Assert(archiveStream.CanSeek); byte [] buffer = new byte[_scanBlockSize + _fixedMinimalRecordSize]; long streamLength = archiveStream.Length; for(long endPos = streamLength; endPos > 0; endPos -= _scanBlockSize) { // calculate offset position of the block to be read based on the end // Position loop variable long beginPos = Math.Max(0, endPos -_scanBlockSize); //read the block archiveStream.Seek(beginPos, SeekOrigin.Begin); // the reads that we do actually overlap each other by the size == _fixedMinimalRecordSize // this is done in order to simplify our searching logic, this way we do not need to specially // process matches that cross buffer boundaries, as we are guaranteed that if match is present // it falls completely inside one of the buffers, as a result of overlapping in the read requests int bytesRead = PackagingUtilities.ReliableRead(archiveStream, buffer, 0, buffer.Length); // We need to pass this parameter into the function, so it knows // the relative positon of the buffer in regard to the end of the stream; // it needs this info in order to checke whether the candidate record // has length of Comment field consistent with the postion of the record long distanceFromStartOfBufferToTheEndOfStream = streamLength -beginPos; for(int i = bytesRead - _fixedMinimalRecordSize; i>=0; i--) { if (IsPositionMatched(i, buffer, distanceFromStartOfBufferToTheEndOfStream)) { return beginPos + i; } } } // At this point we have finished scanning the file and haven't find anything throw new FileFormatException(SR.Get(SRID.CorruptedData)); } private static bool IsPositionMatched (int pos, byte[] buffer, long bufferOffsetFromEndOfStream) { Debug.Assert(buffer != null); Debug.Assert(buffer.Length >= _fixedMinimalRecordSize); // the end of central directory record must fit in there Debug.Assert(pos <= buffer.Length - _fixedMinimalRecordSize); // enough space to fit the record after pos Debug.Assert(bufferOffsetFromEndOfStream >= _fixedMinimalRecordSize); // there is no reason to start searching for the record // after less than 22 byrtes left till the end of stream for(int i = 0; i<_signatureBuffer.Length; i++) { if (_signatureBuffer[i] != buffer[pos+i]) { //signature mismatch return false; } } //we got signature matching, let's see if we can get comment length to match // to handle little endian order of the bytes in the 16 bit length long commentLengthFromRecord = buffer[pos + _fixedMinimalRecordSize-2] + (buffer[pos + _fixedMinimalRecordSize-1] << 8); long commentLengthFromPos = bufferOffsetFromEndOfStream - pos - _fixedMinimalRecordSize; if (commentLengthFromPos != commentLengthFromRecord) { return false; } return true; } private void ParseRecord (BinaryReader reader, long position) { _signature = reader.ReadUInt32(); _numberOfThisDisk = reader.ReadUInt16(); _numberOfTheDiskWithTheStartOfTheCentralDirectory = reader.ReadUInt16(); _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk = reader.ReadUInt16(); _totalNumberOfEntriesInTheCentralDirectory = reader.ReadUInt16(); _sizeOfTheCentralDirectory = reader.ReadUInt32(); _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = reader.ReadUInt32(); _zipFileCommentLength = reader.ReadUInt16(); _zipFileComment = reader.ReadBytes(_zipFileCommentLength); _stringZipFileComment = _blockManager.Encoding.GetString(_zipFileComment); _offset = position; _dirtyFlag = false; Validate(); } // Do minimum validatation here // The rest of validation on the fields that can indicate the possiblity of Zip64 will be validated later // If there is the zip64 End of Central Directory, thoses values will be valided // by ZipIO64EndOfCentralDirectoryBlock // Otherwise it will be validated in ZipIoBlockManager when it tries load ZipIO64EndOfCentralDirectoryBlock // In all of the supported scenarios we always try to load ZipIO64EndOfCentralDirectoryBlock immediately // after it loads ZipIOEndOfCentralDirectoryBlock; so there is not much difference in the timing of // the validation private void Validate() { if (_signature != _signatureConstant) { throw new FileFormatException(SR.Get(SRID.CorruptedData)); } if (_zipFileCommentLength != _zipFileComment.Length) { throw new FileFormatException(SR.Get(SRID.CorruptedData)); } } //----------------------------------------------------- // // Private Members // //------------------------------------------------------ // constant that is used for locating EndOf record signature private static byte [] _signatureBuffer = new byte[] {0x50, 0x4b, 0x05, 0x06}; // this blocks size is used to read data thro the tail of stream block by block private static int _scanBlockSize = 0x01000; private ZipIOBlockManager _blockManager; private long _offset; private bool _dirtyFlag; private const UInt32 _signatureConstant = 0x06054b50; private const int _fixedMinimalRecordSize = 22; // data persisted on disk private UInt32 _signature = _signatureConstant; private UInt16 _numberOfThisDisk; private UInt16 _numberOfTheDiskWithTheStartOfTheCentralDirectory; private UInt16 _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk; private UInt16 _totalNumberOfEntriesInTheCentralDirectory; private UInt32 _sizeOfTheCentralDirectory; private UInt32 _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber; private UInt16 _zipFileCommentLength; private byte[] _zipFileComment; private string _stringZipFileComment; } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ //------------- *** WARNING *** //------------- This file is part of a legally monitored development project. //------------- Do not check in changes to this project. Do not raid bugs on this //------------- code in the main PS database. Do not contact the owner of this //------------- code directly. Contact the legal team at ‘ZSLegal’ for assistance. //------------- *** WARNING *** //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: // This is an internal class that enables interactions with Zip archives // for OPC scenarios // // History: // 11/19/2004: IgorBel: Initial creation. // //----------------------------------------------------------------------------- using System; using System.IO; using System.Diagnostics; using System.Runtime.Serialization; using System.Windows; using MS.Internal.IO.Packaging; // for PackagingUtilities namespace MS.Internal.IO.Zip { internal class ZipIOEndOfCentralDirectoryBlock : IZipIOBlock { //------------------------------------------------------ // // Public Properties // //----------------------------------------------------- // standard IZipIOBlock functionality public long Offset { get { return _offset; } } public long Size { get { return _fixedMinimalRecordSize + _zipFileCommentLength; } } public bool GetDirtyFlag(bool closingFlag) { return _dirtyFlag; } //------------------------------------------------------ // // Public Methods // //------------------------------------------------------ public void Move(long shiftSize) { if (shiftSize != 0) { checked{_offset +=shiftSize;} _dirtyFlag = true; Debug.Assert(_offset >=0); } } public void Save() { if (GetDirtyFlag(true)) { BinaryWriter writer = _blockManager.BinaryWriter; // never seek in streaming mode if (!_blockManager.Streaming && _blockManager.Stream.Position != _offset) { // we need to seek _blockManager.Stream.Seek(_offset, SeekOrigin.Begin); } writer.Write(_signatureConstant); writer.Write(_numberOfThisDisk); writer.Write(_numberOfTheDiskWithTheStartOfTheCentralDirectory); writer.Write(_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk); writer.Write(_totalNumberOfEntriesInTheCentralDirectory); writer.Write(_sizeOfTheCentralDirectory); writer.Write(_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber); writer.Write(_zipFileCommentLength); if (_zipFileCommentLength > 0) { writer.Write(_zipFileComment, 0, _zipFileCommentLength); } writer.Flush(); _dirtyFlag = false; } } public void UpdateReferences(bool closingFlag) { // check whether Central directory is loaded and update references accordingly // if one or more of the following conditions are true // 1. Central Directory is dirty // 2. Zip64 End of Central Directory is dirty // 3. Zip64 End of Central Directory Locator is dirty // 4. streaming mode // if Central Directory isn't loded or none of the relevant structure is dirty, // there is nothing to update for End Of Central directory record if (_blockManager.IsCentralDirectoryBlockLoaded && (_blockManager.Streaming || _blockManager.CentralDirectoryBlock.GetDirtyFlag(closingFlag) || _blockManager.Zip64EndOfCentralDirectoryBlock.GetDirtyFlag(closingFlag) || _blockManager.Zip64EndOfCentralDirectoryLocatorBlock.GetDirtyFlag(closingFlag))) { // intialize them to zIP64 case, and update them if needed UInt16 centralDirCount = UInt16.MaxValue; UInt32 centralDirBlockSize = UInt32.MaxValue; UInt32 centralDirOffset = UInt32.MaxValue; UInt16 numberOfTheDiskWithTheStartOfTheCentralDirectory = 0; UInt16 numberOfThisDisk = 0; // If we don't need Zip 64 struture if (!_blockManager.CentralDirectoryBlock.IsZip64BitRequiredForStoring) { // if it isn't zip 64 let's get the data out centralDirCount = (UInt16)_blockManager.CentralDirectoryBlock.Count; centralDirBlockSize = (UInt32)_blockManager.CentralDirectoryBlock.Size; centralDirOffset = (UInt32)_blockManager.CentralDirectoryBlock.Offset; } // update value and mark record dirty if either it is already dirty or there is a mismatch if ((_dirtyFlag) || (_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk != centralDirCount) || (_totalNumberOfEntriesInTheCentralDirectory != centralDirCount ) || (_sizeOfTheCentralDirectory != centralDirBlockSize) || (_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber != centralDirOffset) || (_numberOfTheDiskWithTheStartOfTheCentralDirectory != numberOfTheDiskWithTheStartOfTheCentralDirectory) || (_numberOfThisDisk != numberOfThisDisk)) { _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk = centralDirCount; _totalNumberOfEntriesInTheCentralDirectory = centralDirCount; _sizeOfTheCentralDirectory = centralDirBlockSize; _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = centralDirOffset; _numberOfTheDiskWithTheStartOfTheCentralDirectory = numberOfTheDiskWithTheStartOfTheCentralDirectory; _numberOfThisDisk = numberOfThisDisk; _dirtyFlag = true; } } } public PreSaveNotificationScanControlInstruction PreSaveNotification(long offset, long size) { // we can safely ignore this notification as we do not keep any data // after parsing on disk. Everything is in memory, it is ok to override // original End of Central directory without any additional backups // we can also safely state that there is no need to continue the PreSafeNotification loop // as there shouldn't be any blocks after the EOCD return PreSaveNotificationScanControlInstruction.Stop; } //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ internal static ZipIOEndOfCentralDirectoryBlock SeekableLoad (ZipIOBlockManager blockManager) { // perform custom serach for record long blockPosition = FindPosition(blockManager.Stream); blockManager.Stream.Seek(blockPosition, SeekOrigin.Begin); ZipIOEndOfCentralDirectoryBlock block = new ZipIOEndOfCentralDirectoryBlock(blockManager); block.ParseRecord(blockManager.BinaryReader, blockPosition); return block; } internal static ZipIOEndOfCentralDirectoryBlock CreateNew(ZipIOBlockManager blockManager, long offset) { ZipIOEndOfCentralDirectoryBlock block = new ZipIOEndOfCentralDirectoryBlock(blockManager); block._offset = offset; block._dirtyFlag = true; return block; } internal void ValidateZip64TriggerValues() { if ((_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber > _offset) || ((_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber == _offset) && (_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk > 0))) { // central directory must start prior to the offset of the end of central directory. // the only exception is when size of the central directory is 0 throw new FileFormatException(SR.Get(SRID.CorruptedData)); } if ((_numberOfThisDisk != 0) || (_numberOfTheDiskWithTheStartOfTheCentralDirectory != 0) || (_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk != _totalNumberOfEntriesInTheCentralDirectory)) { throw new NotSupportedException(SR.Get(SRID.NotSupportedMultiDisk)); } } internal uint NumberOfThisDisk { get { return _numberOfThisDisk; } } internal uint NumberOfTheDiskWithTheStartOfTheCentralDirectory { get { return _numberOfTheDiskWithTheStartOfTheCentralDirectory; } } internal uint TotalNumberOfEntriesInTheCentralDirectoryOnThisDisk { get { return _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk ; } } internal uint TotalNumberOfEntriesInTheCentralDirectory { get { return _totalNumberOfEntriesInTheCentralDirectory; } } internal uint SizeOfTheCentralDirectory { get { return _sizeOfTheCentralDirectory; } } internal uint OffsetOfStartOfCentralDirectory { get { return _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber; } } #if false internal string Comment { get { return _stringZipFileComment; } } #endif internal bool ContainValuesHintingToPossibilityOfZip64 { get { return ((_numberOfThisDisk == UInt16.MaxValue) || (_numberOfTheDiskWithTheStartOfTheCentralDirectory == UInt16.MaxValue) || (_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk == UInt16.MaxValue) || (_totalNumberOfEntriesInTheCentralDirectory == UInt16.MaxValue) || (_sizeOfTheCentralDirectory == UInt32.MaxValue) || (_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber == UInt32.MaxValue)); } } //----------------------------------------------------- // // Private Methods // //----------------------------------------------------- private ZipIOEndOfCentralDirectoryBlock(ZipIOBlockManager blockManager) { Debug.Assert(blockManager != null); _blockManager= blockManager; } private static long FindPosition(Stream archiveStream) { Debug.Assert(archiveStream.CanSeek); byte [] buffer = new byte[_scanBlockSize + _fixedMinimalRecordSize]; long streamLength = archiveStream.Length; for(long endPos = streamLength; endPos > 0; endPos -= _scanBlockSize) { // calculate offset position of the block to be read based on the end // Position loop variable long beginPos = Math.Max(0, endPos -_scanBlockSize); //read the block archiveStream.Seek(beginPos, SeekOrigin.Begin); // the reads that we do actually overlap each other by the size == _fixedMinimalRecordSize // this is done in order to simplify our searching logic, this way we do not need to specially // process matches that cross buffer boundaries, as we are guaranteed that if match is present // it falls completely inside one of the buffers, as a result of overlapping in the read requests int bytesRead = PackagingUtilities.ReliableRead(archiveStream, buffer, 0, buffer.Length); // We need to pass this parameter into the function, so it knows // the relative positon of the buffer in regard to the end of the stream; // it needs this info in order to checke whether the candidate record // has length of Comment field consistent with the postion of the record long distanceFromStartOfBufferToTheEndOfStream = streamLength -beginPos; for(int i = bytesRead - _fixedMinimalRecordSize; i>=0; i--) { if (IsPositionMatched(i, buffer, distanceFromStartOfBufferToTheEndOfStream)) { return beginPos + i; } } } // At this point we have finished scanning the file and haven't find anything throw new FileFormatException(SR.Get(SRID.CorruptedData)); } private static bool IsPositionMatched (int pos, byte[] buffer, long bufferOffsetFromEndOfStream) { Debug.Assert(buffer != null); Debug.Assert(buffer.Length >= _fixedMinimalRecordSize); // the end of central directory record must fit in there Debug.Assert(pos <= buffer.Length - _fixedMinimalRecordSize); // enough space to fit the record after pos Debug.Assert(bufferOffsetFromEndOfStream >= _fixedMinimalRecordSize); // there is no reason to start searching for the record // after less than 22 byrtes left till the end of stream for(int i = 0; i<_signatureBuffer.Length; i++) { if (_signatureBuffer[i] != buffer[pos+i]) { //signature mismatch return false; } } //we got signature matching, let's see if we can get comment length to match // to handle little endian order of the bytes in the 16 bit length long commentLengthFromRecord = buffer[pos + _fixedMinimalRecordSize-2] + (buffer[pos + _fixedMinimalRecordSize-1] << 8); long commentLengthFromPos = bufferOffsetFromEndOfStream - pos - _fixedMinimalRecordSize; if (commentLengthFromPos != commentLengthFromRecord) { return false; } return true; } private void ParseRecord (BinaryReader reader, long position) { _signature = reader.ReadUInt32(); _numberOfThisDisk = reader.ReadUInt16(); _numberOfTheDiskWithTheStartOfTheCentralDirectory = reader.ReadUInt16(); _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk = reader.ReadUInt16(); _totalNumberOfEntriesInTheCentralDirectory = reader.ReadUInt16(); _sizeOfTheCentralDirectory = reader.ReadUInt32(); _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = reader.ReadUInt32(); _zipFileCommentLength = reader.ReadUInt16(); _zipFileComment = reader.ReadBytes(_zipFileCommentLength); _stringZipFileComment = _blockManager.Encoding.GetString(_zipFileComment); _offset = position; _dirtyFlag = false; Validate(); } // Do minimum validatation here // The rest of validation on the fields that can indicate the possiblity of Zip64 will be validated later // If there is the zip64 End of Central Directory, thoses values will be valided // by ZipIO64EndOfCentralDirectoryBlock // Otherwise it will be validated in ZipIoBlockManager when it tries load ZipIO64EndOfCentralDirectoryBlock // In all of the supported scenarios we always try to load ZipIO64EndOfCentralDirectoryBlock immediately // after it loads ZipIOEndOfCentralDirectoryBlock; so there is not much difference in the timing of // the validation private void Validate() { if (_signature != _signatureConstant) { throw new FileFormatException(SR.Get(SRID.CorruptedData)); } if (_zipFileCommentLength != _zipFileComment.Length) { throw new FileFormatException(SR.Get(SRID.CorruptedData)); } } //----------------------------------------------------- // // Private Members // //------------------------------------------------------ // constant that is used for locating EndOf record signature private static byte [] _signatureBuffer = new byte[] {0x50, 0x4b, 0x05, 0x06}; // this blocks size is used to read data thro the tail of stream block by block private static int _scanBlockSize = 0x01000; private ZipIOBlockManager _blockManager; private long _offset; private bool _dirtyFlag; private const UInt32 _signatureConstant = 0x06054b50; private const int _fixedMinimalRecordSize = 22; // data persisted on disk private UInt32 _signature = _signatureConstant; private UInt16 _numberOfThisDisk; private UInt16 _numberOfTheDiskWithTheStartOfTheCentralDirectory; private UInt16 _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk; private UInt16 _totalNumberOfEntriesInTheCentralDirectory; private UInt32 _sizeOfTheCentralDirectory; private UInt32 _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber; private UInt16 _zipFileCommentLength; private byte[] _zipFileComment; private string _stringZipFileComment; } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DetailsViewAutoFormat.cs
- DataProtection.cs
- ObjectAnimationUsingKeyFrames.cs
- DesignTimeTemplateParser.cs
- WebEventCodes.cs
- DbConnectionOptions.cs
- BaseAsyncResult.cs
- XPathExpr.cs
- ByeOperationAsyncResult.cs
- TextServicesDisplayAttributePropertyRanges.cs
- DescriptionAttribute.cs
- ServicesUtilities.cs
- ISFClipboardData.cs
- ProjectionCamera.cs
- XmlSchemaDatatype.cs
- WebBrowserContainer.cs
- JsonServiceDocumentSerializer.cs
- WmfPlaceableFileHeader.cs
- querybuilder.cs
- BackgroundFormatInfo.cs
- ConnectionManagementElement.cs
- Button.cs
- DragDropHelper.cs
- DrawingContextWalker.cs
- UnsafeNativeMethodsTablet.cs
- PopupEventArgs.cs
- SectionRecord.cs
- XmlObjectSerializerReadContextComplexJson.cs
- LicFileLicenseProvider.cs
- WebPartChrome.cs
- GridView.cs
- ValidationResult.cs
- SafeNativeMethodsMilCoreApi.cs
- HostedNamedPipeTransportManager.cs
- FlowDocumentReader.cs
- ListenerElementsCollection.cs
- ObjectListCommandCollection.cs
- Vector.cs
- DateTimeConstantAttribute.cs
- QuaternionValueSerializer.cs
- DataGridViewTextBoxColumn.cs
- SoapSchemaExporter.cs
- Globals.cs
- localization.cs
- HttpHandlersSection.cs
- HttpsHostedTransportConfiguration.cs
- InteropEnvironment.cs
- FileNotFoundException.cs
- Application.cs
- ToolboxItemFilterAttribute.cs
- control.ime.cs
- CopyOnWriteList.cs
- SHA1.cs
- UshortList2.cs
- ButtonAutomationPeer.cs
- SponsorHelper.cs
- KeyTime.cs
- MenuBase.cs
- EntityPropertyMappingAttribute.cs
- ListViewItemEventArgs.cs
- RecognizerInfo.cs
- TextReader.cs
- SplineKeyFrames.cs
- MultiBindingExpression.cs
- ComboBoxRenderer.cs
- CodeTypeConstructor.cs
- QilFunction.cs
- ExeConfigurationFileMap.cs
- PageTheme.cs
- BindingCompleteEventArgs.cs
- FullTrustAssembliesSection.cs
- DataGridColumn.cs
- EntityCollection.cs
- CodeAttributeArgument.cs
- CompositionDesigner.cs
- AppDomainManager.cs
- WeakReferenceKey.cs
- formatter.cs
- WorkflowApplicationUnloadedException.cs
- COM2IDispatchConverter.cs
- CatalogZoneBase.cs
- Pointer.cs
- ConfigXmlCDataSection.cs
- DiscoveryReferences.cs
- _ServiceNameStore.cs
- CustomAssemblyResolver.cs
- DataGridViewCellPaintingEventArgs.cs
- DateTimeFormatInfoScanner.cs
- SqlConnectionHelper.cs
- Set.cs
- InputGestureCollection.cs
- Int64.cs
- TokenBasedSetEnumerator.cs
- IPEndPoint.cs
- SelectedDatesCollection.cs
- PipelineComponent.cs
- BufferModeSettings.cs
- XMLUtil.cs
- Popup.cs
- DataBoundControlAdapter.cs