Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Base / MS / Internal / IO / Packaging / CompoundFile / CompoundFileDeflateTransform.cs / 1305600 / 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
using MS.Internal.WindowsBase;
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
- ISAPIWorkerRequest.cs
- XsdBuildProvider.cs
- Point3DValueSerializer.cs
- FormViewDeleteEventArgs.cs
- ItemCheckEvent.cs
- ListViewContainer.cs
- EntityClassGenerator.cs
- EntityDataSource.cs
- ClientEventManager.cs
- DesignTimeParseData.cs
- FileVersion.cs
- SQLInt16Storage.cs
- WebPartManagerInternals.cs
- RawUIStateInputReport.cs
- Boolean.cs
- ContentControl.cs
- DeclaredTypeValidator.cs
- cookiecollection.cs
- AnnotationAdorner.cs
- TypeResolvingOptions.cs
- PeerToPeerException.cs
- RectangleGeometry.cs
- BackgroundFormatInfo.cs
- HiddenFieldPageStatePersister.cs
- PathFigure.cs
- CodeStatement.cs
- EdmItemCollection.cs
- LinkLabel.cs
- ObjectParameter.cs
- RegexInterpreter.cs
- WindowsToolbarItemAsMenuItem.cs
- DataPagerFieldItem.cs
- LogReservationCollection.cs
- XmlTextReaderImpl.cs
- QuadTree.cs
- Stopwatch.cs
- MultiBindingExpression.cs
- ElementNotAvailableException.cs
- SoapIncludeAttribute.cs
- BevelBitmapEffect.cs
- Command.cs
- CultureInfoConverter.cs
- GraphicsContext.cs
- Endpoint.cs
- IgnoreFileBuildProvider.cs
- ScrollViewer.cs
- StateInitialization.cs
- IsolatedStoragePermission.cs
- ComEventsInfo.cs
- DrawListViewSubItemEventArgs.cs
- Calendar.cs
- XsdSchemaFileEditor.cs
- PartitionResolver.cs
- FocusManager.cs
- OracleCommandBuilder.cs
- DataGridViewToolTip.cs
- TextFragmentEngine.cs
- SequenceDesigner.cs
- AtlasWeb.Designer.cs
- PaginationProgressEventArgs.cs
- SystemIcmpV6Statistics.cs
- Matrix3D.cs
- OperationCanceledException.cs
- DbDataSourceEnumerator.cs
- DesigntimeLicenseContext.cs
- RowParagraph.cs
- CacheChildrenQuery.cs
- TrackingQuery.cs
- SafeNativeMemoryHandle.cs
- LocationSectionRecord.cs
- DataServiceHost.cs
- FixedTextView.cs
- SplineKeyFrames.cs
- XmlSchemaRedefine.cs
- RepeaterCommandEventArgs.cs
- PointKeyFrameCollection.cs
- SoapTypeAttribute.cs
- ListViewItem.cs
- Vector3DKeyFrameCollection.cs
- Vertex.cs
- VirtualPathUtility.cs
- ClassHandlersStore.cs
- SoapSchemaExporter.cs
- GeneralTransform3D.cs
- AmbiguousMatchException.cs
- FileDialog.cs
- StylusPlugin.cs
- ImageConverter.cs
- AnimationClockResource.cs
- IpcServerChannel.cs
- KeyGesture.cs
- ReplacementText.cs
- ProfileModule.cs
- RoutedEvent.cs
- TypeBrowserDialog.cs
- WpfKnownMember.cs
- ConnectionOrientedTransportBindingElement.cs
- DataGridViewCellErrorTextNeededEventArgs.cs
- ForAllOperator.cs
- xmlsaver.cs