File: Base\MS\Internal\IO\Zip\ZipArchive.cs
Project: wpf\src\WindowsBase.csproj (WindowsBase)
//-----------------------------------------------------------------------------
//-------------   *** 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 file="ZipArchive.cs" company="Microsoft">
//    Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// 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.Collections;
using System.Globalization;
using System.Runtime.Serialization;
using System.Windows;  
using MS.Internal.WindowsBase;
 
namespace MS.Internal.IO.Zip
{
    /// <summary>
    ///  This is the main clas of the ZIP IO internal APIs. It has 2 stsatic constructors
    /// OpenOnFile and OpenOnStream. It provides client app with ability to manipulate
    /// a Zip Archive (create, open, open/add/delete file items).
    /// </summary>
    internal sealed class ZipArchive : IDisposable
    {
        //------------------------------------------------------
        //
        //  Public Members
        //
        //------------------------------------------------------
        // None 
 
        //------------------------------------------------------
        //
        //  Internal Constructors
        //
        //------------------------------------------------------
        // None
 
        //------------------------------------------------------
        //
        // Internal API Methods (although these methods are marked as 
        // Internal, they are part of the internal ZIP IO API surface 
        //
        //------------------------------------------------------
        /// <summary>
        ///  Static constructor File based constructor. all parameteres are obvious. 
        ///  This constructor wil  open the file in requested mode, and it will not do any futher parsing.
        ///
        /// In the case of Create/CreateNew, it will also prebuild in cached in-memory 
        /// EndOfCentralDirectoryRecord (which is a minimal zip file requirement), so that if user 
        /// chooses to close right after he will get a file with just EndOfCentralDirectoryRecord.
        /// </summary>
        internal static ZipArchive OpenOnFile(string path, FileMode mode, FileAccess access, FileShare share, bool streaming)
        {
            if (mode == FileMode.OpenOrCreate || mode == FileMode.Open)
            {
                // for OpenOrCreate cases we need to check whether it is an exisiting file of size 0.
                // Files of size 0 are onsidered invalid ZipArchives. If we skip this check here, later we wouldn't be able to distinguish 
                // between brand new file being created as a result of OpenOrCreate mode, or old 0 length file being open as a result of 
                // OpenOrCreate mode.
                FileInfo fileInfo = new FileInfo(path);
            
                if (fileInfo.Exists && fileInfo.Length == 0 && (fileInfo.Attributes & FileAttributes.ReparsePoint) == 0)
                {
                    throw new FileFormatException(SR.Get(SRID.ZipZeroSizeFileIsNotValidArchive));
                }
            }
            
            ZipArchive archive = null;
            FileStream archiveStream = null;
 
            // We would like to run initialization after openning stream in try/catch block 
            // so that if anything goes wrong we can close the stream (release file handler) 
            try
            {
                archiveStream = new FileStream(path, mode, access, share, 4096, streaming);
            
                ValidateModeAccessShareStreaming(archiveStream, mode, access, share, streaming);
 
                archive = new ZipArchive(archiveStream, mode, access, streaming, true);
            }
            catch
            {
                if (archive != null)
                {
                    archive.Close();
                }
                
                if(archiveStream != null)
                {
                    archiveStream.Close();
                }
 
                throw;
            }
                    
            return archive;
        }
 
