ZipIOCentralDirectoryBlock.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 / ZipIOCentralDirectoryBlock.cs / 1 / ZipIOCentralDirectoryBlock.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.Text; 
using System.Collections;
using System.Collections.Specialized;       // OrderedDictionary
using System.Globalization;
using System.Windows; 

namespace MS.Internal.IO.Zip 
{ 
    internal class ZipIOCentralDirectoryBlock : IZipIOBlock
    { 
        //------------------------------------------------------
        //
        //  Public Properties
        // 
        //-----------------------------------------------------
        // standard IZipIOBlock functionality 
        public long Offset 
        {
            get 
            {
                return _offset;
            }
        } 

        public long Size 
        { 
            get
            { 
                long result = 0;
                if (CentralDirectoryDictionary.Count > 0)
                {
                    foreach(ZipIOCentralDirectoryFileHeader fileHeader in CentralDirectoryDictionary.Values) 
                    {
                        checked{result += fileHeader.Size;} 
                    } 

// Zeus PS 3: disable creation/parsing of zip archive digital signatures 
#if ArchiveSignaturesEnabled
                    if (_centralDirectoryDigitalSignature != null)
                    {
                        checked{result += _centralDirectoryDigitalSignature.Size;} 
                    }
#endif 
                } 
                return result;
            } 
        }

        // This property will only return reliable result if Update is called prior
        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 (_dirtyFlag)
            { 
                // Central directory is an optional component of the ZIP Archive
                // we need to save it if it isn't empty
                if (CentralDirectoryDictionary.Count > 0)
                { 
                    BinaryWriter writer = _blockManager.BinaryWriter;
 
                    // Emit entries in the same order as the corresponding file items as this 
                    // improves interoperability with tools that expect this convention.
 
                    // Streaming mode must be handled differently
                    if (_blockManager.Streaming)
                    {
                        // In Streaming mode we cannot rely on the order that entries were inserted via AddFiles() 
                        // as files can be closed in different order than they are added.
 
                        //   NOTE: Neither ZipIOBlockManager._blockList nor CentralDirectoryDicstionry 
                        //      are NOT in offset order
 
#if DEBUG
                        long lastOffset = -1;
#endif
 
                        // collect all file headers in central directory into a local list for sorting
                        SortedList blockList = new SortedList(CentralDirectoryDictionary.Count); 
 
                        // We know that in Streaming mode there can be no RawDataFile blocks in
                        // the block list.  Therefore, we can emit our headers in the order that they 
                        // appear in the block list.
                        foreach (ZipIOCentralDirectoryFileHeader header in CentralDirectoryDictionary.Values)
                        {
                            blockList.Add(header.OffsetOfLocalHeader, header); 
                        }
 
                        // then write out the files headers for central directory in sorted order 
                        foreach (ZipIOCentralDirectoryFileHeader header in blockList.Values)
                        { 

                            header.Save(writer);
#if DEBUG
                            Debug.Assert(lastOffset < header.OffsetOfLocalHeader, "Sort order violated"); 
                            lastOffset = header.OffsetOfLocalHeader;
#endif 
                        } 
                    }
                    else 
                    {
                        // Non-streaming mode - CentralDirectoryDictionary has correct order.
                        // Assume correct location if streaming - otherwise explicitly seek.
                        if (_blockManager.Stream.Position != _offset) 
                        {
                            // we need to seek 
                            _blockManager.Stream.Seek(_offset, SeekOrigin.Begin); 
                        }
 
                        // Save the headers in the order they were added as this matches the physical offsets
                        foreach (ZipIOCentralDirectoryFileHeader fileHeader in CentralDirectoryDictionary.Values)
                        {
                            fileHeader.Save(writer); 
                        }
 
                    } 

                    // Zeus PS 3: disable creation/parsing of zip archive digital signatures 
#if ArchiveSignaturesEnabled
                    //central directory dig sig is optional
                    if (_centralDirectoryDigitalSignature != null)
                    { 
                        _centralDirectoryDigitalSignature.Save(writer);
                    } 
#endif 
                    writer.Flush();
                } 

                _dirtyFlag = false;
            }
        } 

