ZipIOZip64EndOfCentralDirectoryBlock.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Base / MS / Internal / IO / Zip / ZipIOZip64EndOfCentralDirectoryBlock.cs / 1 / ZipIOZip64EndOfCentralDirectoryBlock.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 (Zip 64 bit support)
// 
// History:
//  01/26/2005: IgorBel: Initial creation.
//
//----------------------------------------------------------------------------- 

using System; 
using System.IO; 
using System.Diagnostics;
using System.Runtime.Serialization; 
using System.Windows;

namespace MS.Internal.IO.Zip
{ 
    internal class ZipIOZip64EndOfCentralDirectoryBlock : IZipIOBlock
    { 
        // standard IZipIOBlock functionality 
        public long Offset
        { 
            get
            {
                return _offset;
            } 
        }
 
        public long Size 
        {
            get 
            {
                return _size;
            }
        } 

        // This property will only return reliable result if UpdateReferences is called prior 
        public bool GetDirtyFlag(bool closingFlag) 
        {
                return _dirtyFlag; 
        }

        public void Move(long shiftSize)
        { 
            if (shiftSize != 0)
            { 
                checked{_offset +=shiftSize;} 

                if (_size > 0) 
                {
                    _dirtyFlag = true;
                }
 
                Debug.Assert(_offset >=0);
            } 
        } 

        public void Save() 
        {
            // this record is optional and shouldn't be saved if size is 0
            if (GetDirtyFlag(true) && (Size > 0))
            { 
                BinaryWriter writer = _blockManager.BinaryWriter;
                if (_blockManager.Stream.Position != _offset) 
                { 
                    // we need to seek , as current position isn't accurate
                    _blockManager.Stream.Seek(_offset, SeekOrigin.Begin); 
                }

                writer.Write(_signatureConstant);
                writer.Write(_sizeOfZip64EndOfCentralDirectory); 
                writer.Write(_versionMadeBy);
                writer.Write(_versionNeededToExtract); 
                writer.Write(_numberOfThisDisk); 
                writer.Write(_numberOfTheDiskWithTheStartOfTheCentralDirectory);
                writer.Write(_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk); 
                writer.Write(_totalNumberOfEntriesInTheCentralDirectory);
                writer.Write(_sizeOfTheCentralDirectory);
                writer.Write(_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber);
 
                if (_sizeOfZip64EndOfCentralDirectory > _fixedMinimalValueOfSizeOfZip64EOCD)
                { 
                    Debug.Assert(_zip64ExtensibleDataSector != null); 
                    Debug.Assert(_zip64ExtensibleDataSector.Length ==
                                    checked((int)(_sizeOfZip64EndOfCentralDirectory -_fixedMinimalValueOfSizeOfZip64EOCD))); 

                    writer.Write(_zip64ExtensibleDataSector,
                                    0,
                                    checked((int)(_sizeOfZip64EndOfCentralDirectory -_fixedMinimalValueOfSizeOfZip64EOCD))); 
                }
 
                writer.Flush(); 
            }
 
             _dirtyFlag = false;
        }

        public void UpdateReferences(bool closingFlag) 
        {
            checked 
            { 
                // 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. streaming mode
                // if Central Directory isn't loaded or none of the relevant structure is dirty,
                //  there is nothing to update for Zip64 End Of Central directory record 
                if (_blockManager.IsCentralDirectoryBlockLoaded
                        && (_blockManager.Streaming 
                            || _blockManager.CentralDirectoryBlock.GetDirtyFlag(closingFlag))) 
                {
                    if (_blockManager.CentralDirectoryBlock.IsZip64BitRequiredForStoring) 
                    {
                        UInt64 centralDirCount = (UInt64)_blockManager.CentralDirectoryBlock.Count;
                        UInt64 centralDirBlockSize = (UInt64)_blockManager.CentralDirectoryBlock.Size;
                        UInt64 centralDirOffset = (UInt64)_blockManager.CentralDirectoryBlock.Offset; 

            // Here is a diagram of the record 
            //---------------------------------------------------------------------------------------------------------------------- 
            //|SignatureConst (4 bytes)|sizeOfZip64Eocd (8 bytes)|misc fixed fields (44 bytes)|Variable Size Extensible Data sector|
            //A------------------------B-------------------------C----------------------------D------------------------------------E 
            //
            // in order to calculate the actual record size we subtract _fixedMinimalValueOfSizeOfZip64EOCD (This is a chunk marked
            // (C,D) in thre diagram above) from _fixedMinimalRecordSize (This is a chunk marked (A,D) in the diagram above).
            // Then we add the resulting value (which would be chunked marked (A,C) to the value of  sizeOfZip64Eocd field which 
            // contains the size of the record starting at point (C) and going to the end (E). So we get the total size as
            //   (A,C) + (C,E) = (A,E) 
            // 

                        long size =  checked((long)( 
                                                    // value that was either parsed from a file or initialized to the _fixedMinimalValueOfSizeOfZip64EOCD
                                        _sizeOfZip64EndOfCentralDirectory +
                                                    // const (value indicating minimal whole record size, how many bytes on disk it needs) 56
                                        _fixedMinimalRecordSize - 
                                                    // const (value indicating minimal value for the SizeOfZip64EOCD field as it is contains
                                                    // the whole size without record signature(4), and the itself (8) it is 56 - 12 = 44 
                                        _fixedMinimalValueOfSizeOfZip64EOCD)); 

                        // 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) ||
                            (_size != size)) 
                        { 
                            _versionMadeBy = (ushort)ZipIOVersionNeededToExtract.Zip64FileFormat;
                            _versionNeededToExtract = (ushort)ZipIOVersionNeededToExtract.Zip64FileFormat; 

                            _numberOfThisDisk = 0;
                            _numberOfTheDiskWithTheStartOfTheCentralDirectory  = 0;
 
                            _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk = centralDirCount;
                            _totalNumberOfEntriesInTheCentralDirectory = centralDirCount; 
                            _sizeOfTheCentralDirectory = centralDirBlockSize; 
                            _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = centralDirOffset;
 
                            _size = size;

                            _dirtyFlag = true;
                        } 
                    }
                    else 
                    { 
                        // we do not need zip 64 structures
                        if (_size != 0) 
                        {
                            _dirtyFlag = true;
                            _size = 0;
                        } 
                    }
                } 
            } 
        }
 
