ZipIOEndOfCentralDirectoryBlock.cs source code in C# .NET

Source code for the .NET framework in C#

                        

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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK