ZipIOZip64EndOfCentralDirectoryBlock.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Base / MS / Internal / IO / Zip / ZipIOZip64EndOfCentralDirectoryBlock.cs / 1305600 / 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;
using MS.Internal.WindowsBase;

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;
using MS.Internal.WindowsBase;

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