        public PreSaveNotificationScanControlInstruction PreSaveNotification(long offset, long size)
        {
            // we can safely ignore this notification as we do not keep any data on disk
            // after parsing on disk. Everything is in memory, it is ok to override 
            // original Zip64 EndOf Central Directory Block without any additional backups
 
            // we can also safely state that there is no need to continue the PreSafeNotification loop 
            // as the blocks after the Zip64 Eocd (EOCD, Zip64 locator ) do not have
            // data that is buffered on disk 
            return PreSaveNotificationScanControlInstruction.Stop;
        }

        internal static ZipIOZip64EndOfCentralDirectoryBlock SeekableLoad (ZipIOBlockManager blockManager) 
        {
            ZipIOZip64EndOfCentralDirectoryLocatorBlock zip64endOfCentralDirectoryLocator = 
                                                blockManager.Zip64EndOfCentralDirectoryLocatorBlock; 

            long zip64EndOfCentralDirectoryOffset = 
                                                zip64endOfCentralDirectoryLocator.OffsetOfZip64EndOfCentralDirectoryRecord;

            ZipIOZip64EndOfCentralDirectoryBlock block = new ZipIOZip64EndOfCentralDirectoryBlock(blockManager);
 
            blockManager.Stream.Seek(zip64EndOfCentralDirectoryOffset, SeekOrigin.Begin);
 
            block.ParseRecord(blockManager.BinaryReader, zip64EndOfCentralDirectoryOffset); 

            return block; 
        }

        internal static ZipIOZip64EndOfCentralDirectoryBlock CreateNew(ZipIOBlockManager blockManager)
        { 
            ZipIOZip64EndOfCentralDirectoryBlock block = new ZipIOZip64EndOfCentralDirectoryBlock(blockManager);
 
            block._size = 0; // brand new created records are optional by definition untill UpdateReferences is called, so size must be 0 
            block._offset = 0;
            block._dirtyFlag = false; 

            // initialize fields with ythe data from the EOCD
            block.InitializeFromEndOfCentralDirectory(blockManager.EndOfCentralDirectoryBlock);
 
            return block;
        } 
 
        internal long OffsetOfStartOfCentralDirectory
        { 
            get
            {
                return (long)_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber;
            } 
        }
 
        internal int TotalNumberOfEntriesInTheCentralDirectory 
        {
            get 
            {
                return (int)_totalNumberOfEntriesInTheCentralDirectory; // checked isn't required as we do validation during parsing
            }
        } 

        internal long SizeOfCentralDirectory 
        { 
            get
            { 
                return (long)_sizeOfTheCentralDirectory;
            }
        }
 
        private ZipIOZip64EndOfCentralDirectoryBlock(ZipIOBlockManager blockManager)
        { 
            Debug.Assert(blockManager != null); 
            _blockManager= blockManager;
        } 

