Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Base / MS / Internal / IO / Zip / ZipIOBlockManager.cs / 1305600 / ZipIOBlockManager.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. // 10/21/2005: brucemac: Apply security mitigations // //----------------------------------------------------------------------------- using System; using System.Diagnostics; using System.IO; using System.Text; using System.Collections; using System.Globalization; using System.Runtime.Serialization; using System.Windows; using MS.Internal.IO.Packaging; // for PackagingUtilities using MS.Internal.WindowsBase; namespace MS.Internal.IO.Zip { ////// This is the main class of the actual ZIP IO implementation. It is primary responsibility /// is to maintain the map and status of the parsed and loaded areas(blocks) of the file. /// It is also supports manipulating this map (adding and deleting blocks) /// internal class ZipIOBlockManager : IDisposable, IEnumerable { //------------------------------------------------------ // // Public Methods // //----------------------------------------------------- public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } IEnumerator IEnumerable.GetEnumerator() { CheckDisposed(); return _blockList.GetEnumerator(); } //------------------------------------------------------ // // Internal Properties // //------------------------------------------------------ ////// This property returns the status of whether Central directory is loaded or not. /// This property is rarely used, as most clients will just ask for CentralDirectoryBlock /// and oif it isn't loaded it will be. /// The only reason to use IsCentralDirectoryBlockLoaded property is to differentiate /// scenarios in which some optimization is possible, if central directory isn't loaded yet. /// internal bool IsCentralDirectoryBlockLoaded { get { CheckDisposed(); return (_centralDirectoryBlock != null); } } ////// This property returns the CentralDirectoryBlock and provides lazy load /// fuinctionality. This isthe only way other classes can access information /// from the Central Directory Block /// internal ZipIOCentralDirectoryBlock CentralDirectoryBlock { get { CheckDisposed(); if (_centralDirectoryBlock == null) { // figure out if we are in ZIP64 mode or not if (Zip64EndOfCentralDirectoryBlock.TotalNumberOfEntriesInTheCentralDirectory > 0) { LoadCentralDirectoryBlock(); } else { // We need to be aware of the special case of empty Zip Archive // with a single record : End Of Central directory //In such cases we should create new CentralDirectoryBlock CreateCentralDirectoryBlock(); } } return _centralDirectoryBlock; } } ////// This property returns the Zip64EndOfCentralDirectoryBlock and provides lazy load /// fuinctionality. This is the only way other classes can access information /// from the Zip64EndOfCentralDirectoryBlock /// internal ZipIOZip64EndOfCentralDirectoryBlock Zip64EndOfCentralDirectoryBlock { get { CheckDisposed(); if (_zip64EndOfCentralDirectoryBlock == null) { CreateLoadZip64Blocks(); } return _zip64EndOfCentralDirectoryBlock; } } ////// This property returns the Zip64EndOfCentralDirectoryLocatorBlock and provides lazy load /// fuinctionality. This is the only way other classes can access information /// from the Zip64EndOfCentralDirectoryLocator Block /// internal ZipIOZip64EndOfCentralDirectoryLocatorBlock Zip64EndOfCentralDirectoryLocatorBlock { get { CheckDisposed(); if (_zip64EndOfCentralDirectoryLocatorBlock == null) { CreateLoadZip64Blocks(); } return _zip64EndOfCentralDirectoryLocatorBlock; } } ////// This property returns the CentralDirectoryBlock and provides lazy load /// fuinctionality. This is the only way other classes can access information /// from the Central Directory Block /// internal ZipIOEndOfCentralDirectoryBlock EndOfCentralDirectoryBlock { get { CheckDisposed(); if (_endOfCentralDirectoryBlock == null) { LoadEndOfCentralDirectoryBlock(); } return _endOfCentralDirectoryBlock; } } internal Stream Stream { get { CheckDisposed(); return _archiveStream; } } internal bool Streaming { get { CheckDisposed(); return _openStreaming; } } internal BinaryReader BinaryReader { get { CheckDisposed(); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); if (_binaryReader == null) { _binaryReader = new BinaryReader(Stream, Encoding); } return _binaryReader; } } internal BinaryWriter BinaryWriter { get { CheckDisposed(); if (_binaryWriter == null) { _binaryWriter = new BinaryWriter(Stream, Encoding); } return _binaryWriter; } } internal Encoding Encoding { get { CheckDisposed(); return _encoding; } } internal bool DirtyFlag { set { CheckDisposed(); _dirtyFlag = value; } get { CheckDisposed(); return _dirtyFlag; } } static internal int MaxFileNameSize { get { return UInt16.MaxValue; } } //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ internal void CreateEndOfCentralDirectoryBlock() { CheckDisposed(); // Prevent accidental call if underlying stream is non-empty since // any legal zip archive contains an EOCD block. Debug.Assert(_openStreaming || _archiveStream.Length == 0); // Disallow multiple calls. Debug.Assert(_endOfCentralDirectoryBlock == null); // construct Block find it and parse it long blockOffset = 0; // this will be updated later _endOfCentralDirectoryBlock = ZipIOEndOfCentralDirectoryBlock.CreateNew(this, blockOffset); // this will add a block to the tail AppendBlock(_endOfCentralDirectoryBlock); DirtyFlag = true; } internal void LoadEndOfCentralDirectoryBlock() { Debug.Assert(_endOfCentralDirectoryBlock == null); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); // construct Block find it and parse it _endOfCentralDirectoryBlock = ZipIOEndOfCentralDirectoryBlock.SeekableLoad(this); //ask block manager to MAP this block MapBlock(_endOfCentralDirectoryBlock); } internal ZipIOLocalFileBlock CreateLocalFileBlock(string zipFileName, CompressionMethodEnum compressionMethod, DeflateOptionEnum deflateOption) { CheckDisposed(); // we are guaranteed uniqueness at this point , so let's just add a // block at the end of the file, just before the central directory // construct Block find it and parse it // STREAMING Mode: // NOTE: _blockList is NOT in offset order except the last four blocks // (CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD) ZipIOLocalFileBlock localFileBlock = ZipIOLocalFileBlock.CreateNew(this, zipFileName, compressionMethod, deflateOption); InsertBlock(CentralDirectoryBlockIndex, localFileBlock); CentralDirectoryBlock.AddFileBlock(localFileBlock); DirtyFlag = true; return localFileBlock; } internal ZipIOLocalFileBlock LoadLocalFileBlock(string zipFileName) { CheckDisposed(); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); Debug.Assert(CentralDirectoryBlock.FileExists(zipFileName)); // it must be in the central directory // construct Block find it and parse it ZipIOLocalFileBlock localFileBlock = ZipIOLocalFileBlock.SeekableLoad(this, zipFileName); MapBlock(localFileBlock); return localFileBlock; } internal void RemoveLocalFileBlock(ZipIOLocalFileBlock localFileBlock) { CheckDisposed(); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); Debug.Assert(localFileBlock != null, " At this point local File block must be preloaded"); Debug.Assert(CentralDirectoryBlock.FileExists(localFileBlock.FileName), " At this point local File block must be mapped in central directory"); // remove it from our list _blockList.Remove(localFileBlock); // remove this from Central Directory CentralDirectoryBlock.RemoveFileBlock(localFileBlock.FileName); DirtyFlag = true; // at this point we can Dispose it to make sure that any calls // to this file block through outstanding indirect references will result in object Disposed exception localFileBlock.Dispose(); } internal void MoveData(long moveBlockSourceOffset, long moveBlockTargetOffset, long moveBlockSize) { Debug.Assert(moveBlockSize >=0); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); if ((moveBlockSize ==0) || (moveBlockSourceOffset == moveBlockTargetOffset)) { //trivial empty move case return; } checked { byte[] tempBuffer = new byte [Math.Min(moveBlockSize,0x100000)]; // min(1mb, requested block size) long bytesMoved = 0; while(bytesMoved < moveBlockSize) { long subBlockSourceOffset; long subBlockTargetOffset; int subBlockSize = (int)Math.Min((long)tempBuffer.Length, moveBlockSize - bytesMoved); if (moveBlockSourceOffset > moveBlockTargetOffset) { subBlockSourceOffset = moveBlockSourceOffset + bytesMoved; subBlockTargetOffset = moveBlockTargetOffset + bytesMoved; } else { subBlockSourceOffset = moveBlockSourceOffset + moveBlockSize - bytesMoved - subBlockSize; subBlockTargetOffset = moveBlockTargetOffset + moveBlockSize - bytesMoved - subBlockSize; } _archiveStream.Seek(subBlockSourceOffset, SeekOrigin.Begin); int bytesRead = PackagingUtilities.ReliableRead(_archiveStream, tempBuffer, 0, subBlockSize); if (bytesRead != subBlockSize) { throw new FileFormatException(SR.Get(SRID.CorruptedData)); } _archiveStream.Seek(subBlockTargetOffset, SeekOrigin.Begin); _archiveStream.Write(tempBuffer, 0, subBlockSize); checked{bytesMoved += subBlockSize;} } } } ////// Save - stream level /// /// /// closing or flushing internal void SaveStream(ZipIOLocalFileBlock blockRequestingFlush, bool closingFlag) { // Prevent recursion when propagating Flush or Disposed to our minions // because ZipIOFileItemStream.Flush calls us. if (_propagatingFlushDisposed) return; else _propagatingFlushDisposed = true; // enter first time try { // redirect depending on our mode if (_openStreaming) { StreamingSaveStream(blockRequestingFlush, closingFlag); } else SaveContainer(false); } finally { // all done so restore state _propagatingFlushDisposed = false; } } ////// Save - container level /// /// true if closing, false if flushing internal void Save(bool closingFlag) { CheckDisposed(); // Prevent recursion when propagating Flush or Disposed to our minions // because ZipIOFileItemStream.Flush calls us. if (_propagatingFlushDisposed) return; else _propagatingFlushDisposed = true; // enter first time try { // redirect depending on our mode if (_openStreaming) { StreamingSaveContainer(closingFlag); } else SaveContainer(closingFlag); } finally { // all done so restore state _propagatingFlushDisposed = false; } } ////// Constructor /// /// stream we operate on /// /// true if we own the stream and are expected to close it when we are disposed internal ZipIOBlockManager(Stream archiveStream, bool streaming, bool ownStream) { Debug.Assert(archiveStream != null); _archiveStream = archiveStream; _openStreaming = streaming; _ownStream = ownStream; if (streaming) { // wrap the archive stream in a WriteTimeStream which keeps track of current position _archiveStream = new WriteTimeStream(_archiveStream); } else if (archiveStream.Length > 0) { // for non-empty stream we need to map the whole stream into a raw data block // which helps keep track of shifts and dirty areas ZipIORawDataFileBlock rawBlock = ZipIORawDataFileBlock.Assign(this, 0, archiveStream.Length); _blockList.Add(rawBlock); } } internal static UInt32 ToMsDosDateTime(DateTime dateTime) { UInt32 result = 0; result |= (((UInt32)dateTime.Second) /2) & 0x1F; // seconds need to be divided by 2 // as they stored in 5 bits result |= (((UInt32)dateTime.Minute) & 0x3F) << 5; result |= (((UInt32)dateTime.Hour) & 0x1F) << 11; result |= (((UInt32)dateTime.Day) & 0x1F) << 16; result |= (((UInt32)dateTime.Month) & 0xF) << 21; result |= (((UInt32)(dateTime.Year - 1980)) & 0x7F) << 25; return result; } internal static DateTime FromMsDosDateTime(UInt32 dosDateTime) { int seconds = (int)((dosDateTime & 0x1F) << 1); // seconds need to be multiplied by 2 // as they stored in 5 bits int minutes = (int)((dosDateTime >> 5) & 0x3F); int hours = (int)((dosDateTime >> 11) & 0x1F); int day = (int)((dosDateTime >> 16) & 0x1F); int month =(int)((dosDateTime >> 21) & 0xF); int year = (int)(1980 + ((dosDateTime >> 25) & 0x7F)); //this will throw if parameters are out of range return new DateTime(year, month,day,hours,minutes,seconds); } ////// This is standard way to normalize Zip File Item names. At this point we only /// getting rid of the spaces. The Exists calls are responsible or making sure /// that they check for uniqueness in a case insensitive manner. It is up to the /// higher levels to add stricter restrictions like URI character set, and so on. /// static internal string ValidateNormalizeFileName(string zipFileName) { // Validate parameteres if (zipFileName == null) { throw new ArgumentNullException("zipFileName"); } if (zipFileName.Length > ZipIOBlockManager.MaxFileNameSize) { throw new ArgumentOutOfRangeException("zipFileName"); } zipFileName = zipFileName.Trim(); if (zipFileName.Length < 1)//it must be at least one character { throw new ArgumentOutOfRangeException("zipFileName"); } //Based on the Appnote : // << The path stored should not contain a drive or device letter, or a leading slash. >> return zipFileName; } //----------------------------------------------------- // Internal helper CopyBytes functions for storing data into a byte[] // it is a similar to a BinaryWriter , but not for streams but ratrher // for byte[] // These functiona used in the Extra field parsing, as that functionality is buit // in terms of byte[] not streams //----------------------------------------------------- internal static int CopyBytes(Int16 value, byte[] buffer, int offset) { Debug.Assert(checked(buffer.Length-offset) >= sizeof(Int16)); byte[] tempBuffer = BitConverter.GetBytes(value); Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); return offset + tempBuffer.Length; } internal static int CopyBytes(Int32 value, byte[] buffer, int offset) { Debug.Assert(checked(buffer.Length-offset) >= sizeof(Int32)); byte[] tempBuffer = BitConverter.GetBytes(value); Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); return offset + tempBuffer.Length; } internal static int CopyBytes(Int64 value, byte[] buffer, int offset) { Debug.Assert(checked(buffer.Length-offset) >= sizeof(Int64)); byte[] tempBuffer = BitConverter.GetBytes(value); Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); return offset + tempBuffer.Length; } internal static int CopyBytes(UInt16 value, byte[] buffer, int offset) { Debug.Assert(checked(buffer.Length-offset) >= sizeof(UInt16)); byte[] tempBuffer = BitConverter.GetBytes(value); Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); return offset + tempBuffer.Length; } internal static int CopyBytes(UInt32 value, byte[] buffer, int offset) { Debug.Assert(checked(buffer.Length-offset) >= sizeof(UInt32)); byte[] tempBuffer = BitConverter.GetBytes(value); Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); return offset + tempBuffer.Length; } internal static int CopyBytes(UInt64 value, byte[] buffer, int offset) { Debug.Assert(checked(buffer.Length-offset) >= sizeof(UInt64)); byte[] tempBuffer = BitConverter.GetBytes(value); Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); return offset + tempBuffer.Length; } internal static UInt64 ConvertToUInt64(UInt32 loverAddressValue, UInt32 higherAddressValue) { return checked((UInt64)loverAddressValue + (((UInt64)higherAddressValue) << 32)); } internal static ZipIOVersionNeededToExtract CalcVersionNeededToExtractFromCompression (CompressionMethodEnum compression) { switch (compression) { case CompressionMethodEnum.Stored: return ZipIOVersionNeededToExtract.StoredData; case CompressionMethodEnum.Deflated: return ZipIOVersionNeededToExtract.DeflatedData; default: throw new NotSupportedException(); // Deflated64 this is OFF } } ////// This is the common Pre Save notiofication handler for /// RawDataFile Block and File Item Stream /// It makes assumption that the overlap generally start coming in at the beginning of a /// large disk image, so we should only try to cache cache overlaped data in the prefix /// of the disk block /// Block can also return a value indicating whether PreSaveNotification should be extended to the blocks that are positioned after /// it in the Block List. For example, if block has completely handled PreSaveNotification in a way that it cached the whole area that /// was in danger (of being overwritten) it means that no blocks need to worry about this anymore. After all no 2 blocks should have /// share on disk buffers. Another scenario is when block can determine that area in danger is positioned before the block's on disk /// buffers; this means that all blocks that are positioned later in the block list do not need to worry about this PreSaveNotification /// as their buffers should be positioned even further alone in the file. /// internal static PreSaveNotificationScanControlInstruction CommonPreSaveNotificationHandler( Stream stream, long offset, long size, long onDiskOffset, long onDiskSize, ref SparseMemoryStream cachePrefixStream) { checked { Debug.Assert(size >=0); Debug.Assert(offset >=0); Debug.Assert(onDiskSize >=0); Debug.Assert(onDiskOffset >=0); // trivial request if (size == 0) { // The area being overwritten is of size 0 so there is no need to notify any blocks about this. return PreSaveNotificationScanControlInstruction.Stop; } if (cachePrefixStream != null) { // if we have something in cache prefix buffer we only should check whatever tail data isn't cached checked{onDiskOffset += cachePrefixStream.Length;} checked{onDiskSize -= cachePrefixStream.Length;} Debug.Assert(onDiskSize >=0); } if (onDiskSize == 0) { // the raw data block happened to be fully cached // in this case (onDiskSize==0) can not be used as a reliable indicator of the position of the // on disk buffer relative to the other; it is just an indicator of an empty buffer which might have a meaningless offset // that shouldn't be driving any decisions return PreSaveNotificationScanControlInstruction.Continue; } // we need to first find out if the raw data that isn't cached yet overlaps with any disk space // that is about to be overriden long overlapBlockOffset; long overlapBlockSize; PackagingUtilities.CalculateOverlap(onDiskOffset, onDiskSize, offset, size , out overlapBlockOffset, out overlapBlockSize); if (overlapBlockSize <= 0) { // No overlap , we can ignore this message. // In addition to that, if (onDiskOffset > offset) it means that, given the fact that all blocks after // the current one will have even larger offsets, they couldn't possibly overlap with (offset ,size ) chunk . return (onDiskOffset > offset) ? PreSaveNotificationScanControlInstruction.Stop : PreSaveNotificationScanControlInstruction.Continue; } // at this point we have an overlap, we need to read the data that is overlapped // and merge it with whatever we already have in cache // let's figure out the part that isn't cached yet, and needs to be long blockSizeToCache; checked { blockSizeToCache = overlapBlockOffset + overlapBlockSize - onDiskOffset; } Debug.Assert(blockSizeToCache >0); // there must be a non empty block at this point that needs to be cached // We need to ensure that we do have a place to store this data if (cachePrefixStream == null) { cachePrefixStream = new SparseMemoryStream(_lowWaterMark, _highWaterMark); } else { // if we already have some cached prefix data we have to make sure we are // appending new data tro the tail of the already cached chunk cachePrefixStream.Seek(0, SeekOrigin.End); } stream.Seek(onDiskOffset, SeekOrigin.Begin); long bytesCopied = PackagingUtilities.CopyStream(stream, cachePrefixStream, blockSizeToCache, 4096); if (bytesCopied != blockSizeToCache) { throw new FileFormatException(SR.Get(SRID.CorruptedData)); } // if the contdition below is true it means that, given the fact that all blocks after // the current one will have even larger offsets, they couldn't possibly overlap with (offset ,size ) chunk return ((onDiskOffset + onDiskSize) >= (offset + size)) ? PreSaveNotificationScanControlInstruction.Stop : PreSaveNotificationScanControlInstruction.Continue; } } //----------------------------------------------------- // // Protected Methods // //------------------------------------------------------ protected void Dispose(bool disposing) { if (disposing) { // multiple calls are fine - just ignore them if (!_disposedFlag) { // Prevent recursion into Save() when propagating Flush or Disposed to our minions // because ZipIOFileItemStream.Flush calls us. if (_propagatingFlushDisposed) return; else _propagatingFlushDisposed = true; // enter first time try { try { foreach (IZipIOBlock block in _blockList) { IDisposable disposableBlock = block as IDisposable; if (disposableBlock != null) { // only some Blocks are disposable, most are not disposableBlock.Dispose(); } } } finally { // If we own the stream, we should close it. // If not, we cannot even close the binary reader or writer as these close the // underlying stream on us. if (_ownStream) { if (_binaryReader != null) { // this one might be null of we have been only writing _binaryReader.Close(); } if (_binaryWriter != null) { // this one might be null of we have been only reading _binaryWriter.Close(); } _archiveStream.Close(); } } } finally { _blockList = null; _encoding = null; _endOfCentralDirectoryBlock = null; _centralDirectoryBlock = null; _disposedFlag = true; _propagatingFlushDisposed = false; // reset } } } } //----------------------------------------------------- // // Private Properties // //------------------------------------------------------ ////// This property returns the index of CentralDirectoryBlock within _blockList /// private int CentralDirectoryBlockIndex { get { Invariant.Assert(_blockList.Count >= _requiredBlockCount); Debug.Assert(_centralDirectoryBlock != null && _endOfCentralDirectoryBlock != null && _zip64EndOfCentralDirectoryBlock != null && _zip64EndOfCentralDirectoryLocatorBlock != null); // We always have following blocks at the end of the block lists: // CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD // Thus the index of CD can be calculated from the total number of blocks // and _requiredBlockCount which is 4 return _blockList.Count - _requiredBlockCount; } } //------------------------------------------------------ // // Private Methods // //----------------------------------------------------- ////// Throwes exception if object already Disposed/Closed. /// private void CheckDisposed() { if (_disposedFlag) { throw new ObjectDisposedException(null, SR.Get(SRID.ZipArchiveDisposed)); } } ////// Save - container level /// /// true if closing, false if flushing private void SaveContainer(bool closingFlag) { CheckDisposed(); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); if (!closingFlag && !DirtyFlag) { // we are trying to save some cycles in the case of the subsequent Flush calls // that do not close the container // if it is being closed DirtyFlag isn't reliable as the Compressed streams carry // some extra bytes after flushing and only write them out on closing. return; } // We need a separate cycle to update all the cross block references prior to saving blocks // specifically the central directory needs "dirty" information from blocks in order to properly // update it's references, otherwise (if we call UpdateReferences and Save in the same loop) // information about block shifts will be lost by the time we ask central directory to update it's // references // offset of the first block long currentOffset = 0; // ZIP64 review type here foreach (IZipIOBlock currentBlock in _blockList) { // move block so it is positioned right after the previous block currentBlock.Move(currentOffset - currentBlock.Offset); // this will update references and as well as other internal structures (size) // specifically for the FileItemBlock it will flush buffers of // all the outstanding streams currentBlock.UpdateReferences(closingFlag); //advance current stream position according to the size of the block checked{currentOffset += currentBlock.Size;} } // save dirty blocks bool dirtyBlockFound = false; int blockListCount = _blockList.Count; for (int i = 0; i < blockListCount ; i++) { IZipIOBlock currentBlock = (IZipIOBlock)_blockList[i]; if (currentBlock.GetDirtyFlag(closingFlag)) { dirtyBlockFound = true; long currentBlockOffset = currentBlock.Offset; long currentBlockSize = currentBlock.Size; if (currentBlockSize > 0) { // before saving we need to warn all the blocks that have still some data on disk // that might be overriden // second loop must start at the current position of the extrrnal loop // as all the items before have been saved for (int j = i + 1; j < blockListCount; j++) { // This is an optimization which enabled us to stop going through the // tail blocks as soon as we find a block that returns a status indicating // that it took care of the tail of the target area or is positioned after the // target area. if (((IZipIOBlock)_blockList[j]).PreSaveNotification(currentBlockOffset, currentBlockSize) == PreSaveNotificationScanControlInstruction.Stop ) { break; } } } currentBlock.Save(); // Even if currentBlockSize == 0, call Save to clear DirtyFlag } } // originally we have had an assert for the case when no changes were made to the file // but calculated size didn't match the actual stream size. // As a result of the XPS Viewer dynamically switching streams underneath ZIP IO, we // need to treat this case as a normal non-dirty scenario. So if nothing changed and // nothing was written out we shouldn't even validate whether stream underneath // was modified in any way or not (even such simple modifications as an unexpected // Stream.Length change). If it was modified by someone we assume that the stream // owner was aware of it's action. if (dirtyBlockFound && (Stream.Length > currentOffset)) { Stream.SetLength(currentOffset); } Stream.Flush(); DirtyFlag = false; } ////// Streaming version of Save routine /// /// true if closing the package private void StreamingSaveContainer(bool closingFlag) { // STREAMING Mode: // NOTE: _blockList is NOT in offset order except the last four blocks // (CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD) try { // save dirty blocks long currentOffset = 0; for (int i = 0; i < _blockList.Count; i++) { IZipIOBlock currentBlock = (IZipIOBlock)_blockList[i]; ZipIOLocalFileBlock localFileBlock = currentBlock as ZipIOLocalFileBlock; if (localFileBlock == null) { if (closingFlag) { // Move block so it is positioned right after the previous block. // No need for nested loops like in SaveContainer because none of these // calls can cause a block to move in the Streaming case. currentBlock.Move(currentOffset - currentBlock.Offset); currentBlock.UpdateReferences(closingFlag); if (currentBlock.GetDirtyFlag(closingFlag)) { currentBlock.Save(); } } } else if (currentBlock.GetDirtyFlag(closingFlag)) { // no need to call UpdateReferences in streaming mode for regular // local file blocks because // we manually emit the local file header and the local file descriptor localFileBlock.SaveStreaming(closingFlag); } checked{currentOffset += currentBlock.Size;} } Stream.Flush(); } finally { // all done so restore state _propagatingFlushDisposed = false; } } ////// Flush was called on a ZipIOFileItemStream /// /// block that owns the stream that Flush was called on /// close or dispose private void StreamingSaveStream(ZipIOLocalFileBlock blockRequestingFlush, bool closingFlag) { // STREAMING MODE: // Flush will do one of two things, depending on the currently open stream: // 1) If the currently open stream matches the one passed (or none is currently opened) // then write will occur to the open stream. // 2) Otherwise, the currently opened stream will be flushed and closed, and the // given stream will become the currently opened stream // NOTE: _blockList is NOT in offset order except the last four blocks // (CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD) // different stream? if (_streamingCurrentlyOpenStreamBlock != blockRequestingFlush) { // need to close the currently opened stream // unless its our first time through if (_streamingCurrentlyOpenStreamBlock != null) { _streamingCurrentlyOpenStreamBlock.SaveStreaming(true); } // Now make the given stream the new "currently opened stream". _streamingCurrentlyOpenStreamBlock = blockRequestingFlush; } // this should now be flushable/closable _streamingCurrentlyOpenStreamBlock.SaveStreaming(closingFlag); // if closing - discard the stream because it is now closed if (closingFlag) _streamingCurrentlyOpenStreamBlock = null; } private void CreateCentralDirectoryBlock() { CheckDisposed(); Debug.Assert(_zip64EndOfCentralDirectoryBlock != null); // It must not be loaded yet Debug.Assert(!IsCentralDirectoryBlockLoaded); // The proper position is just before the Zip64EndOfCentralDirectoryRecord // Zip64EndOfCentralDirectoryRecord - might be of size 0 (if file is small enough) int blockPosition = _blockList.IndexOf(Zip64EndOfCentralDirectoryBlock); Debug.Assert(blockPosition >= 0); // construct Block find it and parse it _centralDirectoryBlock = ZipIOCentralDirectoryBlock.CreateNew(this); //ask block manager to insert this this block InsertBlock(blockPosition , _centralDirectoryBlock); } private void LoadCentralDirectoryBlock() { Debug.Assert(_centralDirectoryBlock == null); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); // construct Block find it and parse it _centralDirectoryBlock = ZipIOCentralDirectoryBlock.SeekableLoad(this); //ask block manager to MAP this block MapBlock(_centralDirectoryBlock); } private void CreateLoadZip64Blocks() { CheckDisposed(); Debug.Assert ((_zip64EndOfCentralDirectoryBlock == null) && (_zip64EndOfCentralDirectoryLocatorBlock == null)); // determine whether we want to create it or load it // this check doesn't provide us with a 100% guarantee. // After discussion we have agreed that this should be sufficient if (!Streaming && EndOfCentralDirectoryBlock.ContainValuesHintingToPossibilityOfZip64 && ZipIOZip64EndOfCentralDirectoryLocatorBlock.SniffTheBlockSignature(this)) { // attempt to sniff the header of the LoadZip64EndOfCentralDirectoryLocatorBlock(); LoadZip64EndOfCentralDirectoryBlock(); } else { // We delayed validation of some values in End of Central Directory that can give possible // hints for Zip64; Since there is no Zip64 structure, we need to validate them here _endOfCentralDirectoryBlock.ValidateZip64TriggerValues(); CreateZip64EndOfCentralDirectoryLocatorBlock(); CreateZip64EndOfCentralDirectoryBlock(); } } private void CreateZip64EndOfCentralDirectoryBlock() { Debug.Assert(_zip64EndOfCentralDirectoryBlock == null); // The proper position is just before the Zip64EndOfCentralDirectoryRecordLocator // Zip64EndOfCentralDirectoryRecord - might be of size 0 (if file is small enough) int blockPosition = _blockList.IndexOf(Zip64EndOfCentralDirectoryLocatorBlock); // construct Block find it and parse it _zip64EndOfCentralDirectoryBlock = ZipIOZip64EndOfCentralDirectoryBlock.CreateNew(this); //ask block manager to insert this this block InsertBlock(blockPosition, _zip64EndOfCentralDirectoryBlock); } private void LoadZip64EndOfCentralDirectoryBlock() { Debug.Assert(_zip64EndOfCentralDirectoryBlock == null); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); // construct Block find it and parse it _zip64EndOfCentralDirectoryBlock = ZipIOZip64EndOfCentralDirectoryBlock.SeekableLoad(this); //ask block manager to insert this this block MapBlock(_zip64EndOfCentralDirectoryBlock); } private void CreateZip64EndOfCentralDirectoryLocatorBlock() { Debug.Assert(_zip64EndOfCentralDirectoryLocatorBlock == null); // The proper position is just before the EOCD int blockPosition = _blockList.IndexOf(EndOfCentralDirectoryBlock); // construct Block find it and parse it _zip64EndOfCentralDirectoryLocatorBlock = ZipIOZip64EndOfCentralDirectoryLocatorBlock.CreateNew(this); //ask block manager to MAP this block InsertBlock(blockPosition, _zip64EndOfCentralDirectoryLocatorBlock); } private void LoadZip64EndOfCentralDirectoryLocatorBlock() { Debug.Assert(_zip64EndOfCentralDirectoryLocatorBlock == null); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); // construct Block find it and parse it _zip64EndOfCentralDirectoryLocatorBlock = ZipIOZip64EndOfCentralDirectoryLocatorBlock.SeekableLoad(this); //ask block manager to MAP this block MapBlock(_zip64EndOfCentralDirectoryLocatorBlock); } private void MapBlock(IZipIOBlock block) { // as we map a block to existing file space it must be not dirty. Debug.Assert(!block.GetDirtyFlag(true)); // closingFlag==true used as a more conservative option Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); for (int blockIndex = _blockList.Count - 1; blockIndex >= 0; --blockIndex) { // if we need to find a RawDataBlock that maps to the target area ZipIORawDataFileBlock rawBlock = _blockList[blockIndex] as ZipIORawDataFileBlock; //check the original loaded RawBlock size / offset against the new block if ((rawBlock != null) && rawBlock.DiskImageContains(block)) { ZipIORawDataFileBlock prefixBlock, suffixBlock; //split raw block into prefixRawBlock, SuffixRawBlock rawBlock.SplitIntoPrefixSuffix(block, out prefixBlock, out suffixBlock); _blockList.RemoveAt(blockIndex); // remove the old big raw data block // add suffix Raw data block if (suffixBlock != null) { _blockList.Insert(blockIndex, suffixBlock); } // add new mapped block _blockList.Insert(blockIndex, block); // add prefix Raw data block if (prefixBlock != null) { _blockList.Insert(blockIndex, prefixBlock); } return; } } // we couldn't find a raw data block for mapping this, we can only throw throw new FileFormatException(SR.Get(SRID.CorruptedData)); } private void InsertBlock(int blockPosition, IZipIOBlock block) { // as we are adding a new block it must be dirty unless its size is 0 Debug.Assert(block.GetDirtyFlag(true) || // closingFlag==true used as a more conservative option block.Size == 0); _blockList.Insert(blockPosition, block); } private void AppendBlock(IZipIOBlock block) { // as we are adding a new block it must be dirty unless its size is 0 Debug.Assert(block.GetDirtyFlag(true) || // closingFlag==true used as a more conservative option block.Size == 0); // CentralDirectory persistence logic relies on the fact that we always add headers in a fashion that // matches the order of the corresponding file items in the physical archive (currently to the end of the list). // If this invariant is violated, the corresponding central directory persistence logic must be updated. _blockList.Add(block); } // this flag is used for Perf reasons, it doesn't carry any additional information that isn't stored somewhere // else. In order to prevent complex dirty calculations on the sequential flush calls, we are going to keep // this flag which will be set to true at the end of the flush (or close). This flag will be set from the ZipArchive // and CrcCalculating entry points that can potentially make our structure dirty. // This flag is only used for non-streaming cases. In streaming cases we do not believe there is a perf // penalty of that nature. private bool _dirtyFlag = false; private bool _disposedFlag; private bool _propagatingFlushDisposed; // if true, we ignore calls back to Save to prevent recursion private Stream _archiveStream; private bool _openStreaming; private bool _ownStream; // true if we own the archive stream // Streaming Mode Only: stream that is currently able to write without interfering with other streams private ZipIOLocalFileBlock _streamingCurrentlyOpenStreamBlock; private BinaryReader _binaryReader; private BinaryWriter _binaryWriter; private const int _initialBlockListSize = 50; private ArrayList _blockList = new ArrayList(_initialBlockListSize); private ASCIIEncoding _encoding = new ASCIIEncoding(); ZipIOZip64EndOfCentralDirectoryBlock _zip64EndOfCentralDirectoryBlock; ZipIOZip64EndOfCentralDirectoryLocatorBlock _zip64EndOfCentralDirectoryLocatorBlock; ZipIOEndOfCentralDirectoryBlock _endOfCentralDirectoryBlock; ZipIOCentralDirectoryBlock _centralDirectoryBlock; private const long _lowWaterMark = 0x19000; // we definately would like to keep everythuing under 100 KB in memory private const long _highWaterMark = 0xA00000; // we would like to keep everything over 10 MB on disk private const int _requiredBlockCount = 4; // We always have following blocks: CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD // This value is used to calculate the index of CD within _blockList } } // 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. // 10/21/2005: brucemac: Apply security mitigations // //----------------------------------------------------------------------------- using System; using System.Diagnostics; using System.IO; using System.Text; using System.Collections; using System.Globalization; using System.Runtime.Serialization; using System.Windows; using MS.Internal.IO.Packaging; // for PackagingUtilities using MS.Internal.WindowsBase; namespace MS.Internal.IO.Zip { ////// This is the main class of the actual ZIP IO implementation. It is primary responsibility /// is to maintain the map and status of the parsed and loaded areas(blocks) of the file. /// It is also supports manipulating this map (adding and deleting blocks) /// internal class ZipIOBlockManager : IDisposable, IEnumerable { //------------------------------------------------------ // // Public Methods // //----------------------------------------------------- public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } IEnumerator IEnumerable.GetEnumerator() { CheckDisposed(); return _blockList.GetEnumerator(); } //------------------------------------------------------ // // Internal Properties // //------------------------------------------------------ ////// This property returns the status of whether Central directory is loaded or not. /// This property is rarely used, as most clients will just ask for CentralDirectoryBlock /// and oif it isn't loaded it will be. /// The only reason to use IsCentralDirectoryBlockLoaded property is to differentiate /// scenarios in which some optimization is possible, if central directory isn't loaded yet. /// internal bool IsCentralDirectoryBlockLoaded { get { CheckDisposed(); return (_centralDirectoryBlock != null); } } ////// This property returns the CentralDirectoryBlock and provides lazy load /// fuinctionality. This isthe only way other classes can access information /// from the Central Directory Block /// internal ZipIOCentralDirectoryBlock CentralDirectoryBlock { get { CheckDisposed(); if (_centralDirectoryBlock == null) { // figure out if we are in ZIP64 mode or not if (Zip64EndOfCentralDirectoryBlock.TotalNumberOfEntriesInTheCentralDirectory > 0) { LoadCentralDirectoryBlock(); } else { // We need to be aware of the special case of empty Zip Archive // with a single record : End Of Central directory //In such cases we should create new CentralDirectoryBlock CreateCentralDirectoryBlock(); } } return _centralDirectoryBlock; } } ////// This property returns the Zip64EndOfCentralDirectoryBlock and provides lazy load /// fuinctionality. This is the only way other classes can access information /// from the Zip64EndOfCentralDirectoryBlock /// internal ZipIOZip64EndOfCentralDirectoryBlock Zip64EndOfCentralDirectoryBlock { get { CheckDisposed(); if (_zip64EndOfCentralDirectoryBlock == null) { CreateLoadZip64Blocks(); } return _zip64EndOfCentralDirectoryBlock; } } ////// This property returns the Zip64EndOfCentralDirectoryLocatorBlock and provides lazy load /// fuinctionality. This is the only way other classes can access information /// from the Zip64EndOfCentralDirectoryLocator Block /// internal ZipIOZip64EndOfCentralDirectoryLocatorBlock Zip64EndOfCentralDirectoryLocatorBlock { get { CheckDisposed(); if (_zip64EndOfCentralDirectoryLocatorBlock == null) { CreateLoadZip64Blocks(); } return _zip64EndOfCentralDirectoryLocatorBlock; } } ////// This property returns the CentralDirectoryBlock and provides lazy load /// fuinctionality. This is the only way other classes can access information /// from the Central Directory Block /// internal ZipIOEndOfCentralDirectoryBlock EndOfCentralDirectoryBlock { get { CheckDisposed(); if (_endOfCentralDirectoryBlock == null) { LoadEndOfCentralDirectoryBlock(); } return _endOfCentralDirectoryBlock; } } internal Stream Stream { get { CheckDisposed(); return _archiveStream; } } internal bool Streaming { get { CheckDisposed(); return _openStreaming; } } internal BinaryReader BinaryReader { get { CheckDisposed(); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); if (_binaryReader == null) { _binaryReader = new BinaryReader(Stream, Encoding); } return _binaryReader; } } internal BinaryWriter BinaryWriter { get { CheckDisposed(); if (_binaryWriter == null) { _binaryWriter = new BinaryWriter(Stream, Encoding); } return _binaryWriter; } } internal Encoding Encoding { get { CheckDisposed(); return _encoding; } } internal bool DirtyFlag { set { CheckDisposed(); _dirtyFlag = value; } get { CheckDisposed(); return _dirtyFlag; } } static internal int MaxFileNameSize { get { return UInt16.MaxValue; } } //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ internal void CreateEndOfCentralDirectoryBlock() { CheckDisposed(); // Prevent accidental call if underlying stream is non-empty since // any legal zip archive contains an EOCD block. Debug.Assert(_openStreaming || _archiveStream.Length == 0); // Disallow multiple calls. Debug.Assert(_endOfCentralDirectoryBlock == null); // construct Block find it and parse it long blockOffset = 0; // this will be updated later _endOfCentralDirectoryBlock = ZipIOEndOfCentralDirectoryBlock.CreateNew(this, blockOffset); // this will add a block to the tail AppendBlock(_endOfCentralDirectoryBlock); DirtyFlag = true; } internal void LoadEndOfCentralDirectoryBlock() { Debug.Assert(_endOfCentralDirectoryBlock == null); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); // construct Block find it and parse it _endOfCentralDirectoryBlock = ZipIOEndOfCentralDirectoryBlock.SeekableLoad(this); //ask block manager to MAP this block MapBlock(_endOfCentralDirectoryBlock); } internal ZipIOLocalFileBlock CreateLocalFileBlock(string zipFileName, CompressionMethodEnum compressionMethod, DeflateOptionEnum deflateOption) { CheckDisposed(); // we are guaranteed uniqueness at this point , so let's just add a // block at the end of the file, just before the central directory // construct Block find it and parse it // STREAMING Mode: // NOTE: _blockList is NOT in offset order except the last four blocks // (CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD) ZipIOLocalFileBlock localFileBlock = ZipIOLocalFileBlock.CreateNew(this, zipFileName, compressionMethod, deflateOption); InsertBlock(CentralDirectoryBlockIndex, localFileBlock); CentralDirectoryBlock.AddFileBlock(localFileBlock); DirtyFlag = true; return localFileBlock; } internal ZipIOLocalFileBlock LoadLocalFileBlock(string zipFileName) { CheckDisposed(); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); Debug.Assert(CentralDirectoryBlock.FileExists(zipFileName)); // it must be in the central directory // construct Block find it and parse it ZipIOLocalFileBlock localFileBlock = ZipIOLocalFileBlock.SeekableLoad(this, zipFileName); MapBlock(localFileBlock); return localFileBlock; } internal void RemoveLocalFileBlock(ZipIOLocalFileBlock localFileBlock) { CheckDisposed(); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); Debug.Assert(localFileBlock != null, " At this point local File block must be preloaded"); Debug.Assert(CentralDirectoryBlock.FileExists(localFileBlock.FileName), " At this point local File block must be mapped in central directory"); // remove it from our list _blockList.Remove(localFileBlock); // remove this from Central Directory CentralDirectoryBlock.RemoveFileBlock(localFileBlock.FileName); DirtyFlag = true; // at this point we can Dispose it to make sure that any calls // to this file block through outstanding indirect references will result in object Disposed exception localFileBlock.Dispose(); } internal void MoveData(long moveBlockSourceOffset, long moveBlockTargetOffset, long moveBlockSize) { Debug.Assert(moveBlockSize >=0); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); if ((moveBlockSize ==0) || (moveBlockSourceOffset == moveBlockTargetOffset)) { //trivial empty move case return; } checked { byte[] tempBuffer = new byte [Math.Min(moveBlockSize,0x100000)]; // min(1mb, requested block size) long bytesMoved = 0; while(bytesMoved < moveBlockSize) { long subBlockSourceOffset; long subBlockTargetOffset; int subBlockSize = (int)Math.Min((long)tempBuffer.Length, moveBlockSize - bytesMoved); if (moveBlockSourceOffset > moveBlockTargetOffset) { subBlockSourceOffset = moveBlockSourceOffset + bytesMoved; subBlockTargetOffset = moveBlockTargetOffset + bytesMoved; } else { subBlockSourceOffset = moveBlockSourceOffset + moveBlockSize - bytesMoved - subBlockSize; subBlockTargetOffset = moveBlockTargetOffset + moveBlockSize - bytesMoved - subBlockSize; } _archiveStream.Seek(subBlockSourceOffset, SeekOrigin.Begin); int bytesRead = PackagingUtilities.ReliableRead(_archiveStream, tempBuffer, 0, subBlockSize); if (bytesRead != subBlockSize) { throw new FileFormatException(SR.Get(SRID.CorruptedData)); } _archiveStream.Seek(subBlockTargetOffset, SeekOrigin.Begin); _archiveStream.Write(tempBuffer, 0, subBlockSize); checked{bytesMoved += subBlockSize;} } } } ////// Save - stream level /// /// /// closing or flushing internal void SaveStream(ZipIOLocalFileBlock blockRequestingFlush, bool closingFlag) { // Prevent recursion when propagating Flush or Disposed to our minions // because ZipIOFileItemStream.Flush calls us. if (_propagatingFlushDisposed) return; else _propagatingFlushDisposed = true; // enter first time try { // redirect depending on our mode if (_openStreaming) { StreamingSaveStream(blockRequestingFlush, closingFlag); } else SaveContainer(false); } finally { // all done so restore state _propagatingFlushDisposed = false; } } ////// Save - container level /// /// true if closing, false if flushing internal void Save(bool closingFlag) { CheckDisposed(); // Prevent recursion when propagating Flush or Disposed to our minions // because ZipIOFileItemStream.Flush calls us. if (_propagatingFlushDisposed) return; else _propagatingFlushDisposed = true; // enter first time try { // redirect depending on our mode if (_openStreaming) { StreamingSaveContainer(closingFlag); } else SaveContainer(closingFlag); } finally { // all done so restore state _propagatingFlushDisposed = false; } } ////// Constructor /// /// stream we operate on /// /// true if we own the stream and are expected to close it when we are disposed internal ZipIOBlockManager(Stream archiveStream, bool streaming, bool ownStream) { Debug.Assert(archiveStream != null); _archiveStream = archiveStream; _openStreaming = streaming; _ownStream = ownStream; if (streaming) { // wrap the archive stream in a WriteTimeStream which keeps track of current position _archiveStream = new WriteTimeStream(_archiveStream); } else if (archiveStream.Length > 0) { // for non-empty stream we need to map the whole stream into a raw data block // which helps keep track of shifts and dirty areas ZipIORawDataFileBlock rawBlock = ZipIORawDataFileBlock.Assign(this, 0, archiveStream.Length); _blockList.Add(rawBlock); } } internal static UInt32 ToMsDosDateTime(DateTime dateTime) { UInt32 result = 0; result |= (((UInt32)dateTime.Second) /2) & 0x1F; // seconds need to be divided by 2 // as they stored in 5 bits result |= (((UInt32)dateTime.Minute) & 0x3F) << 5; result |= (((UInt32)dateTime.Hour) & 0x1F) << 11; result |= (((UInt32)dateTime.Day) & 0x1F) << 16; result |= (((UInt32)dateTime.Month) & 0xF) << 21; result |= (((UInt32)(dateTime.Year - 1980)) & 0x7F) << 25; return result; } internal static DateTime FromMsDosDateTime(UInt32 dosDateTime) { int seconds = (int)((dosDateTime & 0x1F) << 1); // seconds need to be multiplied by 2 // as they stored in 5 bits int minutes = (int)((dosDateTime >> 5) & 0x3F); int hours = (int)((dosDateTime >> 11) & 0x1F); int day = (int)((dosDateTime >> 16) & 0x1F); int month =(int)((dosDateTime >> 21) & 0xF); int year = (int)(1980 + ((dosDateTime >> 25) & 0x7F)); //this will throw if parameters are out of range return new DateTime(year, month,day,hours,minutes,seconds); } ////// This is standard way to normalize Zip File Item names. At this point we only /// getting rid of the spaces. The Exists calls are responsible or making sure /// that they check for uniqueness in a case insensitive manner. It is up to the /// higher levels to add stricter restrictions like URI character set, and so on. /// static internal string ValidateNormalizeFileName(string zipFileName) { // Validate parameteres if (zipFileName == null) { throw new ArgumentNullException("zipFileName"); } if (zipFileName.Length > ZipIOBlockManager.MaxFileNameSize) { throw new ArgumentOutOfRangeException("zipFileName"); } zipFileName = zipFileName.Trim(); if (zipFileName.Length < 1)//it must be at least one character { throw new ArgumentOutOfRangeException("zipFileName"); } //Based on the Appnote : // << The path stored should not contain a drive or device letter, or a leading slash. >> return zipFileName; } //----------------------------------------------------- // Internal helper CopyBytes functions for storing data into a byte[] // it is a similar to a BinaryWriter , but not for streams but ratrher // for byte[] // These functiona used in the Extra field parsing, as that functionality is buit // in terms of byte[] not streams //----------------------------------------------------- internal static int CopyBytes(Int16 value, byte[] buffer, int offset) { Debug.Assert(checked(buffer.Length-offset) >= sizeof(Int16)); byte[] tempBuffer = BitConverter.GetBytes(value); Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); return offset + tempBuffer.Length; } internal static int CopyBytes(Int32 value, byte[] buffer, int offset) { Debug.Assert(checked(buffer.Length-offset) >= sizeof(Int32)); byte[] tempBuffer = BitConverter.GetBytes(value); Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); return offset + tempBuffer.Length; } internal static int CopyBytes(Int64 value, byte[] buffer, int offset) { Debug.Assert(checked(buffer.Length-offset) >= sizeof(Int64)); byte[] tempBuffer = BitConverter.GetBytes(value); Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); return offset + tempBuffer.Length; } internal static int CopyBytes(UInt16 value, byte[] buffer, int offset) { Debug.Assert(checked(buffer.Length-offset) >= sizeof(UInt16)); byte[] tempBuffer = BitConverter.GetBytes(value); Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); return offset + tempBuffer.Length; } internal static int CopyBytes(UInt32 value, byte[] buffer, int offset) { Debug.Assert(checked(buffer.Length-offset) >= sizeof(UInt32)); byte[] tempBuffer = BitConverter.GetBytes(value); Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); return offset + tempBuffer.Length; } internal static int CopyBytes(UInt64 value, byte[] buffer, int offset) { Debug.Assert(checked(buffer.Length-offset) >= sizeof(UInt64)); byte[] tempBuffer = BitConverter.GetBytes(value); Array.Copy(tempBuffer, 0, buffer, offset, tempBuffer.Length); return offset + tempBuffer.Length; } internal static UInt64 ConvertToUInt64(UInt32 loverAddressValue, UInt32 higherAddressValue) { return checked((UInt64)loverAddressValue + (((UInt64)higherAddressValue) << 32)); } internal static ZipIOVersionNeededToExtract CalcVersionNeededToExtractFromCompression (CompressionMethodEnum compression) { switch (compression) { case CompressionMethodEnum.Stored: return ZipIOVersionNeededToExtract.StoredData; case CompressionMethodEnum.Deflated: return ZipIOVersionNeededToExtract.DeflatedData; default: throw new NotSupportedException(); // Deflated64 this is OFF } } ////// This is the common Pre Save notiofication handler for /// RawDataFile Block and File Item Stream /// It makes assumption that the overlap generally start coming in at the beginning of a /// large disk image, so we should only try to cache cache overlaped data in the prefix /// of the disk block /// Block can also return a value indicating whether PreSaveNotification should be extended to the blocks that are positioned after /// it in the Block List. For example, if block has completely handled PreSaveNotification in a way that it cached the whole area that /// was in danger (of being overwritten) it means that no blocks need to worry about this anymore. After all no 2 blocks should have /// share on disk buffers. Another scenario is when block can determine that area in danger is positioned before the block's on disk /// buffers; this means that all blocks that are positioned later in the block list do not need to worry about this PreSaveNotification /// as their buffers should be positioned even further alone in the file. /// internal static PreSaveNotificationScanControlInstruction CommonPreSaveNotificationHandler( Stream stream, long offset, long size, long onDiskOffset, long onDiskSize, ref SparseMemoryStream cachePrefixStream) { checked { Debug.Assert(size >=0); Debug.Assert(offset >=0); Debug.Assert(onDiskSize >=0); Debug.Assert(onDiskOffset >=0); // trivial request if (size == 0) { // The area being overwritten is of size 0 so there is no need to notify any blocks about this. return PreSaveNotificationScanControlInstruction.Stop; } if (cachePrefixStream != null) { // if we have something in cache prefix buffer we only should check whatever tail data isn't cached checked{onDiskOffset += cachePrefixStream.Length;} checked{onDiskSize -= cachePrefixStream.Length;} Debug.Assert(onDiskSize >=0); } if (onDiskSize == 0) { // the raw data block happened to be fully cached // in this case (onDiskSize==0) can not be used as a reliable indicator of the position of the // on disk buffer relative to the other; it is just an indicator of an empty buffer which might have a meaningless offset // that shouldn't be driving any decisions return PreSaveNotificationScanControlInstruction.Continue; } // we need to first find out if the raw data that isn't cached yet overlaps with any disk space // that is about to be overriden long overlapBlockOffset; long overlapBlockSize; PackagingUtilities.CalculateOverlap(onDiskOffset, onDiskSize, offset, size , out overlapBlockOffset, out overlapBlockSize); if (overlapBlockSize <= 0) { // No overlap , we can ignore this message. // In addition to that, if (onDiskOffset > offset) it means that, given the fact that all blocks after // the current one will have even larger offsets, they couldn't possibly overlap with (offset ,size ) chunk . return (onDiskOffset > offset) ? PreSaveNotificationScanControlInstruction.Stop : PreSaveNotificationScanControlInstruction.Continue; } // at this point we have an overlap, we need to read the data that is overlapped // and merge it with whatever we already have in cache // let's figure out the part that isn't cached yet, and needs to be long blockSizeToCache; checked { blockSizeToCache = overlapBlockOffset + overlapBlockSize - onDiskOffset; } Debug.Assert(blockSizeToCache >0); // there must be a non empty block at this point that needs to be cached // We need to ensure that we do have a place to store this data if (cachePrefixStream == null) { cachePrefixStream = new SparseMemoryStream(_lowWaterMark, _highWaterMark); } else { // if we already have some cached prefix data we have to make sure we are // appending new data tro the tail of the already cached chunk cachePrefixStream.Seek(0, SeekOrigin.End); } stream.Seek(onDiskOffset, SeekOrigin.Begin); long bytesCopied = PackagingUtilities.CopyStream(stream, cachePrefixStream, blockSizeToCache, 4096); if (bytesCopied != blockSizeToCache) { throw new FileFormatException(SR.Get(SRID.CorruptedData)); } // if the contdition below is true it means that, given the fact that all blocks after // the current one will have even larger offsets, they couldn't possibly overlap with (offset ,size ) chunk return ((onDiskOffset + onDiskSize) >= (offset + size)) ? PreSaveNotificationScanControlInstruction.Stop : PreSaveNotificationScanControlInstruction.Continue; } } //----------------------------------------------------- // // Protected Methods // //------------------------------------------------------ protected void Dispose(bool disposing) { if (disposing) { // multiple calls are fine - just ignore them if (!_disposedFlag) { // Prevent recursion into Save() when propagating Flush or Disposed to our minions // because ZipIOFileItemStream.Flush calls us. if (_propagatingFlushDisposed) return; else _propagatingFlushDisposed = true; // enter first time try { try { foreach (IZipIOBlock block in _blockList) { IDisposable disposableBlock = block as IDisposable; if (disposableBlock != null) { // only some Blocks are disposable, most are not disposableBlock.Dispose(); } } } finally { // If we own the stream, we should close it. // If not, we cannot even close the binary reader or writer as these close the // underlying stream on us. if (_ownStream) { if (_binaryReader != null) { // this one might be null of we have been only writing _binaryReader.Close(); } if (_binaryWriter != null) { // this one might be null of we have been only reading _binaryWriter.Close(); } _archiveStream.Close(); } } } finally { _blockList = null; _encoding = null; _endOfCentralDirectoryBlock = null; _centralDirectoryBlock = null; _disposedFlag = true; _propagatingFlushDisposed = false; // reset } } } } //----------------------------------------------------- // // Private Properties // //------------------------------------------------------ ////// This property returns the index of CentralDirectoryBlock within _blockList /// private int CentralDirectoryBlockIndex { get { Invariant.Assert(_blockList.Count >= _requiredBlockCount); Debug.Assert(_centralDirectoryBlock != null && _endOfCentralDirectoryBlock != null && _zip64EndOfCentralDirectoryBlock != null && _zip64EndOfCentralDirectoryLocatorBlock != null); // We always have following blocks at the end of the block lists: // CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD // Thus the index of CD can be calculated from the total number of blocks // and _requiredBlockCount which is 4 return _blockList.Count - _requiredBlockCount; } } //------------------------------------------------------ // // Private Methods // //----------------------------------------------------- ////// Throwes exception if object already Disposed/Closed. /// private void CheckDisposed() { if (_disposedFlag) { throw new ObjectDisposedException(null, SR.Get(SRID.ZipArchiveDisposed)); } } ////// Save - container level /// /// true if closing, false if flushing private void SaveContainer(bool closingFlag) { CheckDisposed(); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); if (!closingFlag && !DirtyFlag) { // we are trying to save some cycles in the case of the subsequent Flush calls // that do not close the container // if it is being closed DirtyFlag isn't reliable as the Compressed streams carry // some extra bytes after flushing and only write them out on closing. return; } // We need a separate cycle to update all the cross block references prior to saving blocks // specifically the central directory needs "dirty" information from blocks in order to properly // update it's references, otherwise (if we call UpdateReferences and Save in the same loop) // information about block shifts will be lost by the time we ask central directory to update it's // references // offset of the first block long currentOffset = 0; // ZIP64 review type here foreach (IZipIOBlock currentBlock in _blockList) { // move block so it is positioned right after the previous block currentBlock.Move(currentOffset - currentBlock.Offset); // this will update references and as well as other internal structures (size) // specifically for the FileItemBlock it will flush buffers of // all the outstanding streams currentBlock.UpdateReferences(closingFlag); //advance current stream position according to the size of the block checked{currentOffset += currentBlock.Size;} } // save dirty blocks bool dirtyBlockFound = false; int blockListCount = _blockList.Count; for (int i = 0; i < blockListCount ; i++) { IZipIOBlock currentBlock = (IZipIOBlock)_blockList[i]; if (currentBlock.GetDirtyFlag(closingFlag)) { dirtyBlockFound = true; long currentBlockOffset = currentBlock.Offset; long currentBlockSize = currentBlock.Size; if (currentBlockSize > 0) { // before saving we need to warn all the blocks that have still some data on disk // that might be overriden // second loop must start at the current position of the extrrnal loop // as all the items before have been saved for (int j = i + 1; j < blockListCount; j++) { // This is an optimization which enabled us to stop going through the // tail blocks as soon as we find a block that returns a status indicating // that it took care of the tail of the target area or is positioned after the // target area. if (((IZipIOBlock)_blockList[j]).PreSaveNotification(currentBlockOffset, currentBlockSize) == PreSaveNotificationScanControlInstruction.Stop ) { break; } } } currentBlock.Save(); // Even if currentBlockSize == 0, call Save to clear DirtyFlag } } // originally we have had an assert for the case when no changes were made to the file // but calculated size didn't match the actual stream size. // As a result of the XPS Viewer dynamically switching streams underneath ZIP IO, we // need to treat this case as a normal non-dirty scenario. So if nothing changed and // nothing was written out we shouldn't even validate whether stream underneath // was modified in any way or not (even such simple modifications as an unexpected // Stream.Length change). If it was modified by someone we assume that the stream // owner was aware of it's action. if (dirtyBlockFound && (Stream.Length > currentOffset)) { Stream.SetLength(currentOffset); } Stream.Flush(); DirtyFlag = false; } ////// Streaming version of Save routine /// /// true if closing the package private void StreamingSaveContainer(bool closingFlag) { // STREAMING Mode: // NOTE: _blockList is NOT in offset order except the last four blocks // (CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD) try { // save dirty blocks long currentOffset = 0; for (int i = 0; i < _blockList.Count; i++) { IZipIOBlock currentBlock = (IZipIOBlock)_blockList[i]; ZipIOLocalFileBlock localFileBlock = currentBlock as ZipIOLocalFileBlock; if (localFileBlock == null) { if (closingFlag) { // Move block so it is positioned right after the previous block. // No need for nested loops like in SaveContainer because none of these // calls can cause a block to move in the Streaming case. currentBlock.Move(currentOffset - currentBlock.Offset); currentBlock.UpdateReferences(closingFlag); if (currentBlock.GetDirtyFlag(closingFlag)) { currentBlock.Save(); } } } else if (currentBlock.GetDirtyFlag(closingFlag)) { // no need to call UpdateReferences in streaming mode for regular // local file blocks because // we manually emit the local file header and the local file descriptor localFileBlock.SaveStreaming(closingFlag); } checked{currentOffset += currentBlock.Size;} } Stream.Flush(); } finally { // all done so restore state _propagatingFlushDisposed = false; } } ////// Flush was called on a ZipIOFileItemStream /// /// block that owns the stream that Flush was called on /// close or dispose private void StreamingSaveStream(ZipIOLocalFileBlock blockRequestingFlush, bool closingFlag) { // STREAMING MODE: // Flush will do one of two things, depending on the currently open stream: // 1) If the currently open stream matches the one passed (or none is currently opened) // then write will occur to the open stream. // 2) Otherwise, the currently opened stream will be flushed and closed, and the // given stream will become the currently opened stream // NOTE: _blockList is NOT in offset order except the last four blocks // (CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD) // different stream? if (_streamingCurrentlyOpenStreamBlock != blockRequestingFlush) { // need to close the currently opened stream // unless its our first time through if (_streamingCurrentlyOpenStreamBlock != null) { _streamingCurrentlyOpenStreamBlock.SaveStreaming(true); } // Now make the given stream the new "currently opened stream". _streamingCurrentlyOpenStreamBlock = blockRequestingFlush; } // this should now be flushable/closable _streamingCurrentlyOpenStreamBlock.SaveStreaming(closingFlag); // if closing - discard the stream because it is now closed if (closingFlag) _streamingCurrentlyOpenStreamBlock = null; } private void CreateCentralDirectoryBlock() { CheckDisposed(); Debug.Assert(_zip64EndOfCentralDirectoryBlock != null); // It must not be loaded yet Debug.Assert(!IsCentralDirectoryBlockLoaded); // The proper position is just before the Zip64EndOfCentralDirectoryRecord // Zip64EndOfCentralDirectoryRecord - might be of size 0 (if file is small enough) int blockPosition = _blockList.IndexOf(Zip64EndOfCentralDirectoryBlock); Debug.Assert(blockPosition >= 0); // construct Block find it and parse it _centralDirectoryBlock = ZipIOCentralDirectoryBlock.CreateNew(this); //ask block manager to insert this this block InsertBlock(blockPosition , _centralDirectoryBlock); } private void LoadCentralDirectoryBlock() { Debug.Assert(_centralDirectoryBlock == null); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); // construct Block find it and parse it _centralDirectoryBlock = ZipIOCentralDirectoryBlock.SeekableLoad(this); //ask block manager to MAP this block MapBlock(_centralDirectoryBlock); } private void CreateLoadZip64Blocks() { CheckDisposed(); Debug.Assert ((_zip64EndOfCentralDirectoryBlock == null) && (_zip64EndOfCentralDirectoryLocatorBlock == null)); // determine whether we want to create it or load it // this check doesn't provide us with a 100% guarantee. // After discussion we have agreed that this should be sufficient if (!Streaming && EndOfCentralDirectoryBlock.ContainValuesHintingToPossibilityOfZip64 && ZipIOZip64EndOfCentralDirectoryLocatorBlock.SniffTheBlockSignature(this)) { // attempt to sniff the header of the LoadZip64EndOfCentralDirectoryLocatorBlock(); LoadZip64EndOfCentralDirectoryBlock(); } else { // We delayed validation of some values in End of Central Directory that can give possible // hints for Zip64; Since there is no Zip64 structure, we need to validate them here _endOfCentralDirectoryBlock.ValidateZip64TriggerValues(); CreateZip64EndOfCentralDirectoryLocatorBlock(); CreateZip64EndOfCentralDirectoryBlock(); } } private void CreateZip64EndOfCentralDirectoryBlock() { Debug.Assert(_zip64EndOfCentralDirectoryBlock == null); // The proper position is just before the Zip64EndOfCentralDirectoryRecordLocator // Zip64EndOfCentralDirectoryRecord - might be of size 0 (if file is small enough) int blockPosition = _blockList.IndexOf(Zip64EndOfCentralDirectoryLocatorBlock); // construct Block find it and parse it _zip64EndOfCentralDirectoryBlock = ZipIOZip64EndOfCentralDirectoryBlock.CreateNew(this); //ask block manager to insert this this block InsertBlock(blockPosition, _zip64EndOfCentralDirectoryBlock); } private void LoadZip64EndOfCentralDirectoryBlock() { Debug.Assert(_zip64EndOfCentralDirectoryBlock == null); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); // construct Block find it and parse it _zip64EndOfCentralDirectoryBlock = ZipIOZip64EndOfCentralDirectoryBlock.SeekableLoad(this); //ask block manager to insert this this block MapBlock(_zip64EndOfCentralDirectoryBlock); } private void CreateZip64EndOfCentralDirectoryLocatorBlock() { Debug.Assert(_zip64EndOfCentralDirectoryLocatorBlock == null); // The proper position is just before the EOCD int blockPosition = _blockList.IndexOf(EndOfCentralDirectoryBlock); // construct Block find it and parse it _zip64EndOfCentralDirectoryLocatorBlock = ZipIOZip64EndOfCentralDirectoryLocatorBlock.CreateNew(this); //ask block manager to MAP this block InsertBlock(blockPosition, _zip64EndOfCentralDirectoryLocatorBlock); } private void LoadZip64EndOfCentralDirectoryLocatorBlock() { Debug.Assert(_zip64EndOfCentralDirectoryLocatorBlock == null); Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); // construct Block find it and parse it _zip64EndOfCentralDirectoryLocatorBlock = ZipIOZip64EndOfCentralDirectoryLocatorBlock.SeekableLoad(this); //ask block manager to MAP this block MapBlock(_zip64EndOfCentralDirectoryLocatorBlock); } private void MapBlock(IZipIOBlock block) { // as we map a block to existing file space it must be not dirty. Debug.Assert(!block.GetDirtyFlag(true)); // closingFlag==true used as a more conservative option Debug.Assert(!_openStreaming, "Not legal in Streaming mode"); for (int blockIndex = _blockList.Count - 1; blockIndex >= 0; --blockIndex) { // if we need to find a RawDataBlock that maps to the target area ZipIORawDataFileBlock rawBlock = _blockList[blockIndex] as ZipIORawDataFileBlock; //check the original loaded RawBlock size / offset against the new block if ((rawBlock != null) && rawBlock.DiskImageContains(block)) { ZipIORawDataFileBlock prefixBlock, suffixBlock; //split raw block into prefixRawBlock, SuffixRawBlock rawBlock.SplitIntoPrefixSuffix(block, out prefixBlock, out suffixBlock); _blockList.RemoveAt(blockIndex); // remove the old big raw data block // add suffix Raw data block if (suffixBlock != null) { _blockList.Insert(blockIndex, suffixBlock); } // add new mapped block _blockList.Insert(blockIndex, block); // add prefix Raw data block if (prefixBlock != null) { _blockList.Insert(blockIndex, prefixBlock); } return; } } // we couldn't find a raw data block for mapping this, we can only throw throw new FileFormatException(SR.Get(SRID.CorruptedData)); } private void InsertBlock(int blockPosition, IZipIOBlock block) { // as we are adding a new block it must be dirty unless its size is 0 Debug.Assert(block.GetDirtyFlag(true) || // closingFlag==true used as a more conservative option block.Size == 0); _blockList.Insert(blockPosition, block); } private void AppendBlock(IZipIOBlock block) { // as we are adding a new block it must be dirty unless its size is 0 Debug.Assert(block.GetDirtyFlag(true) || // closingFlag==true used as a more conservative option block.Size == 0); // CentralDirectory persistence logic relies on the fact that we always add headers in a fashion that // matches the order of the corresponding file items in the physical archive (currently to the end of the list). // If this invariant is violated, the corresponding central directory persistence logic must be updated. _blockList.Add(block); } // this flag is used for Perf reasons, it doesn't carry any additional information that isn't stored somewhere // else. In order to prevent complex dirty calculations on the sequential flush calls, we are going to keep // this flag which will be set to true at the end of the flush (or close). This flag will be set from the ZipArchive // and CrcCalculating entry points that can potentially make our structure dirty. // This flag is only used for non-streaming cases. In streaming cases we do not believe there is a perf // penalty of that nature. private bool _dirtyFlag = false; private bool _disposedFlag; private bool _propagatingFlushDisposed; // if true, we ignore calls back to Save to prevent recursion private Stream _archiveStream; private bool _openStreaming; private bool _ownStream; // true if we own the archive stream // Streaming Mode Only: stream that is currently able to write without interfering with other streams private ZipIOLocalFileBlock _streamingCurrentlyOpenStreamBlock; private BinaryReader _binaryReader; private BinaryWriter _binaryWriter; private const int _initialBlockListSize = 50; private ArrayList _blockList = new ArrayList(_initialBlockListSize); private ASCIIEncoding _encoding = new ASCIIEncoding(); ZipIOZip64EndOfCentralDirectoryBlock _zip64EndOfCentralDirectoryBlock; ZipIOZip64EndOfCentralDirectoryLocatorBlock _zip64EndOfCentralDirectoryLocatorBlock; ZipIOEndOfCentralDirectoryBlock _endOfCentralDirectoryBlock; ZipIOCentralDirectoryBlock _centralDirectoryBlock; private const long _lowWaterMark = 0x19000; // we definately would like to keep everythuing under 100 KB in memory private const long _highWaterMark = 0xA00000; // we would like to keep everything over 10 MB on disk private const int _requiredBlockCount = 4; // We always have following blocks: CD, Zip64 EOCD, Zip64 EOCD Locator, and EOCD // This value is used to calculate the index of CD within _blockList } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- COM2Properties.cs
- ImageMap.cs
- RegexInterpreter.cs
- PolicyException.cs
- ExpressionEditorAttribute.cs
- StandardCommands.cs
- HashMembershipCondition.cs
- QueryCreatedEventArgs.cs
- TablePattern.cs
- BeginEvent.cs
- TagElement.cs
- UnsafeCollabNativeMethods.cs
- QuerySettings.cs
- ImageListImage.cs
- DataGridViewTopRowAccessibleObject.cs
- KnownTypesHelper.cs
- MissingMemberException.cs
- LineInfo.cs
- Style.cs
- DetailsViewInsertedEventArgs.cs
- DependencyPropertyConverter.cs
- DecoderReplacementFallback.cs
- MultiViewDesigner.cs
- UInt32.cs
- XmlSchemaObjectTable.cs
- Opcode.cs
- XmlAnyAttributeAttribute.cs
- ScrollEventArgs.cs
- HtmlInputReset.cs
- HwndHostAutomationPeer.cs
- URL.cs
- BitmapEffectGroup.cs
- RegexGroupCollection.cs
- WindowsListViewGroupSubsetLink.cs
- Point3D.cs
- Atom10FormatterFactory.cs
- CalendarDataBindingHandler.cs
- MessageQueueEnumerator.cs
- BinaryWriter.cs
- TextBox.cs
- HttpSocketManager.cs
- SimpleWorkerRequest.cs
- Collection.cs
- ProfileWorkflowElement.cs
- CompilerInfo.cs
- AnimationStorage.cs
- NumericUpDown.cs
- CounterCreationDataCollection.cs
- EncodingInfo.cs
- StreamUpdate.cs
- QilXmlWriter.cs
- SqlRewriteScalarSubqueries.cs
- __Filters.cs
- ArithmeticException.cs
- ObjectQueryProvider.cs
- Pen.cs
- TimerEventSubscription.cs
- SqlConnection.cs
- ForAllOperator.cs
- WorkflowWebHostingModule.cs
- StructuredTypeEmitter.cs
- EndpointAddress.cs
- X509Certificate.cs
- path.cs
- ZipIOExtraField.cs
- PeerNodeAddress.cs
- CursorInteropHelper.cs
- TargetFrameworkAttribute.cs
- FileBasedResourceGroveler.cs
- OleAutBinder.cs
- OleDbConnectionFactory.cs
- SpecialNameAttribute.cs
- FtpCachePolicyElement.cs
- CodeTypeMemberCollection.cs
- LinqToSqlWrapper.cs
- DataTableMappingCollection.cs
- DetailsViewPagerRow.cs
- PrintEvent.cs
- RegularExpressionValidator.cs
- filewebrequest.cs
- QueryContinueDragEventArgs.cs
- XmlDataSource.cs
- InputLanguage.cs
- FormViewPagerRow.cs
- CqlGenerator.cs
- GradientBrush.cs
- OleDbConnectionInternal.cs
- Clause.cs
- BinaryObjectWriter.cs
- TableItemStyle.cs
- HtmlTitle.cs
- ChangeTracker.cs
- Subordinate.cs
- LoginView.cs
- Int32EqualityComparer.cs
- FileLogRecordStream.cs
- TextBlock.cs
- JournalNavigationScope.cs
- XPathBuilder.cs
- FixedSOMElement.cs