        public void UpdateReferences(bool closingFlag) 
        { 
            // we just need to ask Block Manager for the new Values for each header
            // there are 2 distinct cases here 
            //    1. local file data is mapped . loaded and might have been changed in size and position
            //    2. local file data is not  loaded and might have been changed only in position (not in size)

            foreach(IZipIOBlock block in _blockManager) 
            {
                ZipIOLocalFileBlock localFileBlock = block as ZipIOLocalFileBlock; 
                ZipIORawDataFileBlock rawDataFileBlock = block as ZipIORawDataFileBlock; 

                if (localFileBlock != null) 
                {
                    // this is case 1 data is mapped and loaded, so we only need to find the matching
                    // Centraldirectory record and update it
                    Debug.Assert(CentralDirectoryDictionary.Contains(localFileBlock.FileName)); 

                    ZipIOCentralDirectoryFileHeader centralDirFileHeader = 
                                    (ZipIOCentralDirectoryFileHeader)CentralDirectoryDictionary[localFileBlock.FileName]; 

                    if (centralDirFileHeader.UpdateIfNeeded(localFileBlock)) 
                    {
                        //update was required let's mark ourselves as dirty
                        _dirtyFlag = true;
                    } 
                }
                            //check whether we deal with raw data block and it was moved 
                else if (rawDataFileBlock != null) 
                {
                    long diskImageShift = rawDataFileBlock.DiskImageShift; 
                    if (diskImageShift != 0)
                    {
                        //this is case #2 data isn't loaded based on the shift in the RawData Block
                        // we need to move all overlapping central directory references 
                        foreach (ZipIOCentralDirectoryFileHeader centralDirFileHeader in CentralDirectoryDictionary.Values)
                        { 
                            // check whether central dir header points into the region of a moved RawDataBlock 
                            if (rawDataFileBlock.DiskImageContains(centralDirFileHeader.OffsetOfLocalHeader))
                            { 
                                centralDirFileHeader.MoveReference(diskImageShift);
                                _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 Central directory without any additional backups
 
            // we can also safely state that there is no need to continue the PreSafeNotification loop 
            // as all the blocks after the central directory (EOCD, Zip64 ....) do not have
            // data that is buffered on disk 
            return PreSaveNotificationScanControlInstruction.Stop;
        }

        //----------------------------------------------------- 
        //
        //  Internal Properties 
        // 
        //------------------------------------------------------
        // although Zip 64 supports 64 bit counter for the number of 
        // entries in the central directory, we have chossen to not
        // support those scenarios and stick wit the basic CLR type
        //          int Collections.Count {get;}
        internal int Count 
        {
            get 
            { 
                return CentralDirectoryDictionary.Count;
            } 
        }

        //-----------------------------------------------------
        // 
        //  Internal Methods
        // 
        //----------------------------------------------------- 
        internal static ZipIOCentralDirectoryBlock SeekableLoad(ZipIOBlockManager blockManager)
        { 
            // get proper values from  zip 64 records request will be redirected to the
            // regular EOCD if ZIP 64 record wasn't originated from the parsing

            ZipIOZip64EndOfCentralDirectoryBlock zip64EOCD = blockManager.Zip64EndOfCentralDirectoryBlock; 

            blockManager.Stream.Seek(zip64EOCD.OffsetOfStartOfCentralDirectory, SeekOrigin.Begin); 
 
            ZipIOCentralDirectoryBlock block = new ZipIOCentralDirectoryBlock(blockManager);
 
            block.ParseRecord(blockManager.BinaryReader,
                                            zip64EOCD.OffsetOfStartOfCentralDirectory,
                                            zip64EOCD.TotalNumberOfEntriesInTheCentralDirectory,
                                            zip64EOCD.SizeOfCentralDirectory); 

            return block; 
        } 

        internal static ZipIOCentralDirectoryBlock CreateNew(ZipIOBlockManager blockManager) 
        {
            ZipIOCentralDirectoryBlock block = new ZipIOCentralDirectoryBlock(blockManager);

            block._offset = 0;              // it just an initial value, that will be adjusted later 
                                                   // it doesn't matter whether this offset overlaps anything or not
 
            block._dirtyFlag = true; 

            // this dig sig is optional if we ever wanted to make this record, we would need to call 
            //       ZipIOCentralDirectoryDigitalSignature.CreateNew();
            block._centralDirectoryDigitalSignature = null;

            return block; 
        }
 
        // This properrty returns current snapsot which might be out of date 
        // if there were changes after parsing or last UpdateReferences call
        internal bool IsZip64BitRequiredForStoring 
        {
            get
            {
                // These values are duplicated the EndOfCentralDirectory record 
                // and if any of them are to big we need to introduce
                //      Zip64 end of central directory record 
                //      Zip64 end of central directory locator 
                return (Count >= UInt16.MaxValue) ||
                            (Offset >= UInt32.MaxValue) || 
                            (Size >= UInt32.MaxValue);
            }
        }
 
        internal void AddFileBlock(ZipIOLocalFileBlock fileBlock)
        { 
            _dirtyFlag = true; 

            ZipIOCentralDirectoryFileHeader fileHeader = 
                        ZipIOCentralDirectoryFileHeader.CreateNew
                                                            (_blockManager.Encoding, fileBlock);

            CentralDirectoryDictionary.Add(fileHeader.FileName, fileHeader); 
        }
 
        ///  
        ///
        ///  
        /// 
        /// precondition: caller must ensure that fileName exists
        internal void RemoveFileBlock(string fileName)
        { 
            _dirtyFlag = true;
 
            CentralDirectoryDictionary.Remove(fileName); 

            if (CentralDirectoryDictionary.Count == 0) 
            {
                // in case of the the last one ,we also need to drop the signature record
                _centralDirectoryDigitalSignature = null;
            } 
        }
 
        internal bool FileExists(string fileName) 
        {
            return CentralDirectoryDictionary.Contains(fileName); 
        }

        // this function should be used carefully as it returns reference to an object
        // that is owned by CentralDirectoryBlock, and should be used by other classes only 
        // for querying information not for updating it.
        internal ZipIOCentralDirectoryFileHeader GetCentralDirectoryFileHeader (string fileName) 
        { 
            return ((ZipIOCentralDirectoryFileHeader)CentralDirectoryDictionary[fileName]);
        } 

        internal ICollection GetFileNamesCollection()
        {
            return CentralDirectoryDictionary.Keys; 
        }
 
        //----------------------------------------------------- 
        //
        //  Private Methods 
        //
        //------------------------------------------------------
        private ZipIOCentralDirectoryBlock(ZipIOBlockManager blockManager)
        { 
            _blockManager = blockManager;
        } 
 
        /// 
        /// Compare FileOffsets for LocalFileHeaders - used by Sort() routine in ParseRecord 
        /// 
        private class HeaderFileOffsetComparer : IComparer
        {
            int IComparer.Compare(object o1, object o2) 
            {
                ZipIOCentralDirectoryFileHeader h1 = o1 as ZipIOCentralDirectoryFileHeader; 
                ZipIOCentralDirectoryFileHeader h2 = o2 as ZipIOCentralDirectoryFileHeader; 
                Debug.Assert(h1 != null && h2 != null, "HeaderFileOffsetComparer: Comparing the wrong data types");
 
                // avoid boxing - don't cast long value to (IComparable)
                if (h1.OffsetOfLocalHeader > h2.OffsetOfLocalHeader)
                    return 1;
                else if (h1.OffsetOfLocalHeader < h2.OffsetOfLocalHeader) 
                    return -1;
                else 
                    return 0; 
            }
        } 

        private void ParseRecord (BinaryReader reader,
                                                        long centralDirectoryOffset,
                                                        int centralDirectoryCount, 
                                                        long expectedCentralDirectorySize)
        { 
            if (centralDirectoryCount > 0) 
            {
                // collect all headers into a local array list for sorting 
                SortedList headerList = new SortedList(centralDirectoryCount);
                ZipIOCentralDirectoryFileHeader header;
                for (int i = 0; i < centralDirectoryCount; i++)
                { 
                    header = ZipIOCentralDirectoryFileHeader.ParseRecord(reader, _blockManager.Encoding);
                    headerList.Add(header.OffsetOfLocalHeader, header); 
                } 

                if (reader.BaseStream.Position - centralDirectoryOffset > expectedCentralDirectorySize) 
                {   // it looks like a corrupted file, as we have parsed more than central directory supposed to contain
                    throw new FileFormatException(SR.Get(SRID.CorruptedData));
                }
 
                // then add to the ordered dictionary in sorted order
                foreach (ZipIOCentralDirectoryFileHeader fileHeader in headerList.Values) 
                { 
                    // at this point fileHeader.FileName is normalized using
                    // the ZipIOBlockManager.ValidateNormalizeFileName 
                    CentralDirectoryDictionary.Add(fileHeader.FileName, fileHeader);
                }

                //load central directory [digital signature] - this has nothing to 
                // do with OPC digital signing
                // this record is optional, and the function might return null 
                _centralDirectoryDigitalSignature = ZipIOCentralDirectoryDigitalSignature.ParseRecord(reader); 
            }
 
            _offset = centralDirectoryOffset;
            _dirtyFlag = false;

            Validate(expectedCentralDirectorySize); 
        }
 
        private void Validate(long expectedCentralDirectorySize) 
        {
            checked 
            {
                    // We only have information about the Compressed data size and the offset of the
                    // local headers. We do not have information about the size of the local header
                    // which varies depending on the file name and the extra field records size. 
                    // (Although we do know the expected size of the file name, there is no way to
                    // predict the extra field size, for example it might have a padding record that we use 
                    // optimize Disk IO for ZIP 64 scenarios). 
                    // We are going to make sure that Blocks do not overlap each other and do not
                    // overlap Central Directory 

                long checkedMark = 0;

                foreach (ZipIOCentralDirectoryFileHeader fileHeader in CentralDirectoryDictionary.Values) 
                {
                    if ((checkedMark == 0) && (fileHeader.OffsetOfLocalHeader != 0)) 
                    { 
                        // first block doesn't start at 0
                        throw new FileFormatException(SR.Get(SRID.CorruptedData)); 
                    }
                    else if (fileHeader.OffsetOfLocalHeader < checkedMark)
                    {
                        // the current block overlaps the previously analyzed block 
                        throw new FileFormatException(SR.Get(SRID.CorruptedData));
                    } 
 
                    // we move the checked mark up by the sum of the compressed size file name size
                    // and the fixed minimal Local file header size 
                    checkedMark += fileHeader.CompressedSize +
                                                ZipIOLocalFileHeader.FixedMinimalRecordSize +
                                                fileHeader.FileName.Length;
                } 

                // now we can ensure that that checked mark didn't reach over the start of the Central directory 
                 if (_offset < checkedMark) 
                {
                    // the central directory block overlaps the last file block 
                    throw new FileFormatException(SR.Get(SRID.CorruptedData));
                }

                 //check the total parsed size of the central directory against value declared in EOCd or ZIP64 EOCD records 
                 if (Size != expectedCentralDirectorySize)
                { 
                    // the central directory block overlaps the last file block 
                    throw new FileFormatException(SR.Get(SRID.CorruptedData));
                } 

                // we should also check ofr presence of gaps between
                //              Central Directory
                //              ZIP64 EOCD 
                //              ZIP 64 EOCD locator
                //              EOCD 
                // Zip64Eocd and Zip64EocdLocator must be either present or absent together 
                Debug.Assert(! (_blockManager.Zip64EndOfCentralDirectoryBlock.Size==0)
                                                    ^ 
                                            (_blockManager.Zip64EndOfCentralDirectoryLocatorBlock.Size==0));

                if (_blockManager.Zip64EndOfCentralDirectoryBlock.Size==0)
                { 
                    // no ZIP 64 record
                    if (_offset + expectedCentralDirectorySize != _blockManager.EndOfCentralDirectoryBlock.Offset) 
                    { 
                        throw new FileFormatException(SR.Get(SRID.CorruptedData));
                    } 
                }
                else
                {
                    // ZIP 64 records present 
                    if ((_offset + expectedCentralDirectorySize
                                        != _blockManager.Zip64EndOfCentralDirectoryBlock.Offset) || 
 
                         (_blockManager.Zip64EndOfCentralDirectoryBlock.Offset + _blockManager.Zip64EndOfCentralDirectoryBlock.Size
                                        != _blockManager.Zip64EndOfCentralDirectoryLocatorBlock.Offset) || 

                         (_blockManager.Zip64EndOfCentralDirectoryLocatorBlock.Offset + _blockManager.Zip64EndOfCentralDirectoryLocatorBlock.Size
                                        != _blockManager.EndOfCentralDirectoryBlock.Offset))
                    { 
                        throw new FileFormatException(SR.Get(SRID.CorruptedData));
                    } 
                } 
            }
        } 

        //-----------------------------------------------------
        //
        //  Private Properties 
        //
        //------------------------------------------------------ 
        private IDictionary CentralDirectoryDictionary 
        {
            get 
            {
                if (_centralDirectoryDictionary == null)
                {
                    // StringComparer.Ordinal guarantees ordinal, case-sensitive comparison for both cases. 

                    // if streaming - order is unimportant for us and we can use the cheaper Hashtable 
                    if (_blockManager.Streaming) 
                    {
 
                        // We take our order during Save() from the physical order of the elements of the
                        // block table in BlockManager.
                        _centralDirectoryDictionary = new Hashtable(_centralDirectoryDictionaryInitialSize, StringComparer.Ordinal);
                    } 
                    else
                    { 
                        // This ordered dictionary serves two purposes.  It allows hash-table lookup by file name 
                        // and it also maintains the physical order of the blocks on disk.  Like any OrderedDictionary,
                        // any of the enumerator, or integer indexer will return the items in the order that they were added. 
                        _centralDirectoryDictionary = new OrderedDictionary(_centralDirectoryDictionaryInitialSize, StringComparer.Ordinal);
                    }
                }
                return _centralDirectoryDictionary; 
            }
        } 
 
        //------------------------------------------------------
        // 
        //  Private Members
        //
        //-----------------------------------------------------
        private const int _centralDirectoryDictionaryInitialSize = 50; 

        // used in Parse 
        private static IComparer _headerOffsetComparer = new HeaderFileOffsetComparer(); 

        // This may be a HashTable (Streaming case) or an OrderedDictionary - see private property 
        // for explanation.
        private IDictionary _centralDirectoryDictionary;

        private ZipIOCentralDirectoryDigitalSignature _centralDirectoryDigitalSignature; 

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

// 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.Text; 
using System.Collections;
using System.Collections.Specialized;       // OrderedDictionary
using System.Globalization;
using System.Windows; 

namespace MS.Internal.IO.Zip 
{ 
    internal class ZipIOCentralDirectoryBlock : IZipIOBlock
    { 
        //------------------------------------------------------
        //
        //  Public Properties
        // 
        //-----------------------------------------------------
        // standard IZipIOBlock functionality 
        public long Offset 
        {
            get 
            {
                return _offset;
            }
        } 

        public long Size 
        { 
            get
            { 
                long result = 0;
                if (CentralDirectoryDictionary.Count > 0)
                {
                    foreach(ZipIOCentralDirectoryFileHeader fileHeader in CentralDirectoryDictionary.Values) 
                    {
                        checked{result += fileHeader.Size;} 
                    } 

// Zeus PS 3: disable creation/parsing of zip archive digital signatures 
#if ArchiveSignaturesEnabled
                    if (_centralDirectoryDigitalSignature != null)
                    {
                        checked{result += _centralDirectoryDigitalSignature.Size;} 
                    }
#endif 
                } 
                return result;
            } 
        }

        // This property will only return reliable result if Update is called prior
        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 (_dirtyFlag)
            { 
                // Central directory is an optional component of the ZIP Archive
                // we need to save it if it isn't empty
                if (CentralDirectoryDictionary.Count > 0)
                { 
                    BinaryWriter writer = _blockManager.BinaryWriter;
 
                    // Emit entries in the same order as the corresponding file items as this 
                    // improves interoperability with tools that expect this convention.
 
                    // Streaming mode must be handled differently
                    if (_blockManager.Streaming)
                    {
                        // In Streaming mode we cannot rely on the order that entries were inserted via AddFiles() 
                        // as files can be closed in different order than they are added.
 
                        //   NOTE: Neither ZipIOBlockManager._blockList nor CentralDirectoryDicstionry 
                        //      are NOT in offset order
 
#if DEBUG
                        long lastOffset = -1;
#endif
 
                        // collect all file headers in central directory into a local list for sorting
                        SortedList blockList = new SortedList(CentralDirectoryDictionary.Count); 
 
                        // We know that in Streaming mode there can be no RawDataFile blocks in
                        // the block list.  Therefore, we can emit our headers in the order that they 
                        // appear in the block list.
                        foreach (ZipIOCentralDirectoryFileHeader header in CentralDirectoryDictionary.Values)
                        {
                            blockList.Add(header.OffsetOfLocalHeader, header); 
                        }
 
                        // then write out the files headers for central directory in sorted order 
                        foreach (ZipIOCentralDirectoryFileHeader header in blockList.Values)
                        { 

                            header.Save(writer);
#if DEBUG
                            Debug.Assert(lastOffset < header.OffsetOfLocalHeader, "Sort order violated"); 
                            lastOffset = header.OffsetOfLocalHeader;
#endif 
                        } 
                    }
                    else 
                    {
                        // Non-streaming mode - CentralDirectoryDictionary has correct order.
                        // Assume correct location if streaming - otherwise explicitly seek.
                        if (_blockManager.Stream.Position != _offset) 
                        {
                            // we need to seek 
                            _blockManager.Stream.Seek(_offset, SeekOrigin.Begin); 
                        }
 
                        // Save the headers in the order they were added as this matches the physical offsets
                        foreach (ZipIOCentralDirectoryFileHeader fileHeader in CentralDirectoryDictionary.Values)
                        {
                            fileHeader.Save(writer); 
                        }
 
                    } 

                    // Zeus PS 3: disable creation/parsing of zip archive digital signatures 
#if ArchiveSignaturesEnabled
                    //central directory dig sig is optional
                    if (_centralDirectoryDigitalSignature != null)
                    { 
                        _centralDirectoryDigitalSignature.Save(writer);
                    } 
#endif 
                    writer.Flush();
                } 

                _dirtyFlag = false;
            }
        } 

        public void UpdateReferences(bool closingFlag) 
        { 
            // we just need to ask Block Manager for the new Values for each header
            // there are 2 distinct cases here 
            //    1. local file data is mapped . loaded and might have been changed in size and position
            //    2. local file data is not  loaded and might have been changed only in position (not in size)

            foreach(IZipIOBlock block in _blockManager) 
            {
                ZipIOLocalFileBlock localFileBlock = block as ZipIOLocalFileBlock; 
                ZipIORawDataFileBlock rawDataFileBlock = block as ZipIORawDataFileBlock; 

                if (localFileBlock != null) 
                {
                    // this is case 1 data is mapped and loaded, so we only need to find the matching
                    // Centraldirectory record and update it
                    Debug.Assert(CentralDirectoryDictionary.Contains(localFileBlock.FileName)); 

                    ZipIOCentralDirectoryFileHeader centralDirFileHeader = 
                                    (ZipIOCentralDirectoryFileHeader)CentralDirectoryDictionary[localFileBlock.FileName]; 

                    if (centralDirFileHeader.UpdateIfNeeded(localFileBlock)) 
                    {
                        //update was required let's mark ourselves as dirty
                        _dirtyFlag = true;
                    } 
                }
                            //check whether we deal with raw data block and it was moved 
                else if (rawDataFileBlock != null) 
                {
                    long diskImageShift = rawDataFileBlock.DiskImageShift; 
                    if (diskImageShift != 0)
                    {
                        //this is case #2 data isn't loaded based on the shift in the RawData Block
                        // we need to move all overlapping central directory references 
                        foreach (ZipIOCentralDirectoryFileHeader centralDirFileHeader in CentralDirectoryDictionary.Values)
                        { 
                            // check whether central dir header points into the region of a moved RawDataBlock 
                            if (rawDataFileBlock.DiskImageContains(centralDirFileHeader.OffsetOfLocalHeader))
                            { 
                                centralDirFileHeader.MoveReference(diskImageShift);
                                _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 Central directory without any additional backups
 
            // we can also safely state that there is no need to continue the PreSafeNotification loop 
            // as all the blocks after the central directory (EOCD, Zip64 ....) do not have
            // data that is buffered on disk 
            return PreSaveNotificationScanControlInstruction.Stop;
        }

        //----------------------------------------------------- 
        //
        //  Internal Properties 
        // 
        //------------------------------------------------------
        // although Zip 64 supports 64 bit counter for the number of 
        // entries in the central directory, we have chossen to not
        // support those scenarios and stick wit the basic CLR type
        //          int Collections.Count {get;}
        internal int Count 
        {
            get 
            { 
                return CentralDirectoryDictionary.Count;
            } 
        }

        //-----------------------------------------------------
        // 
        //  Internal Methods
        // 
        //----------------------------------------------------- 
        internal static ZipIOCentralDirectoryBlock SeekableLoad(ZipIOBlockManager blockManager)
        { 
            // get proper values from  zip 64 records request will be redirected to the
            // regular EOCD if ZIP 64 record wasn't originated from the parsing

            ZipIOZip64EndOfCentralDirectoryBlock zip64EOCD = blockManager.Zip64EndOfCentralDirectoryBlock; 

            blockManager.Stream.Seek(zip64EOCD.OffsetOfStartOfCentralDirectory, SeekOrigin.Begin); 
 
            ZipIOCentralDirectoryBlock block = new ZipIOCentralDirectoryBlock(blockManager);
 
            block.ParseRecord(blockManager.BinaryReader,
                                            zip64EOCD.OffsetOfStartOfCentralDirectory,
                                            zip64EOCD.TotalNumberOfEntriesInTheCentralDirectory,
                                            zip64EOCD.SizeOfCentralDirectory); 

            return block; 
        } 

        internal static ZipIOCentralDirectoryBlock CreateNew(ZipIOBlockManager blockManager) 
        {
            ZipIOCentralDirectoryBlock block = new ZipIOCentralDirectoryBlock(blockManager);

            block._offset = 0;              // it just an initial value, that will be adjusted later 
                                                   // it doesn't matter whether this offset overlaps anything or not
 
            block._dirtyFlag = true; 

            // this dig sig is optional if we ever wanted to make this record, we would need to call 
            //       ZipIOCentralDirectoryDigitalSignature.CreateNew();
            block._centralDirectoryDigitalSignature = null;

            return block; 
        }
 
        // This properrty returns current snapsot which might be out of date 
        // if there were changes after parsing or last UpdateReferences call
        internal bool IsZip64BitRequiredForStoring 
        {
            get
            {
                // These values are duplicated the EndOfCentralDirectory record 
                // and if any of them are to big we need to introduce
                //      Zip64 end of central directory record 
                //      Zip64 end of central directory locator 
                return (Count >= UInt16.MaxValue) ||
                            (Offset >= UInt32.MaxValue) || 
                            (Size >= UInt32.MaxValue);
            }
        }
 
        internal void AddFileBlock(ZipIOLocalFileBlock fileBlock)
        { 
            _dirtyFlag = true; 

            ZipIOCentralDirectoryFileHeader fileHeader = 
                        ZipIOCentralDirectoryFileHeader.CreateNew
                                                            (_blockManager.Encoding, fileBlock);

            CentralDirectoryDictionary.Add(fileHeader.FileName, fileHeader); 
        }
 
        ///  
        ///
        ///  
        /// 
        /// precondition: caller must ensure that fileName exists
        internal void RemoveFileBlock(string fileName)
        { 
            _dirtyFlag = true;
 
            CentralDirectoryDictionary.Remove(fileName); 

            if (CentralDirectoryDictionary.Count == 0) 
            {
                // in case of the the last one ,we also need to drop the signature record
                _centralDirectoryDigitalSignature = null;
            } 
        }
 
        internal bool FileExists(string fileName) 
        {
            return CentralDirectoryDictionary.Contains(fileName); 
        }

        // this function should be used carefully as it returns reference to an object
        // that is owned by CentralDirectoryBlock, and should be used by other classes only 
        // for querying information not for updating it.
        internal ZipIOCentralDirectoryFileHeader GetCentralDirectoryFileHeader (string fileName) 
        { 
            return ((ZipIOCentralDirectoryFileHeader)CentralDirectoryDictionary[fileName]);
        } 

        internal ICollection GetFileNamesCollection()
        {
            return CentralDirectoryDictionary.Keys; 
        }
 
        //----------------------------------------------------- 
        //
        //  Private Methods 
        //
        //------------------------------------------------------
        private ZipIOCentralDirectoryBlock(ZipIOBlockManager blockManager)
        { 
            _blockManager = blockManager;
        } 
 
        /// 
        /// Compare FileOffsets for LocalFileHeaders - used by Sort() routine in ParseRecord 
        /// 
        private class HeaderFileOffsetComparer : IComparer
        {
            int IComparer.Compare(object o1, object o2) 
            {
                ZipIOCentralDirectoryFileHeader h1 = o1 as ZipIOCentralDirectoryFileHeader; 
                ZipIOCentralDirectoryFileHeader h2 = o2 as ZipIOCentralDirectoryFileHeader; 
                Debug.Assert(h1 != null && h2 != null, "HeaderFileOffsetComparer: Comparing the wrong data types");
 
                // avoid boxing - don't cast long value to (IComparable)
                if (h1.OffsetOfLocalHeader > h2.OffsetOfLocalHeader)
                    return 1;
                else if (h1.OffsetOfLocalHeader < h2.OffsetOfLocalHeader) 
                    return -1;
                else 
                    return 0; 
            }
        } 

        private void ParseRecord (BinaryReader reader,
                                                        long centralDirectoryOffset,
                                                        int centralDirectoryCount, 
                                                        long expectedCentralDirectorySize)
        { 
            if (centralDirectoryCount > 0) 
            {
                // collect all headers into a local array list for sorting 
                SortedList headerList = new SortedList(centralDirectoryCount);
                ZipIOCentralDirectoryFileHeader header;
                for (int i = 0; i < centralDirectoryCount; i++)
                { 
                    header = ZipIOCentralDirectoryFileHeader.ParseRecord(reader, _blockManager.Encoding);
                    headerList.Add(header.OffsetOfLocalHeader, header); 
                } 

                if (reader.BaseStream.Position - centralDirectoryOffset > expectedCentralDirectorySize) 
                {   // it looks like a corrupted file, as we have parsed more than central directory supposed to contain
                    throw new FileFormatException(SR.Get(SRID.CorruptedData));
                }
 
                // then add to the ordered dictionary in sorted order
                foreach (ZipIOCentralDirectoryFileHeader fileHeader in headerList.Values) 
                { 
                    // at this point fileHeader.FileName is normalized using
                    // the ZipIOBlockManager.ValidateNormalizeFileName 
                    CentralDirectoryDictionary.Add(fileHeader.FileName, fileHeader);
                }

                //load central directory [digital signature] - this has nothing to 
                // do with OPC digital signing
                // this record is optional, and the function might return null 
                _centralDirectoryDigitalSignature = ZipIOCentralDirectoryDigitalSignature.ParseRecord(reader); 
            }
 
            _offset = centralDirectoryOffset;
            _dirtyFlag = false;

            Validate(expectedCentralDirectorySize); 
        }
 
        private void Validate(long expectedCentralDirectorySize) 
        {
            checked 
            {
                    // We only have information about the Compressed data size and the offset of the
                    // local headers. We do not have information about the size of the local header
                    // which varies depending on the file name and the extra field records size. 
                    // (Although we do know the expected size of the file name, there is no way to
                    // predict the extra field size, for example it might have a padding record that we use 
                    // optimize Disk IO for ZIP 64 scenarios). 
                    // We are going to make sure that Blocks do not overlap each other and do not
                    // overlap Central Directory 

                long checkedMark = 0;

                foreach (ZipIOCentralDirectoryFileHeader fileHeader in CentralDirectoryDictionary.Values) 
                {
                    if ((checkedMark == 0) && (fileHeader.OffsetOfLocalHeader != 0)) 
                    { 
                        // first block doesn't start at 0
                        throw new FileFormatException(SR.Get(SRID.CorruptedData)); 
                    }
                    else if (fileHeader.OffsetOfLocalHeader < checkedMark)
                    {
                        // the current block overlaps the previously analyzed block 
                        throw new FileFormatException(SR.Get(SRID.CorruptedData));
                    } 
 
                    // we move the checked mark up by the sum of the compressed size file name size
                    // and the fixed minimal Local file header size 
                    checkedMark += fileHeader.CompressedSize +
                                                ZipIOLocalFileHeader.FixedMinimalRecordSize +
                                                fileHeader.FileName.Length;
                } 

                // now we can ensure that that checked mark didn't reach over the start of the Central directory 
                 if (_offset < checkedMark) 
                {
                    // the central directory block overlaps the last file block 
                    throw new FileFormatException(SR.Get(SRID.CorruptedData));
                }

                 //check the total parsed size of the central directory against value declared in EOCd or ZIP64 EOCD records 
                 if (Size != expectedCentralDirectorySize)
                { 
                    // the central directory block overlaps the last file block 
                    throw new FileFormatException(SR.Get(SRID.CorruptedData));
                } 

                // we should also check ofr presence of gaps between
                //              Central Directory
                //              ZIP64 EOCD 
                //              ZIP 64 EOCD locator
                //              EOCD 
                // Zip64Eocd and Zip64EocdLocator must be either present or absent together 
                Debug.Assert(! (_blockManager.Zip64EndOfCentralDirectoryBlock.Size==0)
                                                    ^ 
                                            (_blockManager.Zip64EndOfCentralDirectoryLocatorBlock.Size==0));

                if (_blockManager.Zip64EndOfCentralDirectoryBlock.Size==0)
                { 
                    // no ZIP 64 record
                    if (_offset + expectedCentralDirectorySize != _blockManager.EndOfCentralDirectoryBlock.Offset) 
                    { 
                        throw new FileFormatException(SR.Get(SRID.CorruptedData));
                    } 
                }
                else
                {
                    // ZIP 64 records present 
                    if ((_offset + expectedCentralDirectorySize
                                        != _blockManager.Zip64EndOfCentralDirectoryBlock.Offset) || 
 
                         (_blockManager.Zip64EndOfCentralDirectoryBlock.Offset + _blockManager.Zip64EndOfCentralDirectoryBlock.Size
                                        != _blockManager.Zip64EndOfCentralDirectoryLocatorBlock.Offset) || 

                         (_blockManager.Zip64EndOfCentralDirectoryLocatorBlock.Offset + _blockManager.Zip64EndOfCentralDirectoryLocatorBlock.Size
                                        != _blockManager.EndOfCentralDirectoryBlock.Offset))
                    { 
                        throw new FileFormatException(SR.Get(SRID.CorruptedData));
                    } 
                } 
            }
        } 

        //-----------------------------------------------------
        //
        //  Private Properties 
        //
        //------------------------------------------------------ 
        private IDictionary CentralDirectoryDictionary 
        {
            get 
            {
                if (_centralDirectoryDictionary == null)
                {
                    // StringComparer.Ordinal guarantees ordinal, case-sensitive comparison for both cases. 

                    // if streaming - order is unimportant for us and we can use the cheaper Hashtable 
                    if (_blockManager.Streaming) 
                    {
 
                        // We take our order during Save() from the physical order of the elements of the
                        // block table in BlockManager.
                        _centralDirectoryDictionary = new Hashtable(_centralDirectoryDictionaryInitialSize, StringComparer.Ordinal);
                    } 
                    else
                    { 
                        // This ordered dictionary serves two purposes.  It allows hash-table lookup by file name 
                        // and it also maintains the physical order of the blocks on disk.  Like any OrderedDictionary,
                        // any of the enumerator, or integer indexer will return the items in the order that they were added. 
                        _centralDirectoryDictionary = new OrderedDictionary(_centralDirectoryDictionaryInitialSize, StringComparer.Ordinal);
                    }
                }
                return _centralDirectoryDictionary; 
            }
        } 
 
        //------------------------------------------------------
        // 
        //  Private Members
        //
        //-----------------------------------------------------
        private const int _centralDirectoryDictionaryInitialSize = 50; 

        // used in Parse 
        private static IComparer _headerOffsetComparer = new HeaderFileOffsetComparer(); 

        // This may be a HashTable (Streaming case) or an OrderedDictionary - see private property 
        // for explanation.
        private IDictionary _centralDirectoryDictionary;

        private ZipIOCentralDirectoryDigitalSignature _centralDirectoryDigitalSignature; 

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

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