Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / 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: [....]: First created. // 02/14/2006: [....]: Rename file to reflect class name and apply security mitigations // identified during security code review. // 03/09/2006: [....]: 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
- StringUtil.cs
- DataGridViewColumnEventArgs.cs
- ServicePoint.cs
- ExpressionVisitorHelpers.cs
- AuthenticationService.cs
- FontWeightConverter.cs
- SByteConverter.cs
- BeginEvent.cs
- NameTable.cs
- ContextQuery.cs
- Focus.cs
- MatrixCamera.cs
- HtmlInputText.cs
- DesignTimeParseData.cs
- PageContent.cs
- MimeBasePart.cs
- BinaryConverter.cs
- EntitySqlQueryBuilder.cs
- PtsContext.cs
- ViewGenResults.cs
- WmlObjectListAdapter.cs
- CallbackCorrelationInitializer.cs
- WebHttpEndpoint.cs
- DetailsViewDeletedEventArgs.cs
- XmlEncoding.cs
- MetadataFile.cs
- TitleStyle.cs
- AdRotatorDesigner.cs
- DynamicRendererThreadManager.cs
- DecimalAnimation.cs
- ClosureBinding.cs
- ListBindingHelper.cs
- Queue.cs
- EmbeddedMailObject.cs
- EncoderNLS.cs
- ExpressionBindingCollection.cs
- FieldTemplateUserControl.cs
- HashHelper.cs
- PenContext.cs
- RequestStatusBarUpdateEventArgs.cs
- sqlstateclientmanager.cs
- CompiledXpathExpr.cs
- TextServicesDisplayAttributePropertyRanges.cs
- DataChangedEventManager.cs
- XPathMessageFilterElementComparer.cs
- WebServiceClientProxyGenerator.cs
- SmtpDateTime.cs
- TargetParameterCountException.cs
- ExpressionBuilderContext.cs
- EntityContainerEntitySet.cs
- IPPacketInformation.cs
- OLEDB_Util.cs
- DataBinding.cs
- KerberosSecurityTokenProvider.cs
- HttpHandlersSection.cs
- AsymmetricSignatureFormatter.cs
- DataServiceProviderMethods.cs
- XmlSchemaComplexContent.cs
- HttpValueCollection.cs
- NavigateEvent.cs
- RuleInfoComparer.cs
- TreeViewImageGenerator.cs
- As.cs
- HttpPostedFile.cs
- AncestorChangedEventArgs.cs
- Addressing.cs
- StyleBamlTreeBuilder.cs
- DesignTableCollection.cs
- _Win32.cs
- TextRunTypographyProperties.cs
- ComponentRenameEvent.cs
- TimeEnumHelper.cs
- FontCacheUtil.cs
- X509SecurityTokenAuthenticator.cs
- RuleSetDialog.Designer.cs
- InputLanguage.cs
- MexServiceChannelBuilder.cs
- DataServiceSaveChangesEventArgs.cs
- DateTimePicker.cs
- AbstractExpressions.cs
- ExtensionDataReader.cs
- WrappedIUnknown.cs
- RenderingEventArgs.cs
- Configuration.cs
- AssemblyInfo.cs
- BamlRecordReader.cs
- _HeaderInfo.cs
- CachedFontFamily.cs
- TreeNodeCollection.cs
- util.cs
- XmlSchemaParticle.cs
- GradientStop.cs
- CodeParameterDeclarationExpression.cs
- XmlJsonWriter.cs
- ColorAnimationUsingKeyFrames.cs
- UnsafeNativeMethods.cs
- SweepDirectionValidation.cs
- GridViewRow.cs
- SimplePropertyEntry.cs
- IDataContractSurrogate.cs