        /// <summary>
        ///  Static constructor Stream based constructor. all parameteres are obvious. 
        ///  This constructor wil not do any futher parsing.
        ///
        /// In the case of Create/CreateNew, it will also prebuild in cached in-memory 
        /// EndOfCentralDirectoryRecord (which is a minimal zip file requirement), so that if user 
        /// chooses to close right after he will get a file with just EndOfCentralDirectoryRecord.
        /// </summary>
        internal static ZipArchive OpenOnStream(Stream stream, FileMode mode, FileAccess access, bool streaming)
        {
            // we can assume FileShare.None, as there is absolutely nothing we can do 
            // about other people working with the underlying storage  
            ValidateModeAccessShareStreaming(stream, mode, access, FileShare.None, streaming);
 
            if (stream.CanSeek)
            {
                bool emptyStream = (stream.Length == 0);
 
                switch (mode)
                {
                    // for Open cases we need to check whether it is an existing file of size 0.
                    // Streams of size 0 are considered invalid ZipArchives.
                    case FileMode.Open:
                        if (emptyStream)
                        {
                            throw new FileFormatException(SR.Get(SRID.ZipZeroSizeFileIsNotValidArchive));
                        }
                        break;
 
                    // for Create cases, we need to check if the stream is empty or not
                    case FileMode.CreateNew:
                        if (!emptyStream)
                        {
                            throw new IOException(SR.Get(SRID.CreateNewOnNonEmptyStream));
                        }
                        break;
                    case FileMode.Create:
                        if (!emptyStream)
                        {
                            // discard existing data
                            stream.SetLength(0);
                        }
                        break;
                }
            }
 
            return new ZipArchive(stream, mode, access, streaming, false);
        }
 
 
        /// <summary>
        ///  This method will result in a complete parsing of the EndOfCentralDirectory 
        /// and CentralDirectory records (if it hasn't been done yet). 
        /// After that (assuming no duplicates were found). It will create in appropriate 
        /// in memory Local FileHeaders and Central Directory Headers.  
        /// </summary>
        internal ZipFileInfo AddFile(string zipFileName, CompressionMethodEnum compressionMethod, DeflateOptionEnum deflateOption)
        {
            CheckDisposed();
 
            if (_openAccess == FileAccess.Read)
            {
                throw new InvalidOperationException(SR.Get(SRID.CanNotWriteInReadOnlyMode));
            }
 
            // Validate parameteres 
            zipFileName = ZipIOBlockManager.ValidateNormalizeFileName(zipFileName);
 
            if ((compressionMethod != CompressionMethodEnum.Stored) && 
                (compressionMethod != CompressionMethodEnum.Deflated))
            {
                throw new ArgumentOutOfRangeException("compressionMethod");            
            }
 
            // non-contiguous range requires more complex test
            if (deflateOption < DeflateOptionEnum.Normal || (
                deflateOption > DeflateOptionEnum.SuperFast && deflateOption != DeflateOptionEnum.None))
            {
                throw new ArgumentOutOfRangeException("deflateOption");            
            }
 
            // Check for duplicates , 
            if (FileExists(zipFileName))
            {
                throw new System.InvalidOperationException(SR.Get(SRID.AttemptedToCreateDuplicateFileName));            
            }
                
            // Create Local File Block through Block Manager 
            ZipIOLocalFileBlock fileBlock = _blockManager.CreateLocalFileBlock(zipFileName, compressionMethod, deflateOption);
 
            //build new ZipFileInfo and add reference to the collection, so we can keep track of the instances of the ZipFileInfo, 
            // that were given out  and invalidate any collection that was returned on GetFiles calls
            ZipFileInfo zipFileInfo = new ZipFileInfo(this, fileBlock);
            ZipFileInfoDictionary.Add(zipFileInfo.Name, zipFileInfo);
            
            return zipFileInfo; 
        }
 
        /// <summary>
        ///  This method will result in a complete parsing of the EndOfCentralDirectory 
        /// and CentralDirectory records (if it hasn't been done yet). 
        /// After that (assuming the file was found). It will parse the apropriate local file block 
        /// header and data descriptor (if present).
        /// </summary>
        internal ZipFileInfo GetFile(string zipFileName)
        {
            CheckDisposed();        
 
            if (_openAccess == FileAccess.Write)
            {
                throw new InvalidOperationException(SR.Get(SRID.CanNotReadInWriteOnlyMode));
            }
 
            // Validate parameteres 
            zipFileName = ZipIOBlockManager.ValidateNormalizeFileName(zipFileName);
 
            // try to get it from the ZipFileInfo dictionary 
            if (ZipFileInfoDictionary.Contains(zipFileName))
            {
                // this ZipFileInfo was already built through AddFile or GetFile(s)
                // we have this cached 
                return (ZipFileInfo)(ZipFileInfoDictionary[zipFileName]);
            }
            else
            {
                // we need to check whether it is present in the central directory 
                if (!FileExists(zipFileName))
                {
                    throw new InvalidOperationException(SR.Get(SRID.FileDoesNotExists));                
                }
 
                // Load Local File Block through Block Manager 
                ZipIOLocalFileBlock fileBlock = _blockManager.LoadLocalFileBlock(zipFileName);
 
                // build new ZipFileInfo and add reference to the collection, so we can keep track of the instances of the ZipFileInfo, 
                // that were given out  and invalidate any collection that was returned on GetFiles calls
                ZipFileInfo zipFileInfo = new ZipFileInfo(this, fileBlock);
 
                //this should invalidate any outstanding collections                 
                ZipFileInfoDictionary.Add(zipFileInfo.Name, zipFileInfo);
 
                return zipFileInfo;
            }
        }
 
        /// <summary>
        ///  This method will result in a complete parsing of the EndOfCentralDirectory 
        /// and CentralDirectory records (if it hasn't been done yet). 
        /// After that it will check whether central directory contains the file. 
        /// It will not attempt the parsing of the local file headers / descriptors. 
        /// </summary>
        internal bool FileExists (string zipFileName)
        {
            CheckDisposed();        
 
            // Validate parameteres 
            zipFileName = ZipIOBlockManager.ValidateNormalizeFileName(zipFileName);
            
            return _blockManager.CentralDirectoryBlock.FileExists(zipFileName);
        }
 
 
        /// <summary>
        ///  This method will result in a complete parsing of the EndOfCentralDirectory 
        /// and CentralDirectory records (if it hasn't been done yet). 
        /// After that it will check whether central directory contains the file. 
        /// If it is present it will parse local fileheader, and remove their in memory 
        /// representation
        /// </summary>
        internal void DeleteFile (string zipFileName)
        {
            CheckDisposed();        
 
            if (_openAccess == FileAccess.Read)
            {
                throw new InvalidOperationException(SR.Get(SRID.CanNotWriteInReadOnlyMode));
            }
 
            // Validate parameteres 
            zipFileName = ZipIOBlockManager.ValidateNormalizeFileName(zipFileName);
 
            if (FileExists(zipFileName)) // is it in central Directory ? 
            {
                ZipFileInfo fileInfoToBeDeleted  = GetFile(zipFileName);
 
                //this should invalidate any outstanding collections 
                // and update central directory status as appropriate
                ZipFileInfoDictionary.Remove(zipFileName); 
 
                //this should remove the local file block 
                // from the blockManager's collection 
                _blockManager.RemoveLocalFileBlock(fileInfoToBeDeleted.LocalFileBlock);
            }
        }
 
        /// <summary>
        ///  This method will result in a complete parsing of the EndOfCentralDirectory 
        /// and CentralDirectory records (if it hasn't been done yet). 
        /// After that it will go through allfiles in the central directory and parse their 
        /// local headers and desciptors one by one. 
        /// </summary>
        internal ZipFileInfoCollection GetFiles()
        {
            CheckDisposed();        
 
            if (_openAccess == FileAccess.Write)
            {
                throw new InvalidOperationException(SR.Get(SRID.CanNotReadInWriteOnlyMode));
            }
 
            // We need to scan through the central Directory, and for each file 
            // call GetFile(fileName), which will result in adding missing (not loaded) 
            // information to the ZipFileInfoDictionary.
            foreach(string fileName in _blockManager.CentralDirectoryBlock.GetFileNamesCollection())
            {
                GetFile(fileName);  // fileName must be validated and normalized at this 
                                            // point by the central directory parsing routine 
            }
            return new ZipFileInfoCollection(ZipFileInfoDictionary.Values);
        }
        
        /// <summary>
        ///  This method will result in a complete Flushing of any outstanding data in buffers and 
        /// any streams ever returned by the GetStream calls.This call results in Archive file that 
        /// has a completely valid state. If application were to crash right afte the Flush is complete, 
        /// the resulting files would be a "valid" Zip archive  
        /// </summary>
        internal void Flush()
        {
            CheckDisposed();        
            _blockManager.Save(false);
        }
 
        /// <summary>
        /// Results in a complete Flush of all the outstanding buffers and closing/disposing all of the objects
        /// ZipFileInfo, Streams ZipArchive.
        /// </summary>        
        internal void Close()
        {
            Dispose();
        }
 
        /// <summary>
        /// Results in a complete Flush of all the outstanding buffers and closing/disposing all of the objects
        /// ZipFileInfo, Streams ZipArchive.
        /// </summary>                
        public void Dispose()
        {
            Dispose(true);
            // GC.SuppressFinalize(this);  // Because this class is sealed and there is no Finalizer, 
                                                        // there is no need for this call. Leaving it in case we decide to unseal it  
        }
 