        private void ParseRecord (BinaryReader reader, long position)
        {
            _signature = reader.ReadUInt32(); 
            _sizeOfZip64EndOfCentralDirectory = reader.ReadUInt64();
            _versionMadeBy = reader.ReadUInt16(); 
            _versionNeededToExtract = reader.ReadUInt16(); 
            _numberOfThisDisk = reader.ReadUInt32();
            _numberOfTheDiskWithTheStartOfTheCentralDirectory = reader.ReadUInt32(); 
            _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk = reader.ReadUInt64();
            _totalNumberOfEntriesInTheCentralDirectory = reader.ReadUInt64();
            _sizeOfTheCentralDirectory = reader.ReadUInt64();
            _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = reader.ReadUInt64(); 

                                                // pre validate before reading data based on parsed values 
            if ((_sizeOfZip64EndOfCentralDirectory < _fixedMinimalValueOfSizeOfZip64EOCD) || 
                                                // we are refusing to buffer large extended areas
                (_sizeOfZip64EndOfCentralDirectory > UInt16.MaxValue)) 
            {
                throw new FileFormatException(SR.Get(SRID.CorruptedData));
            }
 
            if (_sizeOfZip64EndOfCentralDirectory > _fixedMinimalValueOfSizeOfZip64EOCD)
            { 
                _zip64ExtensibleDataSector = reader.ReadBytes((int)(_sizeOfZip64EndOfCentralDirectory -_fixedMinimalValueOfSizeOfZip64EOCD)); 
            }
 
            // override some numbers bvased on the EOCD data according to the  apnote
            // even in presence of Zip64Eocd we still need to use the regular EOCD data
            OverrideValuesBasedOnEndOfCentralDirectory(_blockManager.EndOfCentralDirectoryBlock);
 
            _size =  checked((long)(    // value that was either parsed from a file or initialized to the _fixedMinimalValueOfSizeOfZip64EOCD
                                        _sizeOfZip64EndOfCentralDirectory + 
                                                    // const (value indicating minimal whole record size, how many bytes on disk it needs) 56 
                                        _fixedMinimalRecordSize -
                                                    // const (value indicating minimal value for the SizeOfZip64EOCD field as it is contains 
                                                    // the whole size without record signature(4), and the itself (8) it is 56 - 12 = 44
                                        _fixedMinimalValueOfSizeOfZip64EOCD));
            Debug.Assert(_size >= _fixedMinimalRecordSize);
 
            _offset = position;
            _dirtyFlag = false; 
 
            Validate();
        } 

        /// 
        /// This function is called from the Create New routine. The purpose of this exercise , is to copy data from 32 bit EOCD into this record,
        /// for scenarios when ZIP64 EOCD wasn't parsed from a file, but was just made up. 
        /// This is done so that Central Dir parsing code can ask the ZIP64 EOCD for this data, and regardless of whether it is real zip 64 file or
        /// not a zip 64 file it will get the right CD offset , size and so on 
        ///  
        private void InitializeFromEndOfCentralDirectory(ZipIOEndOfCentralDirectoryBlock zipIoEocd)
        { 
            _numberOfThisDisk = zipIoEocd.NumberOfThisDisk;
            _numberOfTheDiskWithTheStartOfTheCentralDirectory = zipIoEocd.NumberOfTheDiskWithTheStartOfTheCentralDirectory;
            _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk  = zipIoEocd.TotalNumberOfEntriesInTheCentralDirectoryOnThisDisk;
            _totalNumberOfEntriesInTheCentralDirectory = zipIoEocd.TotalNumberOfEntriesInTheCentralDirectory; 
            _sizeOfTheCentralDirectory = zipIoEocd.SizeOfTheCentralDirectory;
            _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = zipIoEocd.OffsetOfStartOfCentralDirectory; 
        } 

        ///  
        /// This function is called from the Parse routine. The purpose of this exercise , is to figure out the escape
        /// values in the regular 32 bit EOCD. We shouldn't be using values from the 64 bit structure if it wasn't
        /// escaped in the 32 bit structure.
        ///  
        private void OverrideValuesBasedOnEndOfCentralDirectory(ZipIOEndOfCentralDirectoryBlock zipIoEocd)
        { 
            // 16 bit numbers 
            if (zipIoEocd.NumberOfThisDisk < UInt16.MaxValue)
                {_numberOfThisDisk = zipIoEocd.NumberOfThisDisk;} 

            if (zipIoEocd.NumberOfTheDiskWithTheStartOfTheCentralDirectory < UInt16.MaxValue)
                {_numberOfTheDiskWithTheStartOfTheCentralDirectory = zipIoEocd.NumberOfTheDiskWithTheStartOfTheCentralDirectory;}
 
            if (zipIoEocd.TotalNumberOfEntriesInTheCentralDirectoryOnThisDisk  < UInt16.MaxValue)
                {_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk  = zipIoEocd.TotalNumberOfEntriesInTheCentralDirectoryOnThisDisk;} 
 
            if (zipIoEocd.TotalNumberOfEntriesInTheCentralDirectory < UInt16.MaxValue)
                {_totalNumberOfEntriesInTheCentralDirectory = zipIoEocd.TotalNumberOfEntriesInTheCentralDirectory;} 

            // 32  bit numbers
            if (zipIoEocd.SizeOfTheCentralDirectory < UInt32.MaxValue)
                {_sizeOfTheCentralDirectory = zipIoEocd.SizeOfTheCentralDirectory;} 

            if (zipIoEocd.OffsetOfStartOfCentralDirectory < UInt32.MaxValue) 
                {_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = zipIoEocd.OffsetOfStartOfCentralDirectory;} 

        } 

