Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Base / MS / Internal / IO / Packaging / CompoundFile / CompoundFileDeflateTransform.cs / 1 / CompoundFileDeflateTransform.cs
//------------------------------------------------------------------------------ // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: // Implementation of a helper class that provides a fully functional Stream on unmanaged ZLib in a fashion // consistent with Office and RMA (see Creating Rights-Managed HTML Files at // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rma/introduction.asp). // // History: // 10/05/2005: BruceMac: First created. // 02/14/2006: BruceMac: Rename file to reflect class name and apply security mitigations // identified during security code review. // 03/09/2006: BruceMac: Make AllocOrRealloc SecurityCritical because it allocates // pinned memory based on caller arguments. //----------------------------------------------------------------------------- // Allow use of presharp warning numbers [6518] unknown to the compiler #pragma warning disable 1634, 1691 using System; using System.IO; using System.Diagnostics; using System.IO.Packaging; using System.Windows; using System.Runtime.InteropServices; // for Marshal class using MS.Internal.IO.Packaging; // for PackagingUtilities using System.Security; // for SecurityCritical and SecurityTreatAsSafe namespace MS.Internal.IO.Packaging.CompoundFile { //----------------------------------------------------- // // Internal Members // //----------------------------------------------------- ////// Provides Office-compatible ZLib compression using interop to ZLib library /// ///This class makes use of GCHandles in order to share data in a non-trivial fashion with the /// unmanaged ZLib library. Because of this, it demands UnmanagedCodePermission of it's caller. /// IDeflateTransform is a batch-oriented interface. All data will be transformed from source /// to destination. internal class CompoundFileDeflateTransform : IDeflateTransform { //------------------------------------------------------ // // IDeflateTransform Interface // //----------------------------------------------------- ////// Decompress delegate - invoke ZLib in a manner consistent with RMA/Office /// /// stream to read from /// stream to write to ////// Critical: calls AllocOrRealloc which is allocates pinned memory based on arguments /// TreatAsSafe: Callers cannot use this to allocate memory of arbitrary size. /// AllocOrRealloc is used in two occasions: /// 1. Compress - here we provide size based on our default block size (of 4k) /// and growth will not exceed double this size (we are compressing, but sometimes /// compression doesn't succeed in reducing sizes). /// 2. Decompress - here the block size is based on values obtained from the /// stream itself (see ReadBlockHeader) but we are careful to throw on malicious /// input. Any size > 1MB is considered malicious and rejected. /// [SecurityCritical, SecurityTreatAsSafe] public void Decompress(Stream source, Stream sink) { if (source == null) throw new ArgumentNullException("source"); if (sink == null) throw new ArgumentNullException("sink"); Invariant.Assert(source.CanRead); Invariant.Assert(sink.CanWrite, "Logic Error - Cannot decompress into a read-only stream"); // remember this for later long storedPosition = -1; try { if (source.CanSeek) { storedPosition = source.Position; source.Position = 0; } if (sink.CanSeek) sink.Position = 0; // zlib state UnsafeNativeMethods.ZStream zStream = new UnsafeNativeMethods.ZStream(); // initialize the zlib library UnsafeNativeMethods.ZLib.ErrorCode retVal = 0; retVal = UnsafeNativeMethods.ZLib.ums_inflate_init(ref zStream, UnsafeNativeMethods.ZLib.ZLibVersion, Marshal.SizeOf(zStream)); ThrowIfZLibError(retVal); byte[] sourceBuf = null; // source buffer byte[] sinkBuf = null; // destination buffer - where to write data GCHandle gcSourceBuf = new GCHandle(); // Preallocate these so we can safely access them GCHandle gcSinkBuf = new GCHandle(); // in the next finally block. try { // read all available data // each block is preceded by a header that is 3 ulongs int uncompressedSize, compressedSize; long destStreamLength = 0; // keep track of decompressed size while (ReadBlockHeader(source, out uncompressedSize, out compressedSize)) { // ensure we have space AllocOrRealloc(compressedSize, ref sourceBuf, ref gcSourceBuf); AllocOrRealloc(uncompressedSize, ref sinkBuf, ref gcSinkBuf); // read the data into the sourceBuf int bytesRead = PackagingUtilities.ReliableRead(source, sourceBuf, 0, compressedSize); if (bytesRead > 0) { if (compressedSize != bytesRead) throw new FileFormatException(SR.Get(SRID.CorruptStream)); // prepare structure // The buffer pointers must be reset for every call // because ums_inflate modifies them zStream.pInBuf = gcSourceBuf.AddrOfPinnedObject(); zStream.pOutBuf = gcSinkBuf.AddrOfPinnedObject(); zStream.cbIn = (uint)bytesRead; // this is number of bytes available for decompression at pInBuf and is updated by ums_deflate call zStream.cbOut = (uint)sinkBuf.Length; // this is the number of bytes free in pOutBuf and is updated by ums_deflate call // InvokeZLib does the actual interop. It updates zStream, and sinkBuf (sourceBuf passed by ref to avoid copying) // and leaves the decompressed data in sinkBuf. // int decompressedSize = InvokeZLib(bytesRead, ref zStream, ref sourceBuf, ref sinkBuf, pSource, pSink, false); retVal = UnsafeNativeMethods.ZLib.ums_inflate(ref zStream, (int)UnsafeNativeMethods.ZLib.FlushCodes.SyncFlush); ThrowIfZLibError(retVal); checked { int decompressedSize = sinkBuf.Length - (int)zStream.cbOut; // verify that data matches header if (decompressedSize != uncompressedSize) throw new FileFormatException(SR.Get(SRID.CorruptStream)); destStreamLength += decompressedSize; // write to the base stream sink.Write(sinkBuf, 0, decompressedSize); } } else { // block header but no block data if (compressedSize != 0) throw new FileFormatException(SR.Get(SRID.CorruptStream)); } } // make sure we truncate if the destination stream was longer than this current decompress if (sink.CanSeek) sink.SetLength(destStreamLength); } finally { if (gcSourceBuf.IsAllocated) gcSourceBuf.Free(); if (gcSinkBuf.IsAllocated) gcSinkBuf.Free(); } } finally { // seek to the current logical position before returning if (source.CanSeek) source.Position = storedPosition; } } ////// Compress delegate - invoke ZLib in a manner consistent with RMA/Office /// /// /// ///We are careful to avoid use of Position, Length or SetLength on non-seekable streams. If /// source or sink are non-seekable, it is assumed that positions are correctly set upon entry and that /// they need not be restored. We also assume that destination stream length need not be truncated. ////// Critical: calls AllocOrRealloc which is allocates pinned memory based on arguments /// TreatAsSafe: Callers cannot use this to allocate memory of arbitrary size. /// AllocOrRealloc is used in two occasions: /// 1. Compress - here we provide size based on our default block size (of 4k) /// and growth will not exceed double this size (we are compressing, but sometimes /// compression doesn't succeed in reducing sizes). /// 2. Decompress - here the block size is based on values obtained from the /// stream itself (see ReadBlockHeader) but we are careful to throw on malicious /// input. Any size > 1MB is considered malicious and rejected. /// [SecurityCritical, SecurityTreatAsSafe] public void Compress(Stream source, Stream sink) { if (source == null) throw new ArgumentNullException("source"); if (sink == null) throw new ArgumentNullException("sink"); Invariant.Assert(source.CanRead); Invariant.Assert(sink.CanWrite, "Logic Error - Cannot compress into a read-only stream"); // remember this for later if possible long storedPosition = -1; // default to illegal value to catch any logic errors try { int sourceBufferSize; // don't allocate 4k for really tiny source streams if (source.CanSeek) { storedPosition = source.Position; source.Position = 0; // Casting result to int is safe because _defaultBlockSize is very small and the result // of Math.Min(x, _defaultBlockSize) must be no larger than _defaultBlockSize. sourceBufferSize = (int)(Math.Min(source.Length, (long)_defaultBlockSize)); } else sourceBufferSize = _defaultBlockSize; // can't call Length so fallback to default if (sink.CanSeek) sink.Position = 0; // zlib state UnsafeNativeMethods.ZStream zStream = new UnsafeNativeMethods.ZStream(); // initialize the zlib library UnsafeNativeMethods.ZLib.ErrorCode retVal = UnsafeNativeMethods.ZLib.ums_deflate_init(ref zStream, _compressionLevel, UnsafeNativeMethods.ZLib.ZLibVersion, Marshal.SizeOf(zStream)); ThrowIfZLibError(retVal); // where to write data - can actually grow if data is uncompressible long destStreamLength = 0; byte[] sourceBuf = null; // source buffer byte[] sinkBuf = null; // destination buffer GCHandle gcSourceBuf = new GCHandle(); GCHandle gcSinkBuf = new GCHandle(); try { // allocate managed buffers AllocOrRealloc(sourceBufferSize, ref sourceBuf, ref gcSourceBuf); AllocOrRealloc(_defaultBlockSize + (_defaultBlockSize >> 1), ref sinkBuf, ref gcSinkBuf); // while (more data is available) // - read into the sourceBuf // - compress into the sinkBuf // - emit the header // - write out to the _baseStream // Suppress 6518 Local IDisposable object not disposed: // Reason: The stream is not owned by us, therefore we cannot // close the BinaryWriter as it will Close the stream underneath. #pragma warning disable 6518 BinaryWriter writer = new BinaryWriter(sink); int bytesRead; while ((bytesRead = PackagingUtilities.ReliableRead(source, sourceBuf, 0, sourceBuf.Length)) > 0) { Invariant.Assert(bytesRead <= sourceBufferSize); // prepare structure // these pointers must be re-assigned for each loop because // ums_deflate modifies them zStream.pInBuf = gcSourceBuf.AddrOfPinnedObject(); zStream.pOutBuf = gcSinkBuf.AddrOfPinnedObject(); zStream.cbIn = (uint)bytesRead; // this is number of bytes available for compression at pInBuf and is updated by ums_deflate call zStream.cbOut = (uint)sinkBuf.Length; // this is the number of bytes free in pOutBuf and is updated by ums_deflate call // cast is safe because SyncFlush is a constant retVal = UnsafeNativeMethods.ZLib.ums_deflate(ref zStream, (int)UnsafeNativeMethods.ZLib.FlushCodes.SyncFlush); ThrowIfZLibError(retVal); checked { int compressedSize = sinkBuf.Length - (int)zStream.cbOut; Invariant.Assert(compressedSize > 0, "compressing non-zero bytes creates a non-empty block"); // This should never happen because our destination buffer // is twice as large as our source buffer Invariant.Assert(zStream.cbIn == 0, "Expecting all data to be compressed!"); // write the header writer.Write(_blockHeaderToken); // token writer.Write((UInt32)bytesRead); writer.Write((UInt32)compressedSize); destStreamLength += _headerBuf.Length; // write to the base stream sink.Write(sinkBuf, 0, compressedSize); destStreamLength += compressedSize; } } // post-compression // truncate if necessary if (sink.CanSeek) sink.SetLength(destStreamLength); } finally { if (gcSourceBuf.IsAllocated) gcSourceBuf.Free(); if (gcSinkBuf.IsAllocated) gcSinkBuf.Free(); } #pragma warning restore 6518 } finally { // seek to the current logical position before returning if (sink.CanSeek) source.Position = storedPosition; } } //------------------------------------------------------ // // Private Methods // //------------------------------------------------------ ////// Ensures that Buffer has enough room for size using alloc or realloc /// /// buffer - may be null /// desired size /// handle ///When this exits, buffer is at least as large as size /// and gcHandle is pointing to the pinned buffer. If the buffer was already large enough, /// no action is taken. ////// Critical - allocates pinned memory based on arguments /// [SecurityCritical] private static void AllocOrRealloc(int size, ref byte[] buffer, ref GCHandle gcHandle) { Invariant.Assert(size >= 0, "Cannot allocate negative number of bytes"); // verify we have room if (buffer != null) { // do we have room? if (buffer.Length < size) { // overallocate to reduce the chance of future reallocations size = Math.Max(size, buffer.Length + (buffer.Length >> 1)); // fast Length * 1.5 // free existing because it's too small if (gcHandle.IsAllocated) gcHandle.Free(); } else return; // current buffer satisfies the request so there is no need to alloc } // We have to allocate in two cases: // 1. We were called with buffer == null // 2. The original buffer was too small buffer = new byte[size]; // managed source buffer gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); // pinned so unmanaged code can read/write it } ////// ReadBlockHeader - reads the block header and returns true if successful /// /// stream to read from /// compressedSize from header /// uncompressedSize from header ///true if header found private bool ReadBlockHeader(Stream source, out int uncompressedSize, out int compressedSize) { int bytesRead = PackagingUtilities.ReliableRead(source, _headerBuf, 0, _headerBuf.Length); if (bytesRead > 0) { if (bytesRead < _headerBuf.Length) throw new FileFormatException(SR.Get(SRID.CorruptStream)); // header format = 3 ulong's // read and inspect token uint token = BitConverter.ToUInt32(_headerBuf, _ulongSize * 0); if (token != _blockHeaderToken) throw new FileFormatException(SR.Get(SRID.CorruptStream)); // convert to int's as that's what we use everywhere checked { uncompressedSize = (int)BitConverter.ToUInt32(_headerBuf, _ulongSize * 1); compressedSize = (int)BitConverter.ToUInt32(_headerBuf, _ulongSize * 2); // screen out malicious data if (uncompressedSize < 0 || uncompressedSize > _maxAllowableBlockSize || compressedSize < 0 || compressedSize > _maxAllowableBlockSize) throw new FileFormatException(SR.Get(SRID.CorruptStream)); } } else { uncompressedSize = compressedSize = 0; } return (bytesRead > 0); } ////// Throw exception based on ZLib error code /// /// private static void ThrowIfZLibError(UnsafeNativeMethods.ZLib.ErrorCode retVal) { // switch does not support fall-through bool invalidOperation = false; bool corruption = false; switch (retVal) { case UnsafeNativeMethods.ZLib.ErrorCode.Success: return; case UnsafeNativeMethods.ZLib.ErrorCode.StreamEnd: invalidOperation = true; break; case UnsafeNativeMethods.ZLib.ErrorCode.NeedDictionary: corruption = true; break; case UnsafeNativeMethods.ZLib.ErrorCode.StreamError: corruption = true; break; case UnsafeNativeMethods.ZLib.ErrorCode.DataError: corruption = true; break; case UnsafeNativeMethods.ZLib.ErrorCode.MemError: throw new OutOfMemoryException(); case UnsafeNativeMethods.ZLib.ErrorCode.BufError: invalidOperation = true; break; case UnsafeNativeMethods.ZLib.ErrorCode.VersionError: throw new InvalidOperationException(SR.Get(SRID.ZLibVersionError, UnsafeNativeMethods.ZLib.ZLibVersion)); default: { // ErrorNo throw new IOException(); } } if (invalidOperation) throw new InvalidOperationException(); if (corruption) throw new FileFormatException(SR.Get(SRID.CorruptStream)); } //----------------------------------------------------- // // Private Fields // //------------------------------------------------------ // for reading each block header private byte[] _headerBuf = new byte[_blockHeaderSize]; // 3 ulongs // static private const int _defaultBlockSize = 0x1000; // 4k default private const int _maxAllowableBlockSize = 0xFFFFF; // The spec is open ended about supported block sizes but we // want to defend against malicious input so we restrict input to 1MB. private const int _compressionLevel = 9; // mandated by format private const int _ulongSize = 4; // a ULONG in unmanaged C++ is 4 bytes private const UInt32 _blockHeaderToken = 0x0FA0; // signature at start of each block header private const int _blockHeaderSize = _ulongSize * 3; // length of block header } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: // Implementation of a helper class that provides a fully functional Stream on unmanaged ZLib in a fashion // consistent with Office and RMA (see Creating Rights-Managed HTML Files at // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rma/introduction.asp). // // History: // 10/05/2005: BruceMac: First created. // 02/14/2006: BruceMac: Rename file to reflect class name and apply security mitigations // identified during security code review. // 03/09/2006: BruceMac: Make AllocOrRealloc SecurityCritical because it allocates // pinned memory based on caller arguments. //----------------------------------------------------------------------------- // Allow use of presharp warning numbers [6518] unknown to the compiler #pragma warning disable 1634, 1691 using System; using System.IO; using System.Diagnostics; using System.IO.Packaging; using System.Windows; using System.Runtime.InteropServices; // for Marshal class using MS.Internal.IO.Packaging; // for PackagingUtilities using System.Security; // for SecurityCritical and SecurityTreatAsSafe namespace MS.Internal.IO.Packaging.CompoundFile { //----------------------------------------------------- // // Internal Members // //----------------------------------------------------- ////// Provides Office-compatible ZLib compression using interop to ZLib library /// ///This class makes use of GCHandles in order to share data in a non-trivial fashion with the /// unmanaged ZLib library. Because of this, it demands UnmanagedCodePermission of it's caller. /// IDeflateTransform is a batch-oriented interface. All data will be transformed from source /// to destination. internal class CompoundFileDeflateTransform : IDeflateTransform { //------------------------------------------------------ // // IDeflateTransform Interface // //----------------------------------------------------- ////// Decompress delegate - invoke ZLib in a manner consistent with RMA/Office /// /// stream to read from /// stream to write to ////// Critical: calls AllocOrRealloc which is allocates pinned memory based on arguments /// TreatAsSafe: Callers cannot use this to allocate memory of arbitrary size. /// AllocOrRealloc is used in two occasions: /// 1. Compress - here we provide size based on our default block size (of 4k) /// and growth will not exceed double this size (we are compressing, but sometimes /// compression doesn't succeed in reducing sizes). /// 2. Decompress - here the block size is based on values obtained from the /// stream itself (see ReadBlockHeader) but we are careful to throw on malicious /// input. Any size > 1MB is considered malicious and rejected. /// [SecurityCritical, SecurityTreatAsSafe] public void Decompress(Stream source, Stream sink) { if (source == null) throw new ArgumentNullException("source"); if (sink == null) throw new ArgumentNullException("sink"); Invariant.Assert(source.CanRead); Invariant.Assert(sink.CanWrite, "Logic Error - Cannot decompress into a read-only stream"); // remember this for later long storedPosition = -1; try { if (source.CanSeek) { storedPosition = source.Position; source.Position = 0; } if (sink.CanSeek) sink.Position = 0; // zlib state UnsafeNativeMethods.ZStream zStream = new UnsafeNativeMethods.ZStream(); // initialize the zlib library UnsafeNativeMethods.ZLib.ErrorCode retVal = 0; retVal = UnsafeNativeMethods.ZLib.ums_inflate_init(ref zStream, UnsafeNativeMethods.ZLib.ZLibVersion, Marshal.SizeOf(zStream)); ThrowIfZLibError(retVal); byte[] sourceBuf = null; // source buffer byte[] sinkBuf = null; // destination buffer - where to write data GCHandle gcSourceBuf = new GCHandle(); // Preallocate these so we can safely access them GCHandle gcSinkBuf = new GCHandle(); // in the next finally block. try { // read all available data // each block is preceded by a header that is 3 ulongs int uncompressedSize, compressedSize; long destStreamLength = 0; // keep track of decompressed size while (ReadBlockHeader(source, out uncompressedSize, out compressedSize)) { // ensure we have space AllocOrRealloc(compressedSize, ref sourceBuf, ref gcSourceBuf); AllocOrRealloc(uncompressedSize, ref sinkBuf, ref gcSinkBuf); // read the data into the sourceBuf int bytesRead = PackagingUtilities.ReliableRead(source, sourceBuf, 0, compressedSize); if (bytesRead > 0) { if (compressedSize != bytesRead) throw new FileFormatException(SR.Get(SRID.CorruptStream)); // prepare structure // The buffer pointers must be reset for every call // because ums_inflate modifies them zStream.pInBuf = gcSourceBuf.AddrOfPinnedObject(); zStream.pOutBuf = gcSinkBuf.AddrOfPinnedObject(); zStream.cbIn = (uint)bytesRead; // this is number of bytes available for decompression at pInBuf and is updated by ums_deflate call zStream.cbOut = (uint)sinkBuf.Length; // this is the number of bytes free in pOutBuf and is updated by ums_deflate call // InvokeZLib does the actual interop. It updates zStream, and sinkBuf (sourceBuf passed by ref to avoid copying) // and leaves the decompressed data in sinkBuf. // int decompressedSize = InvokeZLib(bytesRead, ref zStream, ref sourceBuf, ref sinkBuf, pSource, pSink, false); retVal = UnsafeNativeMethods.ZLib.ums_inflate(ref zStream, (int)UnsafeNativeMethods.ZLib.FlushCodes.SyncFlush); ThrowIfZLibError(retVal); checked { int decompressedSize = sinkBuf.Length - (int)zStream.cbOut; // verify that data matches header if (decompressedSize != uncompressedSize) throw new FileFormatException(SR.Get(SRID.CorruptStream)); destStreamLength += decompressedSize; // write to the base stream sink.Write(sinkBuf, 0, decompressedSize); } } else { // block header but no block data if (compressedSize != 0) throw new FileFormatException(SR.Get(SRID.CorruptStream)); } } // make sure we truncate if the destination stream was longer than this current decompress if (sink.CanSeek) sink.SetLength(destStreamLength); } finally { if (gcSourceBuf.IsAllocated) gcSourceBuf.Free(); if (gcSinkBuf.IsAllocated) gcSinkBuf.Free(); } } finally { // seek to the current logical position before returning if (source.CanSeek) source.Position = storedPosition; } } ////// Compress delegate - invoke ZLib in a manner consistent with RMA/Office /// /// /// ///We are careful to avoid use of Position, Length or SetLength on non-seekable streams. If /// source or sink are non-seekable, it is assumed that positions are correctly set upon entry and that /// they need not be restored. We also assume that destination stream length need not be truncated. ////// Critical: calls AllocOrRealloc which is allocates pinned memory based on arguments /// TreatAsSafe: Callers cannot use this to allocate memory of arbitrary size. /// AllocOrRealloc is used in two occasions: /// 1. Compress - here we provide size based on our default block size (of 4k) /// and growth will not exceed double this size (we are compressing, but sometimes /// compression doesn't succeed in reducing sizes). /// 2. Decompress - here the block size is based on values obtained from the /// stream itself (see ReadBlockHeader) but we are careful to throw on malicious /// input. Any size > 1MB is considered malicious and rejected. /// [SecurityCritical, SecurityTreatAsSafe] public void Compress(Stream source, Stream sink) { if (source == null) throw new ArgumentNullException("source"); if (sink == null) throw new ArgumentNullException("sink"); Invariant.Assert(source.CanRead); Invariant.Assert(sink.CanWrite, "Logic Error - Cannot compress into a read-only stream"); // remember this for later if possible long storedPosition = -1; // default to illegal value to catch any logic errors try { int sourceBufferSize; // don't allocate 4k for really tiny source streams if (source.CanSeek) { storedPosition = source.Position; source.Position = 0; // Casting result to int is safe because _defaultBlockSize is very small and the result // of Math.Min(x, _defaultBlockSize) must be no larger than _defaultBlockSize. sourceBufferSize = (int)(Math.Min(source.Length, (long)_defaultBlockSize)); } else sourceBufferSize = _defaultBlockSize; // can't call Length so fallback to default if (sink.CanSeek) sink.Position = 0; // zlib state UnsafeNativeMethods.ZStream zStream = new UnsafeNativeMethods.ZStream(); // initialize the zlib library UnsafeNativeMethods.ZLib.ErrorCode retVal = UnsafeNativeMethods.ZLib.ums_deflate_init(ref zStream, _compressionLevel, UnsafeNativeMethods.ZLib.ZLibVersion, Marshal.SizeOf(zStream)); ThrowIfZLibError(retVal); // where to write data - can actually grow if data is uncompressible long destStreamLength = 0; byte[] sourceBuf = null; // source buffer byte[] sinkBuf = null; // destination buffer GCHandle gcSourceBuf = new GCHandle(); GCHandle gcSinkBuf = new GCHandle(); try { // allocate managed buffers AllocOrRealloc(sourceBufferSize, ref sourceBuf, ref gcSourceBuf); AllocOrRealloc(_defaultBlockSize + (_defaultBlockSize >> 1), ref sinkBuf, ref gcSinkBuf); // while (more data is available) // - read into the sourceBuf // - compress into the sinkBuf // - emit the header // - write out to the _baseStream // Suppress 6518 Local IDisposable object not disposed: // Reason: The stream is not owned by us, therefore we cannot // close the BinaryWriter as it will Close the stream underneath. #pragma warning disable 6518 BinaryWriter writer = new BinaryWriter(sink); int bytesRead; while ((bytesRead = PackagingUtilities.ReliableRead(source, sourceBuf, 0, sourceBuf.Length)) > 0) { Invariant.Assert(bytesRead <= sourceBufferSize); // prepare structure // these pointers must be re-assigned for each loop because // ums_deflate modifies them zStream.pInBuf = gcSourceBuf.AddrOfPinnedObject(); zStream.pOutBuf = gcSinkBuf.AddrOfPinnedObject(); zStream.cbIn = (uint)bytesRead; // this is number of bytes available for compression at pInBuf and is updated by ums_deflate call zStream.cbOut = (uint)sinkBuf.Length; // this is the number of bytes free in pOutBuf and is updated by ums_deflate call // cast is safe because SyncFlush is a constant retVal = UnsafeNativeMethods.ZLib.ums_deflate(ref zStream, (int)UnsafeNativeMethods.ZLib.FlushCodes.SyncFlush); ThrowIfZLibError(retVal); checked { int compressedSize = sinkBuf.Length - (int)zStream.cbOut; Invariant.Assert(compressedSize > 0, "compressing non-zero bytes creates a non-empty block"); // This should never happen because our destination buffer // is twice as large as our source buffer Invariant.Assert(zStream.cbIn == 0, "Expecting all data to be compressed!"); // write the header writer.Write(_blockHeaderToken); // token writer.Write((UInt32)bytesRead); writer.Write((UInt32)compressedSize); destStreamLength += _headerBuf.Length; // write to the base stream sink.Write(sinkBuf, 0, compressedSize); destStreamLength += compressedSize; } } // post-compression // truncate if necessary if (sink.CanSeek) sink.SetLength(destStreamLength); } finally { if (gcSourceBuf.IsAllocated) gcSourceBuf.Free(); if (gcSinkBuf.IsAllocated) gcSinkBuf.Free(); } #pragma warning restore 6518 } finally { // seek to the current logical position before returning if (sink.CanSeek) source.Position = storedPosition; } } //------------------------------------------------------ // // Private Methods // //------------------------------------------------------ ////// Ensures that Buffer has enough room for size using alloc or realloc /// /// buffer - may be null /// desired size /// handle ///When this exits, buffer is at least as large as size /// and gcHandle is pointing to the pinned buffer. If the buffer was already large enough, /// no action is taken. ////// Critical - allocates pinned memory based on arguments /// [SecurityCritical] private static void AllocOrRealloc(int size, ref byte[] buffer, ref GCHandle gcHandle) { Invariant.Assert(size >= 0, "Cannot allocate negative number of bytes"); // verify we have room if (buffer != null) { // do we have room? if (buffer.Length < size) { // overallocate to reduce the chance of future reallocations size = Math.Max(size, buffer.Length + (buffer.Length >> 1)); // fast Length * 1.5 // free existing because it's too small if (gcHandle.IsAllocated) gcHandle.Free(); } else return; // current buffer satisfies the request so there is no need to alloc } // We have to allocate in two cases: // 1. We were called with buffer == null // 2. The original buffer was too small buffer = new byte[size]; // managed source buffer gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); // pinned so unmanaged code can read/write it } ////// ReadBlockHeader - reads the block header and returns true if successful /// /// stream to read from /// compressedSize from header /// uncompressedSize from header ///true if header found private bool ReadBlockHeader(Stream source, out int uncompressedSize, out int compressedSize) { int bytesRead = PackagingUtilities.ReliableRead(source, _headerBuf, 0, _headerBuf.Length); if (bytesRead > 0) { if (bytesRead < _headerBuf.Length) throw new FileFormatException(SR.Get(SRID.CorruptStream)); // header format = 3 ulong's // read and inspect token uint token = BitConverter.ToUInt32(_headerBuf, _ulongSize * 0); if (token != _blockHeaderToken) throw new FileFormatException(SR.Get(SRID.CorruptStream)); // convert to int's as that's what we use everywhere checked { uncompressedSize = (int)BitConverter.ToUInt32(_headerBuf, _ulongSize * 1); compressedSize = (int)BitConverter.ToUInt32(_headerBuf, _ulongSize * 2); // screen out malicious data if (uncompressedSize < 0 || uncompressedSize > _maxAllowableBlockSize || compressedSize < 0 || compressedSize > _maxAllowableBlockSize) throw new FileFormatException(SR.Get(SRID.CorruptStream)); } } else { uncompressedSize = compressedSize = 0; } return (bytesRead > 0); } ////// Throw exception based on ZLib error code /// /// private static void ThrowIfZLibError(UnsafeNativeMethods.ZLib.ErrorCode retVal) { // switch does not support fall-through bool invalidOperation = false; bool corruption = false; switch (retVal) { case UnsafeNativeMethods.ZLib.ErrorCode.Success: return; case UnsafeNativeMethods.ZLib.ErrorCode.StreamEnd: invalidOperation = true; break; case UnsafeNativeMethods.ZLib.ErrorCode.NeedDictionary: corruption = true; break; case UnsafeNativeMethods.ZLib.ErrorCode.StreamError: corruption = true; break; case UnsafeNativeMethods.ZLib.ErrorCode.DataError: corruption = true; break; case UnsafeNativeMethods.ZLib.ErrorCode.MemError: throw new OutOfMemoryException(); case UnsafeNativeMethods.ZLib.ErrorCode.BufError: invalidOperation = true; break; case UnsafeNativeMethods.ZLib.ErrorCode.VersionError: throw new InvalidOperationException(SR.Get(SRID.ZLibVersionError, UnsafeNativeMethods.ZLib.ZLibVersion)); default: { // ErrorNo throw new IOException(); } } if (invalidOperation) throw new InvalidOperationException(); if (corruption) throw new FileFormatException(SR.Get(SRID.CorruptStream)); } //----------------------------------------------------- // // Private Fields // //------------------------------------------------------ // for reading each block header private byte[] _headerBuf = new byte[_blockHeaderSize]; // 3 ulongs // static private const int _defaultBlockSize = 0x1000; // 4k default private const int _maxAllowableBlockSize = 0xFFFFF; // The spec is open ended about supported block sizes but we // want to defend against malicious input so we restrict input to 1MB. private const int _compressionLevel = 9; // mandated by format private const int _ulongSize = 4; // a ULONG in unmanaged C++ is 4 bytes private const UInt32 _blockHeaderToken = 0x0FA0; // signature at start of each block header private const int _blockHeaderSize = _ulongSize * 3; // length of block header } } // 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
- MarshalByValueComponent.cs
- PropertyStore.cs
- WizardSideBarListControlItemEventArgs.cs
- ScanQueryOperator.cs
- ClientProxyGenerator.cs
- AssociationSetMetadata.cs
- figurelengthconverter.cs
- SqlDataSourceCommandEventArgs.cs
- SqlMethods.cs
- TextServicesDisplayAttribute.cs
- FormsAuthentication.cs
- StorageEntitySetMapping.cs
- IImplicitResourceProvider.cs
- WmfPlaceableFileHeader.cs
- KnownTypeAttribute.cs
- XmlSchemaExporter.cs
- ResourcesGenerator.cs
- DateTimeOffset.cs
- Pair.cs
- OleDbParameterCollection.cs
- Pens.cs
- TdsParserStaticMethods.cs
- XmlWrappingReader.cs
- AttributeSetAction.cs
- EntryIndex.cs
- RadioButton.cs
- ServicePointManager.cs
- SystemIPv6InterfaceProperties.cs
- ImageButton.cs
- MergeFilterQuery.cs
- ComponentChangedEvent.cs
- BitmapCacheBrush.cs
- EntityDesignerDataSourceView.cs
- LicenseManager.cs
- EntityParameterCollection.cs
- IPAddress.cs
- NetworkInformationPermission.cs
- HttpContext.cs
- Mappings.cs
- UInt16.cs
- TypeLoadException.cs
- WebRequestModuleElement.cs
- IssuanceTokenProviderBase.cs
- SafeHandle.cs
- ImageList.cs
- ConcurrentDictionary.cs
- SourceFilter.cs
- WeakReadOnlyCollection.cs
- CompilerScope.cs
- Int32KeyFrameCollection.cs
- EventLogPermissionEntryCollection.cs
- SqlGenerator.cs
- CodePrimitiveExpression.cs
- ProviderConnectionPointCollection.cs
- StickyNote.cs
- MouseCaptureWithinProperty.cs
- SystemMulticastIPAddressInformation.cs
- XmlNamedNodeMap.cs
- GridViewCellAutomationPeer.cs
- PresentationTraceSources.cs
- TabPageDesigner.cs
- ExecutionTracker.cs
- ModifiableIteratorCollection.cs
- TextDecorationUnitValidation.cs
- XmlSchemaInfo.cs
- Point3DKeyFrameCollection.cs
- OdbcCommandBuilder.cs
- ContainerUIElement3D.cs
- UTF7Encoding.cs
- QueryExecutionOption.cs
- CodeParameterDeclarationExpression.cs
- UnauthorizedAccessException.cs
- BindingExpressionBase.cs
- SafeNativeMethods.cs
- TdsParser.cs
- CustomWebEventKey.cs
- NativeMethods.cs
- ReachPageContentSerializer.cs
- UmAlQuraCalendar.cs
- AspCompat.cs
- IPAddress.cs
- CommandValueSerializer.cs
- EnumerableRowCollectionExtensions.cs
- CollectionChangeEventArgs.cs
- Model3D.cs
- ToolStripArrowRenderEventArgs.cs
- DbException.cs
- UseLicense.cs
- ComponentEvent.cs
- EntityDataSource.cs
- WebPartTracker.cs
- Transform.cs
- RightsManagementInformation.cs
- ValueTable.cs
- SchemaName.cs
- PasswordTextNavigator.cs
- SessionStateContainer.cs
- KeyGestureConverter.cs
- PKCS1MaskGenerationMethod.cs
- MemoryResponseElement.cs