        /// <summary>
        /// Throw if version needed to extract is not supported
        /// </summary>
        /// <param name="version">version to inspect</param>
        static internal void VerifyVersionNeededToExtract(UInt16 version)
        {
            // strictly enforce this list
            switch (version)
            {
                case (UInt16)ZipIOVersionNeededToExtract.StoredData: break;
                case (UInt16)ZipIOVersionNeededToExtract.VolumeLabel: break;
                case (UInt16)ZipIOVersionNeededToExtract.DeflatedData: break;
                case (UInt16)ZipIOVersionNeededToExtract.Zip64FileFormat: break;
                default:
                    throw new NotSupportedException(SR.Get(SRID.NotSupportedVersionNeededToExtract)); 
            }
        }
 
        //------------------------------------------------------
        //
        //  Internal Properties
        //
        //------------------------------------------------------
        /// <summary>
        /// Read only property, that returns the value of FileAccess that 
        /// was passed into the OpenOnFile or OpenOnStream call  
        /// </summary>                
        internal FileAccess OpenAccess
        {
            get
            {
                CheckDisposed();              
                return _openAccess;
            }
        }
 
//  This functionality commented out in order to comply with FX Cop rule 
//  AvoidUncalledPrivateCode. However, because of a chance that this functionality 
//  might eventually get public exposure we would like to keep this code around
#if ZIP_IO_PUBLIC

        /// <summary>
        /// Returns Comment field from the End Of Central Directory Record. 
        /// Therefore, call to this property might result in some parsing. 
        /// If the End Of Central Directory isn't parsed yet, it will be as a 
        /// result of querying this property.  
        /// </summary>                
        internal string Comment
        {
            get
            {
                CheckDisposed();  
                return _blockManager.EndOfCentralDirectoryBlock.Comment;
            }
        }
#endif
 
 
        //------------------------------------------------------
        //
        // Internal NON API Methods (these methods are marked as 
        // Internal, and they are trully internal and not the part of the 
        // internal ZIP IO API surface 
        //
        //------------------------------------------------------
 
        //------------------------------------------------------
        //
        //  Private Constructors
        //
        //------------------------------------------------------
        /// <summary>
        /// This private constructor isonly supposed to be called by the 
        /// OpenOnFile and OpenOnStream static members. 
        /// </summary>         
        /// <param name="archiveStream"></param>
        /// <param name="mode"></param>
        /// <param name="access"></param>
        /// <param name="streaming"></param>
        /// <param name="ownStream">true if this class is responsible for closing the archiveStream</param>
        private ZipArchive(Stream archiveStream, FileMode mode, FileAccess access, bool streaming, bool ownStream)
        {
            // as this contructor is only called from the static member 
            // all checks should have been done before 
 
            _blockManager = new ZipIOBlockManager(archiveStream, streaming, ownStream);
 
            _openMode = mode;
            _openAccess = access;
 
            // In case of "create" we also need to create at least an end of Central Directory Record.
            // For FileMode OpenOrCreate we use stream Length to distinguish open and create scenarios.
            // Implications of this decision is that existing file of size 0 opened in OpenOrCreate Mode 
            // will be treated as a newly/created file.
            if ((_openMode == FileMode.CreateNew) ||
               (_openMode == FileMode.Create) ||
               ((_openMode == FileMode.OpenOrCreate) && archiveStream.Length == 0))
            {
                _blockManager.CreateEndOfCentralDirectoryBlock();
            }
            else
            {
                _blockManager.LoadEndOfCentralDirectoryBlock();
            }
        }
 
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
 