        private void Validate()
        {
            if (_signature != _signatureConstant) 
            {
                throw new FileFormatException(SR.Get(SRID.CorruptedData)); 
            } 

            if ((_numberOfThisDisk != 0) || 
                (_numberOfTheDiskWithTheStartOfTheCentralDirectory != 0) ||
                (_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk !=
                                                    _totalNumberOfEntriesInTheCentralDirectory))
            { 
                throw new NotSupportedException(SR.Get(SRID.NotSupportedMultiDisk));
            } 
 
            // this will throw an unsupported version exception if we see a version that we do not support
            ZipArchive.VerifyVersionNeededToExtract(_versionNeededToExtract); 

            // if it is one of the supported version but it isn't a ZIP64, it is an indication of a corrupted file
            if (_versionNeededToExtract !=  (UInt16)ZipIOVersionNeededToExtract.Zip64FileFormat)
            { 
                // if version isn't equal to the 4.5 it is a corrupted file (as we)
                // as appnote explicitly states that 
                //            When using ZIP64 extensions, the corresponding value in the 
                //            Zip64 end of central directory record should also be set.
                //            This field currently supports only the value 45 to indicate 
                //            ZIP64 extensions are present.
                throw new FileFormatException(SR.Get(SRID.CorruptedData));
            }
 
            if ((_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk > Int32.MaxValue) ||
                (_totalNumberOfEntriesInTheCentralDirectory > Int32.MaxValue) || 
                (_sizeOfTheCentralDirectory > Int64.MaxValue) || 
                (_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber > Int64.MaxValue))
            { 
                // although we are trying to support 64 bit structures
                // we are limited by the CLR model for collections (down to 32 bit collection size for
                // _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk )
                // and streams (down to 63 bit size) for all the outher Uint64 fields 

                throw new NotSupportedException(SR.Get(SRID.Zip64StructuresTooLarge)); 
            } 

            ulong sizeOfZip64ExtensibleDataSector = 0; 
            if (_zip64ExtensibleDataSector != null)
            {
                sizeOfZip64ExtensibleDataSector = (ulong)_zip64ExtensibleDataSector.Length;
            } 

            // the subtraction below doesn't need to be checked as we have validation in the parse logic 
            //    if (_sizeOfZip64EndOfCentralDirectory < _fixedMinimalValueOfSizeOfZip64EOCD)   {   throw ..  } 
            if (_sizeOfZip64EndOfCentralDirectory - _fixedMinimalValueOfSizeOfZip64EOCD != sizeOfZip64ExtensibleDataSector)
            { 
                throw new FileFormatException(SR.Get(SRID.CorruptedData));
            }

            //calculated record size must be larger than the min value 
            // it could be 0 for newly created from scratch records, but we do not pass those records through validation
            // we only validate parsed data 
            if (_size < _fixedMinimalRecordSize) 
            {
                throw new FileFormatException(SR.Get(SRID.CorruptedData)); 
            }
        }

 
        private ZipIOBlockManager _blockManager;
 
        private long _offset; 
        private long _size;
 
        private bool  _dirtyFlag;

        private  const UInt32 _signatureConstant  = 0x06064b50;
        private const uint _fixedMinimalRecordSize = 56; 
        private const uint _fixedMinimalValueOfSizeOfZip64EOCD = 44; // doesn't include the signature and the size itself
 
        // data persisted on disk 
        private UInt32 _signature = _signatureConstant;
 
        private UInt64 _sizeOfZip64EndOfCentralDirectory = _fixedMinimalValueOfSizeOfZip64EOCD;
        private UInt16 _versionMadeBy = (ushort)ZipIOVersionNeededToExtract.Zip64FileFormat;
        private UInt16 _versionNeededToExtract = (ushort)ZipIOVersionNeededToExtract.Zip64FileFormat;
        private UInt32 _numberOfThisDisk; 
        private UInt32 _numberOfTheDiskWithTheStartOfTheCentralDirectory;
        private UInt64 _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk;     // all int64s declared as signed values 
        private UInt64 _totalNumberOfEntriesInTheCentralDirectory;                       // as we can not suport true unsigned 64 bit sizes 
        private UInt64 _sizeOfTheCentralDirectory;                                                    // as a result of limitations in Stream interface
        private UInt64 _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber; 
        private byte[] _zip64ExtensibleDataSector;
    }
}