        /// <summary>
        /// Dispose(bool)
        /// </summary>
        /// <param name="disposing"></param>
        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (!_disposedFlag)
                {
                    try
                    {
                        if (_blockManager != null)
                        {
                            // allow this in Debug mode to catch any place where we accidentally
                            // make something dirty when we are read-only
#if !DEBUG
                            if (_openAccess == FileAccess.ReadWrite || _openAccess == FileAccess.Write)
#endif
                            {
                                _blockManager.Save(true);
                            }
 
                            ((IDisposable)_blockManager).Dispose();
                        }
                    }
                    finally
                    {
                        _disposedFlag = true;
                    }
                }
            }
        }
 
        /// <summary>
        /// This is function is called by the OpenOnFile and OpenOnStream in order
        /// to validate parameteresgiven to those functions. The combinations of valid
        /// parameters is quite complex and not obvious, so after basic range checks, 
        /// it is actually using a lookup table to answer the question whether the given 
        /// parameter  combination is valid or not.
        /// </summary>   
        static private void ValidateModeAccessShareStreaming(Stream stream, FileMode mode, FileAccess access, FileShare share, bool streaming)
        {
            if (stream == null)        
            {
                throw new ArgumentNullException("stream");
            }
 
            ////////////
            // filter out values that are out of enum ranges first 
            ////////////
            ValidateModeAccessShareValidEnums(mode, access, share);
 
            ////////////            
            // filter out values that are not supported regardless of other parameters 
            // but still validate enum members 
            ////////////            
            ValidateModeAccessShareSupportedEnums(mode, share);
 
            ////////////
            // let's makes sure that given stream is capable of supporting required functionality 
            ////////////            
            ValidateModeAccessStreamStreamingCombinations(stream, access, streaming);        
 
            ////////////
            //let's make sure that comnbintaion of mode, access,share,,streaming 
            // parameters is supported 
            ////////////            
            int intMode =  Convert.ToInt32(mode, CultureInfo.InvariantCulture);
            int intAccess = Convert.ToInt32(access, CultureInfo.InvariantCulture);
            int intShare = Convert.ToInt32(share, CultureInfo.InvariantCulture);
            int intStreaming = Convert.ToInt32(streaming, CultureInfo.InvariantCulture);
            for(int i=0; i<_validOpenParameters.GetLength(0); i++)
            {
                if ((_validOpenParameters [i,0] == intMode) &&
                    (_validOpenParameters [i,1] == intAccess) &&
                    (_validOpenParameters [i,2] == intShare) &&
                    (_validOpenParameters [i,3] == intStreaming))
                {
                    return;
                }
            }
 
            throw new ArgumentException(SR.Get(SRID.UnsupportedCombinationOfModeAccessShareStreaming));
        }
 
        static private void ValidateModeAccessStreamStreamingCombinations(Stream stream, FileAccess access, bool streaming)
        {
            ////////////
            // let's makes sure that given stream is capable of supporting required functionality 
            ////////////            
            if ((access== FileAccess.Read || access == FileAccess.ReadWrite) && !stream.CanRead)
            {
                throw new ArgumentException(SR.Get(SRID.CanNotReadDataFromStreamWhichDoesNotSupportReading));
            }
 
            // if user want to be able to write stream needs to support it 
            if ((access == FileAccess.Write || access == FileAccess.ReadWrite) && !stream.CanWrite)
            {
                throw new ArgumentException(SR.Get(SRID.CanNotWriteDataToStreamWhichDoesNotSupportWriting));
            }            
 
            // if user works in non-streaming mode we need to Seek on underlying stream 
            if (! streaming && !stream.CanSeek)
            {
                throw new ArgumentException(SR.Get(SRID.CanNotOperateOnStreamWhichDoesNotSupportSeeking));
            }            
        }
        
        static private void ValidateModeAccessShareSupportedEnums(FileMode mode, FileShare share)
        {
            ////////////            
            // filter out values that are not supported regardless of other parameters 
            // but still validate enum members 
            ////////////            
             if (mode == FileMode.Append || mode == FileMode.Truncate)
            {
                throw new NotSupportedException(SR.Get(SRID.TruncateAppendModesNotSupported));
            }
            else if (share != FileShare.Read && share != FileShare.None)
            {
                // later as we get to streaming other FileShare values will be supported too 
                throw new NotSupportedException (SR.Get(SRID.OnlyFileShareReadAndFileShareNoneSupported));   
            }
        }
        
        static private void ValidateModeAccessShareValidEnums(FileMode mode, FileAccess access, FileShare share)
        {
            ////////////
            // filter out values that are out of enum ranges first 
            ////////////
            if ((mode != FileMode.Append) && (mode != FileMode.Create) && (mode != FileMode.CreateNew) && (mode != FileMode.Open)
                && (mode != FileMode.OpenOrCreate) && (mode != FileMode.Truncate))
            {
                throw new ArgumentOutOfRangeException("mode");            
            }
            else if ((access != FileAccess.Read) && (access != FileAccess.ReadWrite) && (access != FileAccess.Write))
            {
                throw new ArgumentOutOfRangeException("access");            
            }
            else if ((share != FileShare.Delete) && (share != FileShare.Inheritable) && (share != FileShare.None) &&
                (share != FileShare.Read) && (share != FileShare.ReadWrite) && (share != FileShare.Write))
            {
                throw new ArgumentOutOfRangeException("share");            
            }
        }
        
        /// <summary>
        /// Throws exception if object already Disposed/Closed. 
        /// </summary> 
        private void CheckDisposed()
        {
            if (_disposedFlag)
            {
                throw new ObjectDisposedException(null, SR.Get(SRID.ZipArchiveDisposed));
            }
        }
 
        //------------------------------------------------------
        //
        //  Private Properties
        //
        //------------------------------------------------------
        /// <summary>
        /// This private property is used as a mean to achieve lazy memory allocation for the 
        /// hashtable that maintains a cahce of the returned instrances of ZipFileInfo(s).
        /// This hashtable uses file names as keys in the case insensitive and culture invariant fashion 
        /// </summary>   
        private IDictionary ZipFileInfoDictionary 
        {
            get
            {
                if (_zipFileInfoDictionary == null)
                {
                                // ordinal case sensitive comparison 
                    _zipFileInfoDictionary = new Hashtable(_zipFileInfoDictionaryInitialSize, StringComparer.Ordinal); 
                }
                return _zipFileInfoDictionary;
            }
        }
        
        //------------------------------------------------------
        //
        //  Private Fields 
        //
        //------------------------------------------------------        
 
        // This 2 dimensional table is used by the ValidateModeAccessShareStreaming 
        // function as a set of valid parameter combinations 
        static private int[,] _validOpenParameters = new int[,]
        {   // FileMode                             // FileAccess                     / FileShare                     // streaming 
            {(int)FileMode.Create,             (int)FileAccess.Write,        (int)FileShare.None,        1},
            {(int)FileMode.Create,             (int)FileAccess.Write,        (int)FileShare.Read,        1},
            {(int)FileMode.Create,             (int)FileAccess.ReadWrite, (int)FileShare.None,        0},
            {(int)FileMode.CreateNew,       (int)FileAccess.Write,        (int)FileShare.None,        1},
            {(int)FileMode.CreateNew,       (int)FileAccess.Write,        (int)FileShare.Read,        1},
            {(int)FileMode.CreateNew,       (int)FileAccess.ReadWrite, (int)FileShare.None,        0},
            {(int)FileMode.Open,               (int)FileAccess.Read,         (int)FileShare.None,        1},
            {(int)FileMode.Open,               (int)FileAccess.Read,         (int)FileShare.None,        0},
            {(int)FileMode.Open,               (int)FileAccess.Read,         (int)FileShare.Read,        1},
            {(int)FileMode.Open,               (int)FileAccess.Read,         (int)FileShare.Read,        0},
            {(int)FileMode.Open,               (int)FileAccess.Read,         (int)FileShare.Write,        1},
            {(int)FileMode.Open,               (int)FileAccess.Read,         (int)FileShare.ReadWrite, 1},
            {(int)FileMode.Open,               (int)FileAccess.ReadWrite,  (int)FileShare.None,        0},
            {(int)FileMode.OpenOrCreate,  (int)FileAccess.ReadWrite,  (int)FileShare.None,        0}
        };
 
        // modes that were used for openning (OpenOnStream or OpenOnFile),
        // there is no way to change these values after class is constructed 
        private FileMode _openMode; 
        private FileAccess  _openAccess;
 
        private bool _disposedFlag;
 
        // reference to the ZipIOBlockManager, this reference is instantiated as 
        // a part of the OpenOnFile/OpenOnStream contruction 
        private ZipIOBlockManager _blockManager;
 
        // this is a Dictionary of all the ZipFileInfos that were given out.
        // It uses file name as key in case insensitive and culture invariant fashion.
        // all members of the class are supposed to use this field indirectly through 
        // ZipFileInfoDictionary property, as ZipFileInfoDictionary is respnsible 
        // for lazy allocation of the hashtable.
        private IDictionary _zipFileInfoDictionary;
        private const int _zipFileInfoDictionaryInitialSize = 50;  
    }
}