// 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 (Zip 64 bit support)
// 
// History:
//  01/26/2005: IgorBel: Initial creation.
//
//----------------------------------------------------------------------------- 

using System; 
using System.IO; 
using System.Diagnostics;
using System.Runtime.Serialization; 
using System.Windows;

namespace MS.Internal.IO.Zip
{ 
    internal class ZipIOZip64EndOfCentralDirectoryBlock : IZipIOBlock
    { 
        // standard IZipIOBlock functionality 
        public long Offset
        { 
            get
            {
                return _offset;
            } 
        }
 
        public long Size 
        {
            get 
            {
                return _size;
            }
        } 

        // This property will only return reliable result if UpdateReferences is called prior 
        public bool GetDirtyFlag(bool closingFlag) 
        {
                return _dirtyFlag; 
        }

        public void Move(long shiftSize)
        { 
            if (shiftSize != 0)
            { 
                checked{_offset +=shiftSize;} 

                if (_size > 0) 
                {
                    _dirtyFlag = true;
                }
 
                Debug.Assert(_offset >=0);
            } 
        } 

        public void Save() 
        {
            // this record is optional and shouldn't be saved if size is 0
            if (GetDirtyFlag(true) && (Size > 0))
            { 
                BinaryWriter writer = _blockManager.BinaryWriter;
                if (_blockManager.Stream.Position != _offset) 
                { 
                    // we need to seek , as current position isn't accurate
                    _blockManager.Stream.Seek(_offset, SeekOrigin.Begin); 
                }

                writer.Write(_signatureConstant);
                writer.Write(_sizeOfZip64EndOfCentralDirectory); 
                writer.Write(_versionMadeBy);
                writer.Write(_versionNeededToExtract); 
                writer.Write(_numberOfThisDisk); 
                writer.Write(_numberOfTheDiskWithTheStartOfTheCentralDirectory);
                writer.Write(_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk); 
                writer.Write(_totalNumberOfEntriesInTheCentralDirectory);
                writer.Write(_sizeOfTheCentralDirectory);
                writer.Write(_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber);
 
                if (_sizeOfZip64EndOfCentralDirectory > _fixedMinimalValueOfSizeOfZip64EOCD)
                { 
                    Debug.Assert(_zip64ExtensibleDataSector != null); 
                    Debug.Assert(_zip64ExtensibleDataSector.Length ==
                                    checked((int)(_sizeOfZip64EndOfCentralDirectory -_fixedMinimalValueOfSizeOfZip64EOCD))); 

                    writer.Write(_zip64ExtensibleDataSector,
                                    0,
                                    checked((int)(_sizeOfZip64EndOfCentralDirectory -_fixedMinimalValueOfSizeOfZip64EOCD))); 
                }
 
                writer.Flush(); 
            }
 
             _dirtyFlag = false;
        }

        public void UpdateReferences(bool closingFlag) 
        {
            checked 
            { 
                // 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. streaming mode
                // if Central Directory isn't loaded or none of the relevant structure is dirty,
                //  there is nothing to update for Zip64 End Of Central directory record 
                if (_blockManager.IsCentralDirectoryBlockLoaded
                        && (_blockManager.Streaming 
                            || _blockManager.CentralDirectoryBlock.GetDirtyFlag(closingFlag))) 
                {
                    if (_blockManager.CentralDirectoryBlock.IsZip64BitRequiredForStoring) 
                    {
                        UInt64 centralDirCount = (UInt64)_blockManager.CentralDirectoryBlock.Count;
                        UInt64 centralDirBlockSize = (UInt64)_blockManager.CentralDirectoryBlock.Size;
                        UInt64 centralDirOffset = (UInt64)_blockManager.CentralDirectoryBlock.Offset; 

            // Here is a diagram of the record 
            //---------------------------------------------------------------------------------------------------------------------- 
            //|SignatureConst (4 bytes)|sizeOfZip64Eocd (8 bytes)|misc fixed fields (44 bytes)|Variable Size Extensible Data sector|
            //A------------------------B-------------------------C----------------------------D------------------------------------E 
            //
            // in order to calculate the actual record size we subtract _fixedMinimalValueOfSizeOfZip64EOCD (This is a chunk marked
            // (C,D) in thre diagram above) from _fixedMinimalRecordSize (This is a chunk marked (A,D) in the diagram above).
            // Then we add the resulting value (which would be chunked marked (A,C) to the value of  sizeOfZip64Eocd field which 
            // contains the size of the record starting at point (C) and going to the end (E). So we get the total size as
            //   (A,C) + (C,E) = (A,E) 
            // 

                        long size =  checked((long)( 
                                                    // value that was either parsed from a file or initialized to the _fixedMinimalValueOfSizeOfZip64EOCD
                                        _sizeOfZip64EndOfCentralDirectory +
                                                    // const (value indicating minimal whole record size, how many bytes on disk it needs) 56
                                        _fixedMinimalRecordSize - 
                                                    // const (value indicating minimal value for the SizeOfZip64EOCD field as it is contains
                                                    // the whole size without record signature(4), and the itself (8) it is 56 - 12 = 44 
                                        _fixedMinimalValueOfSizeOfZip64EOCD)); 

                        // 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) ||
                            (_size != size)) 
                        { 
                            _versionMadeBy = (ushort)ZipIOVersionNeededToExtract.Zip64FileFormat;
                            _versionNeededToExtract = (ushort)ZipIOVersionNeededToExtract.Zip64FileFormat; 

                            _numberOfThisDisk = 0;
                            _numberOfTheDiskWithTheStartOfTheCentralDirectory  = 0;
 
                            _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk = centralDirCount;
                            _totalNumberOfEntriesInTheCentralDirectory = centralDirCount; 
                            _sizeOfTheCentralDirectory = centralDirBlockSize; 
                            _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = centralDirOffset;
 
                            _size = size;

                            _dirtyFlag = true;
                        } 
                    }
                    else 
                    { 
                        // we do not need zip 64 structures
                        if (_size != 0) 
                        {
                            _dirtyFlag = true;
                            _size = 0;
                        } 
                    }
                } 
            } 
        }
 
        public PreSaveNotificationScanControlInstruction PreSaveNotification(long offset, long size)
        {
            // we can safely ignore this notification as we do not keep any data on disk
            // after parsing on disk. Everything is in memory, it is ok to override 
            // original Zip64 EndOf Central Directory Block without any additional backups
 
            // we can also safely state that there is no need to continue the PreSafeNotification loop 
            // as the blocks after the Zip64 Eocd (EOCD, Zip64 locator ) do not have
            // data that is buffered on disk 
            return PreSaveNotificationScanControlInstruction.Stop;
        }

        internal static ZipIOZip64EndOfCentralDirectoryBlock SeekableLoad (ZipIOBlockManager blockManager) 
        {
            ZipIOZip64EndOfCentralDirectoryLocatorBlock zip64endOfCentralDirectoryLocator = 
                                                blockManager.Zip64EndOfCentralDirectoryLocatorBlock; 

            long zip64EndOfCentralDirectoryOffset = 
                                                zip64endOfCentralDirectoryLocator.OffsetOfZip64EndOfCentralDirectoryRecord;

            ZipIOZip64EndOfCentralDirectoryBlock block = new ZipIOZip64EndOfCentralDirectoryBlock(blockManager);
 
            blockManager.Stream.Seek(zip64EndOfCentralDirectoryOffset, SeekOrigin.Begin);
 
            block.ParseRecord(blockManager.BinaryReader, zip64EndOfCentralDirectoryOffset); 

            return block; 
        }

        internal static ZipIOZip64EndOfCentralDirectoryBlock CreateNew(ZipIOBlockManager blockManager)
        { 
            ZipIOZip64EndOfCentralDirectoryBlock block = new ZipIOZip64EndOfCentralDirectoryBlock(blockManager);
 
            block._size = 0; // brand new created records are optional by definition untill UpdateReferences is called, so size must be 0 
            block._offset = 0;
            block._dirtyFlag = false; 

            // initialize fields with ythe data from the EOCD
            block.InitializeFromEndOfCentralDirectory(blockManager.EndOfCentralDirectoryBlock);
 
            return block;
        } 
 
        internal long OffsetOfStartOfCentralDirectory
        { 
            get
            {
                return (long)_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber;
            } 
        }
 
        internal int TotalNumberOfEntriesInTheCentralDirectory 
        {
            get 
            {
                return (int)_totalNumberOfEntriesInTheCentralDirectory; // checked isn't required as we do validation during parsing
            }
        } 

        internal long SizeOfCentralDirectory 
        { 
            get
            { 
                return (long)_sizeOfTheCentralDirectory;
            }
        }
 
        private ZipIOZip64EndOfCentralDirectoryBlock(ZipIOBlockManager blockManager)
        { 
            Debug.Assert(blockManager != null); 
            _blockManager= blockManager;
        } 

        private void ParseRecord (BinaryReader reader, long position)
        {
            _signature = reader.ReadUInt32(); 
            _sizeOfZip64EndOfCentralDirectory = reader.ReadUInt64();
            _versionMadeBy = reader.ReadUInt16(); 
            _versionNeededToExtract = reader.ReadUInt16(); 
            _numberOfThisDisk = reader.ReadUInt32();
            _numberOfTheDiskWithTheStartOfTheCentralDirectory = reader.ReadUInt32(); 
            _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk = reader.ReadUInt64();
            _totalNumberOfEntriesInTheCentralDirectory = reader.ReadUInt64();
            _sizeOfTheCentralDirectory = reader.ReadUInt64();
            _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = reader.ReadUInt64(); 

                                                // pre validate before reading data based on parsed values 
            if ((_sizeOfZip64EndOfCentralDirectory < _fixedMinimalValueOfSizeOfZip64EOCD) || 
                                                // we are refusing to buffer large extended areas
                (_sizeOfZip64EndOfCentralDirectory > UInt16.MaxValue)) 
            {
                throw new FileFormatException(SR.Get(SRID.CorruptedData));
            }
 
            if (_sizeOfZip64EndOfCentralDirectory > _fixedMinimalValueOfSizeOfZip64EOCD)
            { 
                _zip64ExtensibleDataSector = reader.ReadBytes((int)(_sizeOfZip64EndOfCentralDirectory -_fixedMinimalValueOfSizeOfZip64EOCD)); 
            }
 
            // override some numbers bvased on the EOCD data according to the  apnote
            // even in presence of Zip64Eocd we still need to use the regular EOCD data
            OverrideValuesBasedOnEndOfCentralDirectory(_blockManager.EndOfCentralDirectoryBlock);
 
            _size =  checked((long)(    // value that was either parsed from a file or initialized to the _fixedMinimalValueOfSizeOfZip64EOCD
                                        _sizeOfZip64EndOfCentralDirectory + 
                                                    // const (value indicating minimal whole record size, how many bytes on disk it needs) 56 
                                        _fixedMinimalRecordSize -
                                                    // const (value indicating minimal value for the SizeOfZip64EOCD field as it is contains 
                                                    // the whole size without record signature(4), and the itself (8) it is 56 - 12 = 44
                                        _fixedMinimalValueOfSizeOfZip64EOCD));
            Debug.Assert(_size >= _fixedMinimalRecordSize);
 
            _offset = position;
            _dirtyFlag = false; 
 
            Validate();
        } 

        /// 
        /// This function is called from the Create New routine. The purpose of this exercise , is to copy data from 32 bit EOCD into this record,
        /// for scenarios when ZIP64 EOCD wasn't parsed from a file, but was just made up. 
        /// This is done so that Central Dir parsing code can ask the ZIP64 EOCD for this data, and regardless of whether it is real zip 64 file or
        /// not a zip 64 file it will get the right CD offset , size and so on 
        ///  
        private void InitializeFromEndOfCentralDirectory(ZipIOEndOfCentralDirectoryBlock zipIoEocd)
        { 
            _numberOfThisDisk = zipIoEocd.NumberOfThisDisk;
            _numberOfTheDiskWithTheStartOfTheCentralDirectory = zipIoEocd.NumberOfTheDiskWithTheStartOfTheCentralDirectory;
            _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk  = zipIoEocd.TotalNumberOfEntriesInTheCentralDirectoryOnThisDisk;
            _totalNumberOfEntriesInTheCentralDirectory = zipIoEocd.TotalNumberOfEntriesInTheCentralDirectory; 
            _sizeOfTheCentralDirectory = zipIoEocd.SizeOfTheCentralDirectory;
            _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = zipIoEocd.OffsetOfStartOfCentralDirectory; 
        } 

        ///  
        /// This function is called from the Parse routine. The purpose of this exercise , is to figure out the escape
        /// values in the regular 32 bit EOCD. We shouldn't be using values from the 64 bit structure if it wasn't
        /// escaped in the 32 bit structure.
        ///  
        private void OverrideValuesBasedOnEndOfCentralDirectory(ZipIOEndOfCentralDirectoryBlock zipIoEocd)
        { 
            // 16 bit numbers 
            if (zipIoEocd.NumberOfThisDisk < UInt16.MaxValue)
                {_numberOfThisDisk = zipIoEocd.NumberOfThisDisk;} 

            if (zipIoEocd.NumberOfTheDiskWithTheStartOfTheCentralDirectory < UInt16.MaxValue)
                {_numberOfTheDiskWithTheStartOfTheCentralDirectory = zipIoEocd.NumberOfTheDiskWithTheStartOfTheCentralDirectory;}
 
            if (zipIoEocd.TotalNumberOfEntriesInTheCentralDirectoryOnThisDisk  < UInt16.MaxValue)
                {_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk  = zipIoEocd.TotalNumberOfEntriesInTheCentralDirectoryOnThisDisk;} 
 
            if (zipIoEocd.TotalNumberOfEntriesInTheCentralDirectory < UInt16.MaxValue)
                {_totalNumberOfEntriesInTheCentralDirectory = zipIoEocd.TotalNumberOfEntriesInTheCentralDirectory;} 

            // 32  bit numbers
            if (zipIoEocd.SizeOfTheCentralDirectory < UInt32.MaxValue)
                {_sizeOfTheCentralDirectory = zipIoEocd.SizeOfTheCentralDirectory;} 

            if (zipIoEocd.OffsetOfStartOfCentralDirectory < UInt32.MaxValue) 
                {_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber = zipIoEocd.OffsetOfStartOfCentralDirectory;} 

        } 

        private void Validate()
        {
            if (_signature != _signatureConstant) 
            {
                throw new FileFormatException(SR.Get(SRID.CorruptedData)); 
            } 

            if ((_numberOfThisDisk != 0) || 
                (_numberOfTheDiskWithTheStartOfTheCentralDirectory != 0) ||
                (_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk !=
                                                    _totalNumberOfEntriesInTheCentralDirectory))
            { 
                throw new NotSupportedException(SR.Get(SRID.NotSupportedMultiDisk));
            } 
 
            // this will throw an unsupported version exception if we see a version that we do not support
            ZipArchive.VerifyVersionNeededToExtract(_versionNeededToExtract); 

            // if it is one of the supported version but it isn't a ZIP64, it is an indication of a corrupted file
            if (_versionNeededToExtract !=  (UInt16)ZipIOVersionNeededToExtract.Zip64FileFormat)
            { 
                // if version isn't equal to the 4.5 it is a corrupted file (as we)
                // as appnote explicitly states that 
                //            When using ZIP64 extensions, the corresponding value in the 
                //            Zip64 end of central directory record should also be set.
                //            This field currently supports only the value 45 to indicate 
                //            ZIP64 extensions are present.
                throw new FileFormatException(SR.Get(SRID.CorruptedData));
            }
 
            if ((_totalNumberOfEntriesInTheCentralDirectoryOnThisDisk > Int32.MaxValue) ||
                (_totalNumberOfEntriesInTheCentralDirectory > Int32.MaxValue) || 
                (_sizeOfTheCentralDirectory > Int64.MaxValue) || 
                (_offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber > Int64.MaxValue))
            { 
                // although we are trying to support 64 bit structures
                // we are limited by the CLR model for collections (down to 32 bit collection size for
                // _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk )
                // and streams (down to 63 bit size) for all the outher Uint64 fields 

                throw new NotSupportedException(SR.Get(SRID.Zip64StructuresTooLarge)); 
            } 

            ulong sizeOfZip64ExtensibleDataSector = 0; 
            if (_zip64ExtensibleDataSector != null)
            {
                sizeOfZip64ExtensibleDataSector = (ulong)_zip64ExtensibleDataSector.Length;
            } 

            // the subtraction below doesn't need to be checked as we have validation in the parse logic 
            //    if (_sizeOfZip64EndOfCentralDirectory < _fixedMinimalValueOfSizeOfZip64EOCD)   {   throw ..  } 
            if (_sizeOfZip64EndOfCentralDirectory - _fixedMinimalValueOfSizeOfZip64EOCD != sizeOfZip64ExtensibleDataSector)
            { 
                throw new FileFormatException(SR.Get(SRID.CorruptedData));
            }

            //calculated record size must be larger than the min value 
            // it could be 0 for newly created from scratch records, but we do not pass those records through validation
            // we only validate parsed data 
            if (_size < _fixedMinimalRecordSize) 
            {
                throw new FileFormatException(SR.Get(SRID.CorruptedData)); 
            }
        }

 
        private ZipIOBlockManager _blockManager;
 
        private long _offset; 
        private long _size;
 
        private bool  _dirtyFlag;

        private  const UInt32 _signatureConstant  = 0x06064b50;
        private const uint _fixedMinimalRecordSize = 56; 
        private const uint _fixedMinimalValueOfSizeOfZip64EOCD = 44; // doesn't include the signature and the size itself
 
        // data persisted on disk 
        private UInt32 _signature = _signatureConstant;
 
        private UInt64 _sizeOfZip64EndOfCentralDirectory = _fixedMinimalValueOfSizeOfZip64EOCD;
        private UInt16 _versionMadeBy = (ushort)ZipIOVersionNeededToExtract.Zip64FileFormat;
        private UInt16 _versionNeededToExtract = (ushort)ZipIOVersionNeededToExtract.Zip64FileFormat;
        private UInt32 _numberOfThisDisk; 
        private UInt32 _numberOfTheDiskWithTheStartOfTheCentralDirectory;
        private UInt64 _totalNumberOfEntriesInTheCentralDirectoryOnThisDisk;     // all int64s declared as signed values 
        private UInt64 _totalNumberOfEntriesInTheCentralDirectory;                       // as we can not suport true unsigned 64 bit sizes 
        private UInt64 _sizeOfTheCentralDirectory;                                                    // as a result of limitations in Stream interface
        private UInt64 _offsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber; 
        private byte[] _zip64ExtensibleDataSector;
    }
}

// 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